微服务保护(Sentinel,请求限流,线程隔离,降级策略,熔断处理)

雪崩问题:

微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。

就拿这张图的流程举例子:

购物车服务需要查询最新的商品信息,与购物车数据做对比,提醒用户。大家设想一下,如果商品服务查询时发生故障,查询购物车列表在调用商品服 务时,是不是也会异常?从而导致购物车查询失败。但从业务角度来说,为了提升用户体验,即便是商品查询失败,购物车列表也应该正确展示出来,哪怕是不包含最新的商品信息。

我们可以再稍微了解底层一点,

就是购物车服务是部署在一个web服务器上,那一个服务器都会有一定的线程资源,

每个工作线程处理一个请求,如果商品服务被堵塞了,响应时间很久,购物车服务需要查询商品这条链路的线程就会被一直停滞,因为你商品服务处理不了这么多的请求,然后呢,那请求就会一直

堆积在购物车服务,并且不断抢占线程,这就会导致购物车服务的其它链路会遭到堵塞。

这就不太好,我们就需要对微服务的模块进行保护。

Sentinel:

微服务保护的技术有很多,但在目前国内使用较多的还是Sentinel,所以接下来我们学习Sentinel的使用。

https://sentinelguard.io/zh-cn/

安装:

https://github.com/alibaba/Sentinel/releases

运行:

java -Dserver.port=8091 -Dcsp.sentinel.dashboard.server=localhost:8091 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

 需要输入账号和密码,默认都是:sentinel

登录后,即可看到控制台,默认会监控sentinel-dashboard服务本身:

现在这个时候是只有sentinel-dashboard自己的一个记录

在项目中整合sentinel:

导入依赖:
<!--sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId> 
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

我们这里就用购物车服务来举例:

修改配置文件信息:
spring:
  cloud: 
    sentinel:
      transport:
        dashboard: localhost:8091

项目启动也不会有记录,只有中的方法被调用了才会有记录

所谓簇点链路,就是单机调用链路,是一次请求进入服务后经过的每一个被Sentinel监控的资源。默认情况下,Sentinel会监控SpringMVC的每一个Endpoint(接口)。

因此,我们看到/carts这个接口路径就是其中一个簇点,我们可以对其进行限流、熔断、隔离等保护措施。

不过,需要注意的是,我们的SpringMVC接口是按照Restful风格设计,因此购物车的查询、删除、修改等接口全部都是/carts路径:

默认情况下Sentinel会把路径作为簇点资源的名称,无法区分路径相同但请求方式不同的接口,查询、删除、修改等都被识别为一个簇点资源,这显然是不合适的。

所以我们可以选择打开Sentinel的请求方式前缀,把请求方式 + 请求路径作为簇点资源名:

首先,在cart-serviceapplication.yml中添加下面的配置:

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8091
      http-method-specify: true

然后,重启服务,通过页面访问购物车的相关接口,可以看到sentinel控制台的簇点链路发生了变化:

就可以看到上面的界面展示了。

有了这个控制台,我们所有的操作直接就可以在这个控制台中操作就行

请求限流

qps的概念:

一秒内可以处理的请求数量称之为服务器的QPS。

简单理解为就是一个对高并发的数量描述

我们这里给这个单击阈值设置为6,

什么意思呢

就是每秒钟最后访问我这个浏览器的请求数量只能右6个

多了我直接拒绝

效果展示:

为了展示效果用到了Jmeter工具对网站来发送请求

总共发1000次请求,在100秒内发完,所以我们可以得出每秒就是发10次

我们在这个cart-service服务中设置好,每秒最多请求6个请求,根据我们上面每秒发10个请求,这样看来的话就会有2/5的请求被拦截

直接右键启动:

 我们可以从这个控制台中看到,就是我们释放的比例

通过这个方式可以对请求进行一个限流。

线程隔离

限流可以降低服务器压力,尽量减少因并发流量引起的服务故障的概率,但并不能完全避免服务故障。一旦某个服务出现故障,我们必须隔离对这个服务的调用,避免发生雪崩。

比如,查询购物车的时候需要查询商品,为了避免因商品服务出现故障导致购物车服务级联失败,我们可以把购物车业务中查询商品的部分隔离起来,限制可用的线程资源:

什么意思呢,就是说假设我们这个tomcat服务器只有10个线程,我们有两个方法,一个请求商品服务,一个请求其它服务,

我们现在给这个商品服务设置这个方法的最多可用的线程资源,比如说为5,

那这样就算商品服务发生了堵塞,那最多也就消耗五个,可以不至于一个方法把所有tomcat服务器的线程占满。

说起来满好理解:

具体的步骤还是有很多注意点的:

具体步骤:

1:OpenFeign整合Sentinel

我们为什么要OpenFeign整合Sentinel,首先我们想我们要调用商品服务,那我们需要给我们整个cart所有方法都设置嘛

只需要设置openfeign远程调用的那个方法即可。

所以我们需要在配置文件中设置一下:

feign:
  sentinel:
    enabled: true # 开启feign对sentinel的支持
需要注意的是,

默认情况下SpringBoot项目的tomcat最大线程数是200,允许的最大连接是8492,单机测试很难打满。

所以我们需要配置一下cart-service模块的application.yml文件,修改tomcat连接:

server:
  port: 8087
  tomcat:
    threads:
      max: 25
    accept-count: 25
    max-connections: 100
2:我们具体配置方法占用的最大线程数:

比如说我们这里设置一个10,注意这里的10是线程是

最多我们能给这个方法10个线程来处理,如果查询商品的接口每秒处理2个请求(这个为什么是两个,是黑马的老师说的),我们就先当两个来算

那有10个线程的话,就是一秒钟最多处理20个请求,我们还是用Jmeter来测试

这一次用5000个请求在50秒内发完。所以每秒就是100个

看看效果:

我们可以看到 GET:http://item-service/items这个方法,最多处理20个请求每秒

但是我们反观 GET:/carts,却丝毫不受影响。

这就可以起到一个线程隔离的作用。

就算微服务中有一个模块挂了,也不影响其它模块运行

降级策略:

和API网关的对比:

首先我昨天学这个降级策略的时候,我的第一反应就想到了API网关里面也有一个降级策略

我就去询问了一下GPT

大概的意思就是:

网关是一个在整个微服务项目前面的一个关口,虽然我们可以设置降级策略,但是我们只能指定一个路径,如果你需要访问的服务挂了,你就去访问我给你指定好的服务

是一个大的方向上的降级

而这里要讲的降级是这一个方法挂了,比如我要请求商品服务,商品服务挂了,那我降级去请求另一个服务,这里的降级策略更细致

GPT好像还讲了一个什么粒度,理解起来就是上面这个意思

具体步骤:

降级逻辑需要编写代码了:

1:在hm-api模块中给ItemClient定义降级处理类,实现FallbackFactory这个降级工厂:
@Slf4j
public class FallbackClient implements FallbackFactory<ItemClient> {
    @Override
    public ItemClient create(Throwable cause) {
        return new ItemClient() {
            @Override
            public List<ItemDTO> queryItemByIds(Collection<Long> ids) {
                log.info("查询商品失败"+cause);
                return new ArrayList<>();
            }

            @Override
            public void deductStock(List<OrderDetailDTO> items) {
                log.info("减少库存失败"+cause);
                throw new RuntimeException(cause);
            }
        };
    }
}

在这个工厂中,我们需要给这两个方法都提供降级逻辑

2:在hm-api模块中的com.hmall.api.config.DefaultFeignConfig类中将ItemClientFallback注册为一个Bean

我这里写了一个config包。

3:在hm-api模块中的ItemClient接口中使用ItemClientFallbackFactory

测试结果:

我们还是要先给这方法设置一个qps的阈值,要先让它会有失败的请求

这里我傻了

我写笔记的时候犯了个蠢

我们能通过设置这个qps阈值来模拟这个降级策略嘛

显然不能,为什么,如果设置了qps,那那些请求都进不去,就发送失败了直接

我们的需求是什么?

是让已经接收的请求,没有线程来处理你,让你去走降级逻辑。

我们可以看大这里所有的请求都是成功了的

这里的异常率也是0

说明就算有请求没有成功,但是也是走了我们的降级策略。

服务熔断:

查询商品的RT较高(模拟的500ms),从而导致查询购物车的RT也变的很长。这样不仅拖慢了购物车服务,消耗了购物车服务的更多资源,而且用户体验也很差。

对于商品服务这种不太健康的接口,我们应该停止调用,直接走降级逻辑,避免影响到当前服务。也就是将商品查询接口熔断。当商品服务接口恢复正常后,再允许调用。这其实就是断路器的工作模式了。

Sentinel中的断路器不仅可以统计某个接口的慢请求比例,还可以统计异常请求比例。当这些比例超出阈值时,就会熔断该接口,即拦截访问该接口的一切请求,降级处理;当该接口恢复正常时,再放行对于该接口的请求。

在讲服务熔断之前,我们需要先来看一下这个断路器的工作状态

断路器的工作状态切换有一个状态机来控制:

 仔细来说一下这个流程

首先这个熔断器是需要去监听一个阈值的,我们这里就设置成这个响应时间(超过了200ms就触发),一开始是一个closed的状态

然后呢,超过了我们指定的阈值,就会进入一个open状态,那就会暂时的熔断这个接口的请求

进入一个open状态

但是呢,我肯定不会一直熔断,我会设置一个熔断时间,等这个时间一过,我就会再发一次请求

发现如果这个请求可以,那行,我就关闭熔断,发现还是不行,我就继续熔断。

整体过程就是上面这样

这个最大RT就是上面说的阈值

这里还有一个比例阈值,我设置成了0.5

这个的意思就是在我的统计时长1s中之内,有一半的请求超过了,我就开始熔断

测试结果:

我们会发现触发熔断之后,那个请求就不让过了

不过突然有一个通过了的请求,

那个就是熔断时间到了,再去访问,发现时间还是不行,又再次熔断

  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值