限流规则配置
每次服务重启后 之前配置的限流规则就会被清空因为是内存态的规则对象,所以就要用到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表示排队等待
隔离降级
限流是一种预防措施,虽然限流可以尽量避免因高并发而引起的服务故障,但服务还会因为其它原因而故障。而要将这些故障控制在一定范围,避免雪崩,就要靠线程隔离(舱壁模式)和熔断降级手段了。
-
线程隔离就是调用者在调用服务提供者时,给每个调用的请求分配独立线程池,出现故障时,最多消耗这个线程池内资源,避免把调用者的所有资源耗尽
-
熔断降级是在调用方这边加入断路器,统计对服务提供者的调用,如果调用的失败比例过高,则熔断该业务,不允许访问该服务的提供者了。
不管是线程隔离还是熔断降级,都是对客户端调用方的保护。需要在调用方发起远程调用时做线程隔离、或者服务熔断。
RestTemplate整合Sentinel
Spring Cloud Alibaba Sentinel 支持对 RestTemplate 的服务调用使用 Sentinel 进行保护,在构造RestTemplate bean的时候需要加上 @SentinelRestTemplate 注解。
- @SentinelRestTemplate 注解的属性支持限流(blockHandler, blockHandlerClass)和降级(fallback, fallbackClass)的处理。
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, "服务熔断降级处理-托底数据")));}
}
FeignClient整合Sentinel
SpringCloud中微服务调用都是通过Feign来实现的,因此做客户端保护必须整合Feign和Sentinel。
在启动类上面加个注解 @EnableFeignClients
修改OrderService的application.yml文件,开启Feign的Sentinel功能 feign.sentinel.enabled=true
开启feign对sentinel的支持
方法1:定义对应FeignClient接口的实现类,提供对应的处理
@Component
public class UserClientImpl implements UserClient {@Overridepublic 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> {@Overridepublic UserClient create(Throwable cause) {return new UserClient() {@Overridepublic 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> {@Overridepublic UserClient create(Throwable throwable) {return new UserClient() {@Overridepublic 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支持的雪崩解决方案:线程隔离的仓壁模式、降级熔断