每次服务重启后 之前配置的限流规则就会被清空因为是内存态的规则对象,所以就要用到Sentinel一个特性ReadableDataSource 获取文件、数据库或者配置中心是限流规则
依赖:spring-cloud-alibaba-sentinel-datasource
通过文件读取限流规则
spring.cloud.sentinel.datasource.ds1.file.file=classpath:flowrule.json
spring.cloud.sentinel.datasource.ds1.file.data-type=json
spring.cloud.sentinel.datasource.ds1.file.rule-type=flow
在resources新建一个文件 比如flowrule.json 添加限流规则
[
{
"resource": "resource",
"controlBehavior": 0,
"count": 1,
"grade": 1,
"limitApp": "default",
"strategy": 0
}
]
@RestController
public class TestController {
@GetMapping("/test1")
@SentinelResource(value = "resource")
public String sayHello(String name){
if(name==null || name.trim().length()<1)
throw new IllegalArgumentException("参数为空!");
return "Hello "+name+"!";
}
}
一条限流规则主要有:
resource资源名,即限流规则的作用对象
count限流阈值
grade限流阈值类型,0表示线程,1表示QPS
limitApp流控针对的调用来源,若为 default 则不区分调用来源
strategy调用关系限流策略,0表示直接,1表示关联,2表示链路
controlBehavior流量控制效果,0表示快速失败,1表示Warm Up,2表示排队等待
限流是一种预防措施,虽然限流可以尽量避免因高并发而引起的服务故障,但服务还会因为其它原因而故障。而要将这些故障控制在一定范围,避免雪崩,就要靠线程隔离(舱壁模式)和熔断降级手段了。
线程隔离就是调用者在调用服务提供者时,给每个调用的请求分配独立线程池,出现故障时,最多消耗这个线程池内资源,避免把调用者的所有资源耗尽
熔断降级是在调用方这边加入断路器,统计对服务提供者的调用,如果调用的失败比例过高,则熔断该业务,不允许访问该服务的提供者了。
不管是线程隔离还是熔断降级,都是对客户端调用方的保护。需要在调用方发起远程调用时做线程隔离、或者服务熔断。
Spring Cloud Alibaba Sentinel 支持对 RestTemplate 的服务调用使用 Sentinel 进行保护,在构造RestTemplate bean的时候需要加上 @SentinelRestTemplate 注解。
SentinelRestTemplate用于sentinel 集成 RestTemplate。可以添加在 RestTemplate上,全局的限流容错处理,优先级低于局部限流容错注解。例如 @SentinelRestTemplate(blockHandler ="handleException", blockHandlerClass = ExceptionUtil.class, fallback = "fallback",fallbackClass = ExceptionUtil.class)
blockHandler限流策略 (方法名,方法必须是静态的)
blockHandlerClass 限流方法类
fallback 熔断降级策略(方法名,方法必须是静态的)
fallbackClass 熔断降级类
@Bean
@LoadBalance
@SentinelRestTemplate
public RestTemplate restTemplate(){
return new RestTemplate();
}
对应实现,static修饰参数类型不能出错
public class ExceptionUtil {
// 服务流量控制处理
public static ClientHttpResponse handleException(HttpRequest request,
byte[] body, ClientHttpRequestExecution execution, BlockException exception)
{
exception.printStackTrace();
return new SentinelClientHttpResponse( JSON.toJSONString(new
Product(1, "服务流量控制处理-托底数据")));
}
// 服务熔断降级处理
public static ClientHttpResponse fallback(HttpRequest request,byte[]
body, ClientHttpRequestExecution execution, BlockException exception) {
exception.printStackTrace();
return new SentinelClientHttpResponse( JSON.toJSONString(new
Product(1, "服务熔断降级处理-托底数据")));
}
}
SpringCloud中微服务调用都是通过Feign来实现的,因此做客户端保护必须整合Feign和Sentinel。
在启动类上面加个注解 @EnableFeignClients
修改OrderService的application.yml文件,开启Feign的Sentinel功能 feign.sentinel.enabled=true
开启feign对sentinel的支持
方法1:定义对应FeignClient接口的实现类,提供对应的处理
@Component
public class UserClientImpl implements UserClient {
@Override
public JsonResult getAllUsers() {
return JsonResult.failure(103,"加载失败...");
}
}
然后在FeignClient上添加注解说明
@FeignClient(value="user-provider",fallback = UserClientImpl.class)
public interface UserClient {
@GetMapping("/users")
public JsonResult getAllUsers();
}
方法2:自定义工厂 [推荐使用]
@Component
public class MyFeignFactory implements FallbackFactory<UserClient> {
@Override
public UserClient create(Throwable cause) {
return new UserClient() {
@Override
public JsonResult getAllUsers() {
return JsonResult.failure(1031,"加载失败...");
}
};
}
}
在配置类中添加配置
@Bean
public MyFeignFactory myClientFallbackFactory(){
return new MyFeignFactory();
}
在FeignClient接口上添加配置使用MyFeignFactory
@FeignClient(value="user-provider",fallbackFactory = MyFeignFactory.class)
public interface UserClient {
@GetMapping("/users")
public JsonResult getAllUsers();
}
业务失败后不能直接报错,而应该返回用户一个友好提示或者默认结果,这个就是失败降级逻辑。可以给FeignClient编写失败后的降级逻辑。
方式一FallbackClass无法对远程调用的异常做处理
方式二FallbackFactory可以对远程调用的异常做处理
定义类实现FallbackFactory
@Slf4j
public class UserClientFallbackFactory implements
FallbackFactory<UserClient> {
@Override
public UserClient create(Throwable throwable) {
return new UserClient() {
@Override
public User findById(Long id) {
log.error("查询用户异常", throwable);
return new User();
}
};
}
}
项目中的DefaultFeignConfiguration类中将UserClientFallbackFactory注册为一个Bean
@Bean
public UserClientFallbackFactory userClientFallbackFactory(){
return new UserClientFallbackFactory();
}
项目中的UserClient接口中使用UserClientFallbackFactory
@FeignClient(value = "userservice", fallbackFactory =
UserClientFallbackFactory.class) public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
在需要被保护的方法上使用@SentinelResource注解进行熔断配置。与Hystrix不同的是,Sentinel对抛出异常和熔断降级做了更加细致的区分,通过 blockHandler 指定熔断降级方法,通过 fallback 指定 触发异常执行的降级方法
@GetMapping("/buy/{id}")
@SentinelResource(value="order",blockHandler = "orderblockHandler",fallback
= "orderfallback")
public Product order(@PathVariable Long id) {
return restTemplate.getForObject("http://shop-
service/product/product/1", Product.class);
}
//降级方法
public Product orderblockHandler(Long id) {
Product product = new Product();
product.setId(-1l);
product.setProductName("触发熔断降级方法");
return product;
}
//降级方法
public Product orderfallback(Long id) {
Product product = new Product();
product.setId(-1l);
product.setProductName("触发抛出异常方法");
return product;
}
Sentinel支持的雪崩解决方案:线程隔离的仓壁模式、降级熔断