雪崩效应

高并发,分布式的系统,如果微服务A挂了,B又没有做好容错,还在继续疯狂调用A,会发生什么?
基础服务故障导致上层服务故障,并且这个故障不断放大的过程称为“雪崩效应”
有时在英文书中也叫:cascading failure (级联失效、级联故障)
常见容错方案
雪崩效应往往是因为服务消费者没有做好容错导致的,如上图,如果B做好了容错,它就不会被A拖死
常见的容错方案有:
- 超时:为每个请求设置一个很短的请求时间,不管请求是否成功,到时间线程都会被释放
- 限流:B被拖死的前期是“高并发”,所以才会出现大量的线程阻塞。可以根据QPS为服务提供者A的实例设置一个阈值,达到这个值就拒绝请求
- 舱壁模式:为每个Controller设置独立大小的线程池,线程池有拒绝策略,到达阈值可以拒绝请求

https://en.wikipedia.org/wiki/Bulkhead_(partition))
- 断路器模式:断路器的基本原理非常简单。 将受保护的函数调用包装在断路器对象中,该对象将监视故障。 一旦故障达到一定阈值,断路器将跳闸,并且所有进一步的断路器调用都会返回错误,而根本不会进行受保护的调用。
马丁弗勒《断路器模式》原文:https://martinfowler.com/bliki/CircuitBreaker.html
监控接口,达到阈值就跳闸,目的是保护自己不被拖死
但是断开后,如果服务提供方又恢复正常了,怎么继续能调用呢?
断路器模式巧妙地设计了一种“半开”模式
半开状态是一个瞬间态,允许一次请求,来验证是否恢复 ,如果调用成功,断路器关闭;如果失败,断路器继续打开。通过这种方式,实现了微服务的自我修复。

通俗总结四种服务容错的思想
- 超时:只要释放够快,我就没那么容易死
- 限流:我就一碗的饭量,不管你给我多少,反正我只吃一碗
- 舱壁模式:不把鸡蛋放在一个篮子里
- 断路器模式:监控+开关
实际中间件的实现,比如Sentinel,会和以上稍有不同,只是说明思想。
使用Sentinel实现容错
- Sentinel是什么:分布式系统的流量防卫兵
As distributed systems become increasingly popular, the reliability between services is becoming more important than ever before. Sentinel takes “flow” as breakthrough point, and works on multiple fields including flow control, traffic shaping, circuit breaking and system adaptive protection, to guarantee reliability and resilience for microservices.定义 随着分布式系统越来越流行,服务之间的可靠性变得比以往任何时候都更加重要。Sentinel以“流”为切入点,在流控制、交通整形、断路、系统自适应保护等多个领域开展工作,保证微服务的可靠性和弹性。
Sentinel 具有以下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
- 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel主要特征:


Sentinel Github地址:https://github.com/alibaba/Sentinel
文档地址:https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
新手指南:https://github.com/alibaba/Sentinel/wiki/%E6%96%B0%E6%89%8B%E6%8C%87%E5%8D%97#%E5%85%AC%E7%BD%91-demo
整合Sentinel
加依赖
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>
加注解:没有
- 写配置:没有
只要加上依赖,应用就整合好了Sentinel,那么,如何证明呢?
应用整合Sentinel后,会暴露一个“/actuator/sentinel”端点(默认是隐藏的,需要配置)
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: always
启动内容中心,访问:http://localhost:8888/actuator/sentinel
莫名其妙报了个错,解决:
Sentinel控制台
- 搭建Sentinel控制台
下载地址:https://github.com/alibaba/Sentinel/releases
Sentinel控制台文档:https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
Sentinel1.6开始引入登录页面,账号密码都是sentinel

- 整合应用&控制台
yml加入配置,指定sentinel控制台地址和端口
刷新Sentinel控制台,依然一片空白,why?
sentinel是懒加载,所以需要访问内容中心的API之后,才会有内容
需要调用几次content-center的接口后,再次刷新,才可以看到结果
QPS、TPS、吞吐量、并发用户量的概念
JMeter:性能测试
流控规则
- 直接
- 关联
- 链路

新增一个流控规则,模式为直接
访问http://localhost:8888/shares/1,只有第一次能成功,后面就报错了,因为单机的阈值配置为1
改为“关联”,表示当关联的资源达到阈值,就限流自己
测试代码,通过RestTemplate来调用关联的/actuator/sentinel端点,由于它被多次访问超过阈值,因此会让”/shares/1”被限流,访问失败
可以用debug模式调试,查看过程
public class SentinelTest {
public static void main(String[] args) throws InterruptedException {
RestTemplate restTemplate = new RestTemplate();
for (int i = 0; i < 100; i++) {
String object = restTemplate.getForObject("http://localhost:8888/actuator/sentinel", String.class);
Thread.sleep(1000);
}
}
}
在同样的数据既要被修改接口访问,又要被查询接口访问的时候,可以通过这样的方式,权衡读和写的优先级,保护资源。
“链路”:只记录指定链路上的流量
测试代码
TestService
@Service
public class TestService {
//指定sentinel的资源名称
@SentinelResource("common")
public String commonMethod() {
log.info("commonMethod....");
return "common";
}
}
**
TestController
@GetMapping("test-a")
public String testA() {
this.testService.commonMethod();
return "test-a";
}
@GetMapping("test-b")
public String testB() {
this.testService.commonMethod();
return "test-b";
}
重启应用,新增流控规则如下:
可以实现细粒度的流量监控,”/common”这个资源的”/test-a”端点限流,”/test-b”不受影响


