Sentinel 是什么?
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应过载保护、热点流量防护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
Sentinel 是分布式系统的防御系统。以流量为切入点,通过动态设置的流量控制、服务熔断等手段达到保护系统的目的,通过服务降级增强服务被拒用户的体验。
服务降级是一种增强用户体验的方式。当用户的请求由于各种原因被拒后,系统返回一个事先设定好的、用户可以接受的,但又令用户并不满意的结果。这种请求处理方式称为服 务降级。
案例完整源码
对于 Sentinel,服务降级的实现方式根据消费者类型的不同,其支持两种方式:
在原有的feign-nacos项目中添加依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
<version>2.2.1.RELEASEversion>
dependency>
/**
* 定义了降级
*/
@SentinelResource(fallback = "getHandleFallback")
@GetMapping("/get/{id}")
public DepartVO getHandle(@PathVariable("id") int id) {
return departService.getDepartById(id);
}
public DepartVO getHandleFallback(int id, Throwable e) {
DepartVO departVO = new DepartVO();
departVO.setId(id);
departVO.setName("degrade-method-" + id + "-" + e.getMessage());
return departVO;
}
public class DepartServiceFallback {
public static boolean saveFallback(DepartVO depart, Throwable e) {
System.out.println("getHandle()执行异常 " + depart.toString());
return false;
}
public static List<DepartVO> listFallback() {
System.out.println("listHandle()执行异常 ");
DepartVO departVO = new DepartVO();
List<DepartVO> arrayList = new ArrayList<>();
departVO.setId(1);
departVO.setName("no any depart");
arrayList.add(departVO);
return arrayList;
}
}
/**
*@SentinelResource注解中指定降级方法和降级方法对应的类
*/
@SentinelResource(fallback = "saveFallback", fallbackClass = DepartServiceFallback.class)
@PostMapping("/save")
public boolean saveHandle(@RequestBody DepartVO depart) {
return departService.saveDepart(depart);
}
@SentinelResource(fallback = "listFallback", fallbackClass = DepartServiceFallback.class)
@GetMapping("/list")
public List<DepartVO> listHandle() {
return departService.listAllDeparts();
}
只启动消费者
消费者端项目中添加依赖
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
<version>2.2.1.RELEASEversion>
dependency>
feign:
sentinel: # 开启sentinel对feign的支持
enabled: true
在feign对外接口中添加DepartServiceFallback降级类
降级类实现feign接口,在方法中根据实际业务完成降级业务
// 降级类:实现了Feign接口
@Component
//@RequestMapping("/fallback/provider/depart") // 其开头必须是/fallback
public class DepartServiceFallback implements DepartService {
@Override
public boolean saveDepart(DepartVO depart) {
System.out.println("执行saveDepart()的服务降级处理方法");
return false;
}
@Override
public boolean removeDepartById(int id) {
System.out.println("执行removeDepartById()的服务降级处理方法");
return false;
}
@Override
public boolean modifyDepart(DepartVO depart) {
System.out.println("执行modifyDepart()的服务降级处理方法");
return false;
}
@Override
public DepartVO getDepartById(int id) {
System.out.println("执行getDepartById()的服务降级处理方法");
DepartVO depart = new DepartVO();
depart.setId(id);
depart.setName("degrade-feign");
return depart;
}
@Override
public List<DepartVO> listAllDeparts() {
System.out.println("执行listAllDeparts()的服务降级处理方法");
return null;
}
}
//在注解中指定降级类
@FeignClient( value="feign-nacos-provider-modules", path = "/provider/depart",
fallback = DepartServiceFallback.class)
public interface DepartService {
@PostMapping("/save")
boolean saveDepart(@RequestBody DepartVO depart);
@DeleteMapping("/del/{id}")
boolean removeDepartById(@PathVariable("id") int id);
@PutMapping("/update")
boolean modifyDepart(@RequestBody DepartVO depart);
@GetMapping("/get/{id}")
DepartVO getDepartById(@PathVariable("id") int id);
@GetMapping("/list")
List<DepartVO> listAllDeparts();
}
只启动消费者,模拟生产者异常。访问删除接口。结果发现执行了降级方法
Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能。
Sentinel 控制台包含如下功能:
注意:Sentinel 控制台目前仅支持单机部署。Sentinel 控制台项目提供 Sentinel 功能全集示例,不作为开箱即用的生产环境控制台,不提供安全可靠保障。若希望在生产环境使用请根据文档自行进行定制和改造。
直接从官方下载打包好的 Sentinel Dashboard 启动运行
组件版本关系
Sentinel Dashboard 版本需要和Spring Cloud Alibaba Version对应,项目中Spring Cloud Alibaba Version为2.2.1.RELEASE,我们选用1.7.1以上版本。1.8版本和1.7版本界面变化有些大,选用新一点版本使用1.8.0下载
启动 Sentinel 控制台需要 JDK 版本为 1.8 及以上版本
java -Dserver.port=8888 -Dcsp.sentinel.dashboard.server=localhost:8888
-Dproject.name=sentinel-dashboard
-Dsentinel.dashboard.auth.username=sentinel
-Dsentinel.dashboard.auth.password=sentinel -jar sentinel-dashboard-1.8.0.jar
http://localhost:8888/#/login
(1) 服务雪崩
如果说服务流控是为了在高并发场景下不至于将系统压垮,那么服务熔断则是为了在外部环境不通畅场景下,不至于将系统拖垮,也就是为了防止服务雪崩的发生。
在复杂的系统中,经常会出现 A 依赖于 B,B 依赖于 C,C 依赖于 D,……这种依赖将会产生很长的调用链路,这种复杂的调用链路称为 1->N 的扇出。
如果在 A 的调用链路上某一个或几个被调用的子服务不可用或延迟较高,则会导致调用A 服务的请求被堵住(等待超时)。堵住的 A 请求会消耗占用系统的线程、IO 等资源,当对A 服务的请求越来越多,占用的计算机资源越来越多的时候,会导致系统瓶颈出现,造成其他的请求同样不可用,最终导致业务系统崩溃,这种现象称为雪崩效应
。
(2) 服务熔断
Hystrix官网
为了防止服务雪崩的发生,在发现了对某些资源请求的响应缓慢或调用异常较多时,直接将对这些资源的请求掐断一段时间。而在这段时间内的请求将不再等待超时,而是直接返回事先设定好的降级结果。这些请求将不占用系统资源,从而避免了服务雪崩的发生。这就是服务熔断。
对于服务熔断规则,可以通过 Sentinel Dashboard 进行动态设置。
在消费者类处理器上配置降级名称,后续降级名称需要在sentinel dashboard中使用。这样客户端上配置的降级规则才能够知道给谁使用。
/**
* 定义了降级
*/
@SentinelResource(value = "slowRequstDegradeRule",fallback = "getHandleFallback")
@GetMapping("/get/{id}")
public DepartVO getHandle(@PathVariable("id") int id) {
return departService.getDepartById(id);
}
spring:
application:
name: feign-nacos-consumer # 微服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos discovery地址
sentinel:
transport:
dashboard: localhost:8888 # Sentinel客户端地址
port: 8197 # 服务和客户端通信端口,在客户端上配置会生效到服务上需要发送请求
eager: true # 开启配置后立即生效
先启动消费者服务,在进入sentinel控制台。
设置睡眠时间,模拟接口请求慢。设置完成启动提供者
@GetMapping("/get/{id}")
public DepartVO getHandle(@PathVariable("id") int id) throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(500);
DepartVO departVO = new DepartVO();
departVO.setId(1);
departVO.setName("liming");
return departVO;
}
http://localhost:8080/consumer/depart/get/1
3.紧接着再次刷新接口
还是返回的降级结果。设置了熔断时长为20s,在这20秒访问会直接走降级逻辑。
慢调用比。该策略需要设置用于界定慢调用的响应时间阈值 RT(Response Time),
当请求的响应时间大于该值时,将该请求统计为慢调用。若要发生熔断,
在 1 秒内收到的请求数 量不能小于“最小请求数”,且慢调用占比不能低于“比例阈值”。
当触发熔断,则会在“熔断时长”内不再对请求进行处理,即熔断期间再来的请求,
将直接进行降级响应。
异常比。该策略需要设置异常请求在统计时间窗口内所有请求中的占比,异常比率的阈
值范围是 [0.0, 1.0],代表 0% - 100%。当异常请求比例大于该值时则会触发熔断。
默认情况下,统计时间窗口大小为 1 秒,期间接收到的请求至少 5 个。
发生熔断后,熔断时长为指定的时长。熔断期间再来的请求,将直接地降级响应。
异常数。该策略需要设置在统计时间窗口内所接收到的异常请求的数量。
当异常请求数量大于该值时则会触发熔断。默认情况下,统计时间窗口大小为 1 分钟,
注意,是 1 分钟。发生熔断后,熔断时长为指定的时长。熔断期间再来的请求,
将直接地降级响应。
通过 Dashboard 平台进行熔断规则设置,粒度有些粗,有些属性只能通过代码来设置。而代码中的 API,在不同的 Sentinel 版本中,是有所不同的。
熔断规则直接定义在代码中,当应用启动时完成熔断规则的创建与初始化。这个熔断规则在 Dashboard 中也是可以查看到并且进行编辑的,编辑后以动态编辑的规则为准。
@Configuration
public class hystrixRuleConfig {
/**
* 初始化生效的降级规则
*/
@Bean
public void initRule() {
List<DegradeRule> rules = new ArrayList<>();
rules.add(slowRequestDegradeRule());
rules.add(errorRatioDegradeRule());
rules.add(errorCountDegradeRule());
DegradeRuleManager.loadRules(rules);
}
/**
* 慢调用
*/
@Bean
public DegradeRule slowRequestDegradeRule() {
//定义降级规则
DegradeRule degradeRule = new DegradeRule();
//指定降级规则名称
degradeRule.setResource("slowRequestDegradeRule");
//指定熔断策略为Rt(response time)
degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
//指定熔断阈值为200ms(请求响应时长大于200ms,统计为慢调用)
degradeRule.setCount(200);
//指定熔断时长60s
degradeRule.setTimeWindow(60);
//指定在1秒的统计时间窗内至少要5个请求
degradeRule.setMinRequestAmount(5);
//指定若要启动熔断,则在1秒内至少要3次慢请求
degradeRule.setRtSlowRequestAmount(3);
return degradeRule;
}
/**
* 异常比
*/
@Bean
public DegradeRule errorRatioDegradeRule() {
DegradeRule degradeRule = new DegradeRule();
//指定降级规则名称
degradeRule.setResource("errorRatioDegradeRule");
//指定熔断策略为异常比
degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
//指定熔断阈值为50%
degradeRule.setCount(0.5);
//指定熔断时长60s
degradeRule.setTimeWindow(60);
//指定在1秒的统计时间窗内至少要5个请求
degradeRule.setMinRequestAmount(5);
return degradeRule;
}
/**
* 异常数
*/
@Bean
public DegradeRule errorCountDegradeRule() {
DegradeRule degradeRule = new DegradeRule();
degradeRule.setResource("errorCountDegradeRule");
//指定熔断策略为异常数
degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
//指定熔断阈值为3
degradeRule.setCount(3);
//指定熔断时长60s
degradeRule.setTimeWindow(60);
//指定在1秒的统计时间窗内至少要5个请求
degradeRule.setMinRequestAmount(5);
return degradeRule;
}
}
启动消费者服务后我们观察一下控制台,发现三个降级规则都已成功注册。
测试异常
连续发送三次请求
流控,即流量控制,也称为限流。Sentinel 实现流控的原理是监控应用流量的 QPS 或并 发线程数等指标,当达到指定的阈值时对再来的请求进行进行控制,以避免被瞬时的流量高 峰冲垮,从而保障应用的高可用性。
流控规则直接通过 Sentinel Dashboard 定义,该规则可以随时修改而不需要重启应用该 规则的应用程序。所以这种流控是一种动态流控。
server:
port: 8080
spring:
application:
name: feign-nacos-consumer # 微服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 # nacos discovery地址
sentinel:
transport:
dashboard: localhost:8888 # Sentinel客户端地址
port: 8197 # 服务和客户端通信端口,在客户端上配置会生效到服务上需要发送请求
eager: true # 开启配置后立即生效
/**
* 定义了降级,和限流
*/
@SentinelResource(value = "qpsFlowRule",
blockHandler = "getBlockHandler",
fallback = "getHandleFallback")
@GetMapping("/get/{id}")
public DepartVO getHandle(@PathVariable("id") int id) {
return departService.getDepartById(id);
}
public DepartVO getBlockHandler(int id, BlockException e) {
DepartVO departVO = new DepartVO();
departVO.setId(id);
departVO.setName("flow-control" + id + "-" + e.getMessage());
return departVO;
}
public DepartVO getHandleFallback(int id, Throwable e) {
DepartVO departVO = new DepartVO();
departVO.setId(id);
departVO.setName("degrade-method-" + id + "-" + e.getMessage());
return departVO;
}
每秒访问量超过一次时,就会立刻进入限流函数。
http://localhost:8080/consumer/depart/get/1
@Configuration
public class flowRuleConfig {
@Bean
public void initFlowRule(){
List<FlowRule> flowRules=new ArrayList<>();
flowRules.add(qpsFlowRule());
FlowRuleManager.loadRules(flowRules);
}
@Bean
public FlowRule qpsFlowRule(){
FlowRule flowRule = new FlowRule();
flowRule.setResource("qpsFlowRule");
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
flowRule.setCount(1);
flowRule.setLimitApp("default");
return flowRule;
}
}
项目重新启动,限流配置会注册到控制台