第三章 Sentinel组件使用
代码地址:源码地址
1. 服务雪崩效应以及服务容错
由于tomcat等服务器支持的最大线程数有限,如果一个服务出现了问题,调用这个服务的服务器就会出现线程阻塞的情况,此时如果有大量请求进入,就会导致服务瘫痪,这个服务器上的其他接口也不能调用,继而导致其他服务也会阻塞,瘫痪。
这种传播就是服务雪崩效应。我们无法完全杜绝雪崩源头的发生,只有做好足够的容错,保证在一个服务发生问题,不会影响到其它服务的正常运行。也就是"雪落而不雪崩"。
服务容错:常见的容错思路有隔离、超时、限流、熔断、降级这几种。
降级:类似于try/catch,在服务接口级别的try/catch
容错组件有:Hystrix,Resilience4J,Sentinel。
2. Sentinel的使用
2.1 Sentinel的集成
a. 在pom.xml中加入下面依赖(给需要流量控制的微服务模块添加)
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
b.下载控制台jar包,下载地址
c.修改配置(每个集成了Sentinel的微服务模块都要加):
spring:
cloud:
sentinel:
transport:
port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可
dashboard: localhost:8080 # 指定控制台服务的地址
d.运行命令启动jar包
java -Dserver.port=8180 -Dcsp.sentinel.dashboard.server=localhost:8180 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.0.jar
e.访问localhost:8080 进入控制台 ( 默认用户名密码是sentinel/sentinel )
控制台界面如下图所示
注:需要微服务访问一次,才能刷新加载出微服务的资源信息。
2.2 Sentinel主要功能
流量控制:将请求排成队列形状处理。
熔断降级:当要调用的服务的响应时间变长或者异常比例升高的时候,减少该调用的并发线程数量,快速失败,以限制其对资源的占用。(hystrix利用线程池隔离的方式进行限制,线程池隔离:对每个服务调用接口划分一块线程池,调用某服务只能使用对应线程池里的线程,因此达到隔离效果不影响统一服务器上的其他接口)
系统负载保护:集群环境下,当cpu,内存快占满时,把请求发到其他机器上去。
2.3 Sentinel规则
2.3.1 流控规则
对某一资源设置访问频率限制
阈值类型:频率指标有QPS(每秒请求次数),线程数(并发线程个数)
单机阈值:对上述频率定量规则
流控模式:直接:对资源的直接限制。
关联:当某一资源达到上述设定的限制时,另一资源会被限流。
链路:假设有一个服务a,会调用另一个资源b(可以是一个接口,也可以是一个方法,用@SentinelResource注解可以设置该资源的名字),当b满足上述访问频率时,会对a进行限流。举个例子:
上面这个/order/prod/1 是一个接口的调用,它涉及到对GET http://service-product/reduceInventory这个接口和GET http://……的调用,这两个接口相当于上面说的的资源b,/order/prod/1相当于上面的服务a,(除了服务a以外可能还有其他服务调用资源b,或者直接访问资源b),这时候只对a进行流控,对其他调用b的不流控。
流控效果:快速失败:当一个资源被流控时,再次访问该资源时直接出异常界面。
Warm up:预热,逐渐放开频率限制,阈值逐渐增长。
排队等待:处理不过来的请求会放到队列中等待被处理。
2.3.2 降级规则
去搜了下降级,流控,熔断的区别:流控超过频率就把请求完全屏蔽等频率降下来就继续提供服务,降级超过频率采用备用服务或者丢弃部分资源,等待一定时间以后继续提供服务。熔断直接停用服务,等一段时间后再去尝试是否可以调用服务。
降级策略:RT:一秒内的请求平均响应时间,时间窗口:等待的时间;
异常比例:一秒内的请求异常的数量/请求总数量,时间窗口:等待时间;
异常数:一分钟产生的异常个数,时间窗口:等待时间(需要大于60s)
2.3.3 热点规则
带参数的流控规则
略
2.3.4 授权规则
判断请求头/参数,确定是否为白名单/黑名单,下面是一个配置类,,请求发出的时候的该类就可以从url上获取request域的源标识。
@Component
public class RequestOriginParserDefinition implements RequestOriginParser{
@Override
public String parseOrigin(HttpServletRequest request) {
String serviceName = request.getParameter("serviceName");
if(StringUtils.isEmpty(serviceName)){
throw new Exception("serviceName is empty");
}
return serviceName;
}
}
2.3.5 系统规则
服务器维度的流控。
略
2.3.6 自定义异常返回
写个配置类,继承UrlBlockHandler,它会接受异常,然后运行blocked,可以在该函数里面写if,else语句判断异常类型,进行相对处理,实例代码:
@Component
public class ExceptionHandlerPage implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
httpServletResponse.setContentType("application/json;charset=utf-8");
ResponseData responseData = null;
if(e instanceof FlowException){
responseData = new ResponseData(-1,"接口被限流了");
}else if(e instanceof DegradeException){
responseData = new ResponseData(-2,"接口被降级了");
}
httpServletResponse.getWriter().write(JSON.toJSONString(responseData));
}
}
2.4 @SentinelResource的使用
@SentinelResource有value属性,定义资源名,有blockHandler,后面跟函数名,处理sentinel的异常。fallback后面跟函数名。处理所有异常。实例代码:
@Service
@Slf4j
public class TestService {
//blockHandler sentinel异常
//fallback
@SentinelResource(value = "message",blockHandler = "blockHandler",fallback = "fallBack")
public String message(){
return "message";
}
public String blockHandler(BlockException blockException){
log.error("触发了blockException内容{}",blockException);
return "blockException";
}
public String fallBack(Throwable e){
log.error("触发了fallback内容{}",e);
return "fallback";
}
}
blockHandlerClass可以指定一个类,与blockHandler拼接就是哪一个类的哪一个函数。函数必须是static,参数必须是和原请求参数一致,在最后可以加一个参数,Exception。fallBack同理也有个fallBackClass……
2.5 Sentinel规则持久化
略
3. Feign整合Sentinel
3.1 fallBack容错类
①在配置文件中开启Feign对Sentinel的支持
feign:
sentinel:
enabled: true
②创建容错类,容错类,需要实现feign所在接口,并实现接口中所有方法,一旦远程调用出错,即进入容错类的同名方法。实例代码:
//容错类,需要实现feign所在接口,并实现接口中所有方法
//一旦远程调用出错,即进入容错类的同名方法
@Service
public class ProductServiceFallback implements ProductService {
@Override
public Product findByPid(Integer pid) {
//容错逻辑
Product p = new Product();
p.setPid(-100);
return p;
}
@Override
public String reduceInventory(Integer pid, Integer number) {
return "error";
}
}
③为被容器的接口指定容错类,@FeignClient注解的value值是调用的服务名,fallBack值是容错类的类名.class。
实例:
@FeignClient(value = "service-product", fallback = ProductServiceFallBack.class)
fallBack有个缺陷,就是远程调用失败转容错类的时候不会把远程调用的异常信息抛出来。
3.2 fallBackFactory容错类
容错类实现FallbackFactory<>接口范型里面是feign的接口类,需要重写方法create,返回值就是feign的接口类。用匿名内部类,或者Lambda表达式的方式创建新的接口类,并实现。实例代码如下:
@Slf4j
@Service
public class ProductServiceFallbackFactory implements FallbackFactory<ProductService> {
@Override
public ProductService create(Throwable throwable) {
return new ProductService() {
@Override
public Product findByPid(Integer pid) {
log.error("{}",throwable);
//容错逻辑
Product p = new Product();
p.setPid(-100);
return p;
}
@Override
public String reduceInventory(Integer pid, Integer number) {
return "error";
}
};
}
}
@FeignClient注解中fallBackFactory值指定上面创建容错类的类名.class