微服务架构➖SpringCloud➖Sentinel
关于作者
- 作者介绍
🍓 博客主页:作者主页
🍓 简介:JAVA领域优质创作者🥇、一名在校大三学生🎓、在校期间参加各种省赛、国赛,斩获一系列荣誉 🏆、阿里云专家博主 、51CTO专家博主
🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨💻
Spring Cloud Alibaba Sentinel
服务雪崩效应以及服务容错
由于tomcat等服务器支持的最大线程数有限,如果一个服务出现了问题,调用这个服务的服务器就会出现线程阻塞的情况,此时如果有大量请求进入,就会导致服务瘫痪,这个服务器上的其他接口也不能调用,继而导致其他服务也会阻塞,瘫痪。 这种传播就是服务雪崩效应。我们无法完全杜绝雪崩源头的发生,只有做好足够的容错,保证在一个服务发生问题,不会影响到其它服务的正常运行。也就是"雪落而不雪崩"。 服务容错:常见的容错思路有隔离、超时、限流、熔断、降级这几种。 降级:类似于try/catch,在服务接口级别的try/catch 容错组件有:Hystrix,Resilience4J,Sentinel。
1.Sentinel初识
Sentinel (分布式系统的流量防卫兵)是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。
Sentinel具有以下特征:
-
丰富的应用场景:Sentinel承接了阿里巴巴近10年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
-
完备的实时监控:Sentinel提供了实时的监控功能。通过控制台可以看到接入应用的单台机器秒级数据,甚至500台以下规模的集群的汇总运行情况。
-
广泛的开源生态:Sentinel提供开箱即用的与其它开源框架/库的整合模块,例如与Spring Cloud、Dubbo.gRPC的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。
-
完善的SPI扩展点: Sentinel提供简单易用、完善的SPI扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
sentinel分为两个部分:
- 核心库(Java客户端)不依赖任何框架/库,能够运行于所有Java运行时环境,同时对Dubbo / Spring Cloud等框架也有较好的支持。
- 控制台(Dashboard)基于Spring Boot开发,打包后可以直接运行,不需要额外的Tomcat 等应用容器。
2. Sentinel的使用
2.1 Sentinel的集成
- 在pom.xml中加入下面依赖(给需要流量控制的微服务模块添加)
xml
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
-
下载控制台jar包,
sentinel-dashboard-1.7.0.jar
-
修改配置(每个集成了Sentinel的微服务模块都要加):
yaml
spring:
cloud:
sentinel:
transport:
port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可
dashboard: localhost:8080 # 指定控制台服务的地址
- 运行命令启动jar包,
sentinel-dashboard-1.7.0.jar
shell
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.0.jar
- 访问localhost:8080 进入控制台 ( 默认用户名密码是sentinel/sentinel ) 控制台界面如下图所示
注:需要微服务访问一次,才能刷新加载出微服务的资源信息。
2.2 Sentinel主要功能
流量控制:将请求排成队列形状处理。
熔断降级:当要调用的服务的响应时间变长或者异常比例升高的时候,减少该调用的并发线程数量,快速失败,以限制其对资源的占用。(hystrix利用线程池隔离的方式进行限制,线程池隔离:对每个服务调用接口划分一块线程池,调用某服务只能使用对应线程池里的线程,因此达到隔离效果不影响统一服务器上的其他接口)
系统负载保护:集群环境下,当cpu,内存快占满时,把请求发到其他机器上去。
2.3 Sentinel规则
2.3.1 流控规则
对某一资源设置访问频率限制
阈值类型 :频率指标有QPS(每秒请求次数),线程数(并发线程个数) 单机阈值:对上述频率定量规则
流控模式:直接:对资源的直接限制。 关联:当某一资源达到上述设定的限制时,另一资源会被限流。 链路:假设有一个服务A,会调用另一个资源B(可以是一个接口,也可以是一个方法,用@SentinelResource注解可以设置该资源的名字),当b满足上述访问频率时,会对a进行限流。
流控效果:快速失败:当一个资源被流控时,再次访问该资源时直接出异常界面。 Warm up:预热,逐渐放开频率限制,阈值逐渐增长。 排队等待:处理不过来的请求会放到队列中等待被处理。
2.3.2 降级规则
流控,降级,熔断的区别:流控超过频率就把请求完全屏蔽等频率降下来就继续提供服务,降级超过频率采用备用服务或者丢弃部分资源,等待一定时间以后继续提供服务。熔断直接停用服务,等一段时间后再去尝试是否可以调用服务。
降级策略:RT:一秒内的请求平均响应时间,时间窗口:等待的时间; 异常比例:一秒内的请求异常的数量/请求总数量;异常数:一分钟产生的异常个数
2.3.3 热点规则
热点参数流控规则是一种更细粒度的流控规则, 它允许将规则具体到参数上。
带参数的流控规则
第1步: 编写代码
java
@RequestMapping("/order/message3")
@SentinelResource("message3")
public String message3(String name, Integer age) {
return "message3" + name + age;
}
第2步: 配置热点规则
第3步: 分别用两个参数访问,会发现只对第一个参数限流了
热点规则增强使用
参数例外项允许对一个参数的具体值进行流控,编辑刚才定义的规则,增加参数例外项
2.3.4 授权规则
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源 访问控制的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过:
- 若配置白名单,则只有请求来源位于白名单内时才可通过;
- 若配置黑名单,则请求来源位于黑名单时不通过,其余的请求通过。
判断请求头/参数,确定是否为白名单/黑名单,下面是一个配置类,,请求发出的时候的该类就可以从url上获取request域的源标识。
java
@Component
public class RequestOriginParserDefinition implements RequestOriginParser {
//定义区分来源: 本质作用是通过request域获取到来源标识
//app pc
//然后 交给流控应用 位置进行匹配
@Override
public String parseOrigin(HttpServletRequest request) {
String serviceName = request.getParameter("serviceName");
if (StringUtils.isEmpty(serviceName)){
throw new RuntimeException("serviceName is not empty");
}
return serviceName;
}
}
分别访问:
http://localhost:8091/order/message1?serviceName=service
http://localhost:8091/order/message1?serviceName=service-A
记一次错误
shell
java.util.ServiceConfigurationError: com.alibaba.csp.sentinel.init.InitFunc: Provider com.itheima.config.FilePersistence not found
因为没有配置 FilePersistence
而导致的sentinel无法使用。
2.3.5 系统规则---服务器维度的流控
系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load、RT、入口 QPS 、CPU 使用率和线程数五个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量 (进入应用的流量) 生效。
- Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过 系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般 是 CPU cores * 2.5。
- RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。 CPU使用率:当单台机器上所有入口流量的 CPU使用率达到阈值即触发系统保护。
2.3.6 自定义异常返回
写个配置类,继承UrlBlockHandler,它会接受异常,然后运行blocked,可以在该函数里面写if,else语句判断异常类型,进行相对处理,实例代码:
java
package com.itheima.config;
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//自定义异常返回页面
@Component
public class ExceptionHandlerPage implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException e) throws IOException {
response.setContentType("application/json;charset=utf-8");
ResponseData responseData = null;
//BlockException 异常接口,包含Sentinel的五个异常
// FlowException 限流异常
// DegradeException 降级异常
// ParamFlowException 参数限流异常
// AuthorityException 授权异常
// SystemBlockException 系统负载异常
if (e instanceof FlowException) {
responseData = new ResponseData(-1, "接口被限流了...");
} else if (e instanceof DegradeException) {
responseData = new ResponseData(-2, "接口被降级了...");
}
response.getWriter().write(JSON.toJSONString(responseData));
}
}
@Data
@AllArgsConstructor//全参构造
@NoArgsConstructor
//无参构造
class ResponseData {
private int code;
private String message;
}