目录
1.雪崩问题
1.1.雪崩问题产生原因:
比如查询购物车的业务,假如商品服务业务并发较高,占用过多Tomcat连接。可能会导致商品服务的所有接口响应时间增加,延迟变高,甚至是长时间阻塞直至查询失败。
此时查询购物车业务需要查询并等待商品查询结果,从而导致查询购物车列表业务的响应时间也变长,甚至也阻塞直至无法访问。而此时如果查询购物车的请求较多,可能导致购物车服务的Tomcat连接占用较多,所有接口的响应时间都会增加,整个服务性能很差, 甚至不可用。
依次类推,整个微服务群中与购物车服务、商品服务等有调用关系的服务可能都会出现问题,最终导致整个集群不可用。
这就产生了级联失败问题,也称雪崩问题
言简意赅的说:雪崩问题产生的原因是什么?
微服务相互调用,服务提供者出现故障或阻塞。
微服务调用者没有做好异常处理,导致自身故障。
调用链中的所有服务级联失败,导致整个集群故障。
那么我们解决问题的思路有哪些?
尽量避免服务出现故障或阻塞:
**- 保持代码的健壮性;
- 保持网络畅通;
- 能应对较多的并发请求;**
服务调用者做好远程调用异常的后备方案,避免故障扩散
业务健壮性问题:
比如在查询购物车列表业务中,购物车服务需要查询最新的商品信息,与购物车数据做对比,提醒用户。大家设想一下,如果商品服务查询时发生故障,查询购物车列表在调用商品服 务时,是不是也会异常?从而导致购物车查询失败。但从业务角度来说,为了提升用户体验,即便是商品查询失败,购物车列表也应该正确展示出来,哪怕是不包含最新的商品信息。
1.2.雪崩问题解决方案
微服务保护的方案有很多,比如:
**- 请求限流:**限制流量在服务可以处理的范围内,避免因突发流量而故障。
**- 线程隔离:**控制业务可用的线程数量,将故障隔离在一定的范围内。
**- 服务熔断:**将异常比例接口过高的接口断开,拒绝所有请求,直接fallback。
这些方案或多或少都会导致服务的体验上略有下降,比如请求限流,降低了并发上限;线程隔离,降低了可用资源数量;服务熔断,降低了服务的完整度,部分服务变的不可用或弱可用 。因此这些方案都属于服务降级的方案。但通过这些方案,服务的健壮性得到了提升。
1.2.1.请求限流
服务故障最重要原因,就是并发太高 !解决了这个问题,就能避免大部分故障。当然,接口的并发不是一直很高,而是突发的。因此请求限流就是限制或控制接口访问的并发流量,避免服务因流量激增而出现故障。
请求限流往往会有一个限流器 ,数量高低起伏的并发请求曲线,经过限流器就变的非常平稳。这就像是水电站的大坝,起到蓄水的作用,可以通过开关控制水流出的大小,让下游水流始终维持在一个平稳的量。
1.1.2.线程隔离
当一个业务接口响应时间长,而且并发高时,就可能耗尽服务器的线程资源,导致服务内的其它接口受到影响。所以我们必须把这种影响降低,或者缩减影响的范围。线程隔离正是解决这个问题的好办法。
线程隔离的思想来自轮船的舱壁模式 :船舱都会被隔板分离为多个独立空间,当船体破损时,海水只会进入部分空间,将故障控制在一定范围内,避免整个船体都被淹没。
为了避免某个接口故障或压力过大导致整个服务不可用,我们可以限定每个接口可以使用的资源范围,也就是将其"隔离"起来。
如图所示,我们给查询购物车业务限定可用线程数量上限为20,这样即便查询购物车的请求因为查询商品服务而出现故障,也不会导致服务器的线程资源被耗尽,不会影响到其它接口。
1.1.3.服务熔断
线程隔离虽然避免了雪崩问题,但故障服务(商品服务)依然会拖慢购物车服务(服务调用方)的接口响应速度。而且商品查询的故障依然会导致查询购物车功能出现故障,购物车业务也变的不可用了。
所以,我们要做两件事情:
- 编写服务降级逻辑: 就是服务调用失败后的处理逻辑,根据业务场景,可以抛出异常,也可以返回友好提示或默认数据。
- 异常统计和熔断: 统计服务提供方的异常比例,当比例过高表明该接口会影响到其它服务,应该拒绝调用该接口,而是直接走降级逻辑。
1.3.微服务保护技术对比
在SpringCloud中支持多种服务保护技术:
目前比较流行的是阿里巴巴的Sentinel框架。
1.4.Sentinel
1.4.1.介绍与安装
Sentinel 的使用可以分为两个部分:
- 核心库(Jar包): 不依赖任何框架/库,能够运行于 Java 8 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。在项目中引入依赖即可实现服务限流、隔离、熔断等功能。
**- 控制台(Dashboard):**Dashboard 主要负责管理推送规则、监控、管理机器信息等。
为了方便监控微服务,我们先把Sentinel的控制台搭建出来。
1)下载jar包
下载地址:Releases · alibaba/Sentinel · GitHub
2)运行
将jar包放在任意非中文、不包含特殊字符的目录下,重命名为sentinel-dashboard.jar
:
然后打开在jar包目录下打开cmd窗口,运行如下命令启动控制台:
java -Dserver.port=8090 -Dcsp.sentinel.dashboard.server=localhost:8090 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
效果如下:
3)访问
访问http://localhost:8090页面,就可以看到sentinel的控制台了:
需要输入账号和密码,默认都是:sentinel
登录后,即可看到控制台,默认会监控sentinel-dashboard服务本身:
1.4.2.微服务整合
我们在cart-service
模块中整合sentinel,连接sentinel-dashboard
控制台,步骤如下:
1)引入sentinel依赖
<!--sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
2)配置控制台
修改application.yaml文件,添加下面内容:
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090
3)访问cart-service
的任意端点
重启cart-service
,然后访问查询购物车接口,sentinel的客户端就会将服务访问的信息提交到sentinel-dashboard
控制台。并展示出统计信息:
点击簇点链路菜单,会看到下面的页面:
所谓簇点链路,就是单机调用链路 ,是一次请求进入服务后经过的每一个被Sentinel
监控的资源。默认情况下,Sentinel
会监控SpringMVC
的每一个Endpoint
(接口)。
因此,我们看到/carts
这个接口路径就是其中一个簇点,我们可以对其进行限流、熔断、隔离等保护措施。
不过,需要注意的是,我们的SpringMVC接口是按照Restful风格设计,因此购物车的查询、删除、修改等接口全部都是/carts
路径:
默认情况下Sentinel会把路径作为簇点资源的名称,无法区分路径相同但请求方式不同的接口,查询、删除、修改等都被识别为一个簇点资源 ,这显然是不合适的。
所以我们可以选择打开Sentinel的请求方式前缀,把**请求方式 + 请求路径
** 作为簇点资源名:
首先,在cart-service
的application.yml
中添加下面的配置:
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8090
http-method-specify: true # 开启请求方式前缀
然后,重启服务,通过页面访问购物车的相关接口,可以看到sentinel控制台的簇点链路发生了变化: