一 Sentinel

1.1、背景:雪崩问题

雪崩问题,就是指在微服务调用链路中,某个服务出现故障,结果引起整个链路的所有微服务都不可用.在微服务中,业务往往会比较复杂,一业务服务可能会依赖多个服务. 比如有一个 服务a 内部会依赖 服务b、服务c、服务d.  现在假设 服务d 出现了故障,那么 服务a 依赖于 服务d 的请求还能正常响应吗?显然不能,请求来了以后,就要等待 服务d 的响应,因为 服务d 出现故障了以后不能正常返回响应,因此就会阻塞在这里,导致 服务a 内部的业务阻塞在这里. 也就不会释放 tomcat 连接.

SpringCloud-Sentinel解决雪崩问题_线程池

编辑

此时,服务a 内部依赖的 服务b 或 c 的业务还可以正常运行,但是如果依赖于 服务d 的请求肯定后续还会有多个,他们都不会释放连接,就导致把 a 内部所有连接都占用了,连接资源也就耗尽了,此时,就导致依赖的 b 和 c 的服务故障了.  

SpringCloud-Sentinel解决雪崩问题_sentinel_02

编辑

这还不是最严重的,在微服务的调用关系中很复杂,也就是说,会有其他服务也依赖于 a 服务,进一步导致其他依赖 a 的服务故障,最后整个微服务群就垮了.

1.2、雪崩问题的解决办法

1.2.1、超时处理

超时处理:设定一个超时时间,当请求超过一定时间没有响应时,就直接返回错误信息,而不是无休止等待.

就拿刚才的栗子讲,服务a 内部依赖 b、c、d,此时 d 出现了故障,a 内部依赖于 c 的所有业务都会阻塞,假以时日 a 也就崩溃了. 那么 超时处理 会怎么做呢?

他会在调用的业务上加一个超时时间,比如说 1s,也就是说,a 依赖于 d 的业务他最多等 1s.  当 d 故障了以后,等待时间超过 1s,他就会立即结束这个请求,不再等待返回,会给用户提示请求失败,那么这个就不会导致一直占用连接资源,可以一定程度上缓解雪崩问题.

为什么知识环节雪崩问题,而不是解决呢?

假设他只是等待 1s 以后把资源释放了,也就是说,1s 释放一个连接,那么如果现在请求来的速度比他释放的还要快(比如 500ms 一个请求),那么终有一天,服务a 的资源也有可能被耗尽(就像是一个蓄水池,进水的速度比出水快,终有一日,水池的水会溢出).因此 超时处理 并不能从根本上解决这个问题.

1.2.2、舱壁模式

舱壁模式的涉及来源于我们现实生活中~  一些大型的轮船,会利用隔板(也叫舱壁)把船体分隔成一个个独立的小空间,因此空间彼此之间时隔离的. 假设现在船体的某个部位撞上了冰山漏水了,那么最多也只是把部分船舱给他填满水了,不会导致整个船体挂满水,最后导致沉船,提高了整艘船的容灾能力.

SpringCloud-Sentinel解决雪崩问题_线程池_03

编辑

在程序涉及这边的做法是,把 tomcat 看成整艘船,限定每个业务能使用的线程数(相当于舱壁隔离),避免耗尽整个 tomcat 资源,因此这个方法也叫做“线程隔离”.这里还是拿刚刚的栗子来讲,假设限定每个业务分配一个线程池,限定线程最大数目为 10,此时 服务d 故障了,那么他最多也就只能占用 10 个连接资源,避免占用其他业务的连接资源.

缺陷: 浪费资源

这种模式确实解决了雪崩问题,但是资源上还是会有一定的浪费,比如 服务d 真的宕机了,明明知道他挂了,但请求还是回来,直到占满 10 个线程.  因此对 cpu 的资源也是一种浪费. 

1.2.3、熔断降级

这种模式中会有一个 断路器,它可以用来统计业务出现 故障的请求 和 正常请求 之间的比例是什么样子,如果超出来看阈值,就会熔断该业务,拦截访问该业务的所有请求.

拿之前的栗子讲,服务a 里有一个业务时访问 服务d 的,然后呢第一次访问时正常的,结果后面两次都出现了故障,这个时候熔断器就会统计这个异常比例是怎么样的.  假设阈值时 50%,而三个请求中,两个都出了问题,肯定超出了阈值,这个时候就会出现熔断,那么在 服务a 内部,访问 服务d 的业务都会被拦截下来,然后快速释放资源. 

这就有点像我们的手机快没电的时候,就会提示我们进入省电模式,他就会保留我们手机上的主要核心功能,暂时屏蔽边缘业务.

SpringCloud-Sentinel解决雪崩问题_sentinel_04

编辑

这种方式不仅解决了雪崩问题,也解决了 舱壁模式 中,资源浪费的情况.  因此,熔断降级模式是解决雪崩问题中比较好的一种方案.

1.2.4、流量控制

流量控制就是限制业务访问的 qps(每秒处理请求数量),避免服务因流量突增而故障.

比如有一个微服务能承受的最大 qps 是 2,也就是每秒钟最多处理两个请求,但是现在有一百个请求涌过来了,那他不得被达成筛子了,但是 sentinel 会根据这个 qps 的限制,去处理请求然后释放(这就像是有大量的水要涌入的时候,会先经过一个漏斗,通过这个漏斗,将水的流量限制住),这个时候,就可以有效的避免出现故障(超出的请求会被拦截,默认会报错,但也可以配置成 排队等待、预热).

SpringCloud-Sentinel解决雪崩问题_限流_05

编辑

流量控制和前面三种方法不同,前面三种方法是服务已经有故障了,而流量控制是通过限制 qps,避免出现故障.

误区:那是不是只用流量控制就可以,前面三种方法就不用了? 

当然不是,因为高并发引起的服务故障只是故障的原因之一,服务往往还会因为其他问题而引发故障,比如网络问题,或者是假死问题.

但是以上三种解决方式中,我们重点学习的是 舱壁模式、熔断降级、流量控制.

 1.3、Sentinel 和 Hystrix 的区别

Hystrix 是 SpringCloud 刚刚流行的这几年,推荐大家使用的服务保护机制,是由 Netflix 公司出品的,只不过后来这个公司宣布停止对 Hystrix 的升级和维护,逐渐就没落了,于是人们都开始寻找一种新的方案.这个时候,阿里巴巴开源了一个项目就是 sentinel ,并且成为了 SpringCloud 中服务保护的组件,现在已经广泛的应用在国内的互联网公司.

主要差异如下:

  1. 隔离策略:hystrix 底层是基于线程池和信号量实现的,默认情况下是使用线程池. sentinel 底层是基于信号量来实现的.  这两种方式有什么差别呢?
    线程池隔离就是给每一个业务链上都有一个独立的线程池来处理请求,但是一旦请求数量突增,就会导致多出很多线程,因此会给 cpu 带来一些额外的上下文切换的消耗(线程不是越多越好,就像之前讲过的 “一群滑稽吃鸡” 的栗子).
    信号量隔离就不会创建单独的线程池,而是使用一个大线程池,在请求来了之后做一个统计,统计当前业务已经使用了几个线程了,然后限制一下当前业务使用的线程数目,例如只能使用 10 个,假设你也已经使用了 10 个,那么再有新的业务来获取线程的时候,就会阻止你. 池子就是那一个池子,他默认不会创建新的线程,因此性能不受影响.
    这种方式相比于 线程池隔离,隔离性略微逊色一点,因此比较在同一个池子里,只不过一个大锅饭,每个人拿单独的碗成了,性能比较高.  因此我们认为信号量这种方案更好一
  2. 熔断降级,在 sentinel 中除了可以统计异常请求的比例,也可以统计慢调用的比例. 什么是慢调用呢?就是一个业务中,10 个请求中有 8 个耗时都比较久,可能导致拖慢整个服务,因此这个时候就会去他熔断掉.
  3. 限流: 在 sentinel 中,是支持基于 qps 限流、调用关系限流、甚至还可以针对热点的参数去限流.而在 hystrix 中,没有专门的限流控制,只是基于线程池,设置池子的线程数上限来限流.
  4. 流量变形: 在 sentinel 中,支持流量整形,就是让基于 慢启动(预热模式 或者 匀速排队) 让突发流量变成稳定的流速.而 hystrix 中是不支持这样的功能的.
  5. 控制台: 控制台也就是所谓的 可视化界面,方便你去操作.在 sentinel 中,控制台这边不仅可以监控微服务,查看运行状态,还可以配置我们的降级规则,先流规则,并且设置完立即动态生效.在 hystrix 中,控制台只支持查看服务状态的功能.因此相对来讲,sentinel 控制台的功能要强大一些.

1.4 安装Sentinel

Sentinel 官方提供了 UI 控制台,方便我们对系统做限流设置.

a)大家可以去 GitHub 上下载.

git clone https://github.com/alibaba/Sentinel.git

b)下载好 jar 包以后,把他拷贝到一个非中文的目录,然后打开终端,使用以下命令:

java -jar sentinel-dashboard-1.8.1.jar

SpringCloud-Sentinel解决雪崩问题_线程池_06

编辑官方教程中使用的端口是8080,用户可以自定义更改.

用户名和密码都是sentinel

例如,修改端口号为 8090

java -jar sentinel-dashboard-1.8.1.jar -Dserver.port=8090

SpringCloud-Sentinel解决雪崩问题_线程池_07

编辑

登录成功以后,可以看到只有一个欢迎页面,这是因为我们还没有在微服务中配置 Sentinel 的信息.

SpringCloud-Sentinel解决雪崩问题_微服务_08

编辑

1.5 微服务项目整合 Sentinel

1.5.1、引入微服务工程

项目结构如下:

  1. gateway:网关.
  2. user-service:用户服务(涉及到用户表),包含用户的 CRUD.
  3. order-service:订单服务(涉及到订单表),查询订单 的时候会远程调用 用户服务 ,将订单和用户信息一起展示出来.
  4. feign-api:用户服务对外暴露 feign 客户端、实体类.

1.5.2 整合 Sentinel

在 order-service 中整合 Sentinel,并且连接 Sentinel 控制台,步骤如下.

a)在 order-service 中引入 sentinel 依赖

<!--sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId> 
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

b)配置控制台地址

spring:
  cloud: 
    sentinel:
      transport:
        dashboard: localhost:8080

c)访问 order-service 服务的任意端点,触发 sentinel 监控.

启动微服务前,记得启动 nacos 和 sentinel.

SpringCloud-Sentinel解决雪崩问题_微服务_09

编辑