Sentinel 微服务保护

关于服务雪崩的问题

服务雪崩指的是微服务调用链路中的某个服务发生故障,所引起的上层服务全部出现错误的情况。

如下图所示:

当服务A可能调用到服务D,而服务D却发生故障时,服务A的依然不断地向服务D发送请求,但服务D并没有响应,此时的服务A会不停地累积请求,直致服务A的资源耗尽,这时即使希望想访问服务B也无法访问了。

而服务A只是整个服务中的其中一个下游服务节点,会因服务D的故障,使得所有上游服务全部发生故障,这就是服务雪崩。

 

解决服务雪崩

通常我们有4种方案解决服务雪崩的问题:

1.超时处理

  • 设定请求的超时时间,当响应时间低于设定时间后,服务A会认为服务D已经出现故障,从而释放资源。
  • 但是超时处理在高并发上作用不明显,只能缓解资源的耗尽速度,因为一旦请求的并发数量高于超时速度时,雪崩也只是时间的问题

2.舱壁模式

  • 限定每个业务能使用的线程数,避免耗尽整个tomcat的资源,也叫线程隔离,如下图中的概念
  • 就像轮船上的船壁,即使船底发生破裂,也只会影响部分船底,而不至于整个船底都发生渗水。
  • 在服务中我们可以事先设置好每一个服务请求的最多线程数,这样服务C发生故障时,也不会影响服务B的使用,但缺点是,即使知道服务C已故障,但依然分配线程给C进行不断访问,这是一种资源的浪费。

 

3.熔断降级

  • 由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求。
  • 我们可以设定一个异常比例,若服务的请求失败次数超过一定比例后,直接对该服务进行熔断,所有前来访问的所有请求直接拒绝。

 

4.流量控制(预防性措施)

  • 当服务仅能处理一定量的请求时,我们可以利用Sentinel控制请求的流量,使得服务不会在瞬间超出了可处理的请求能力范围值,预防服务在出现瞬间服务过多所引起的服务崩溃问题。

 

Sentinel 安装与引入

Sentinel 安装

可以到以下地址下载最新版本的Sentinel 进行安装

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

它是一个SpringBoot的工程,可以通过 java -jar Sentinel.jar 命令运行Sentinel

用户名和密码默认为 sentinel 可以使用自定义配置对这些进行配置,参考以下网址:

https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0

 

引入 Sentinel

Sentinel 运行后,默认后台是什么都没有的,这是因为我们还没有把Sentinel引入到项目中,这样Sentinel就无法对我们项目中的请求做限流操作,所以我们需要再引入Sentinel的包,让它监控我们的项目。

本地下载地址:

https://www.tzming.com/wp-content/uploads/filedown/sentinel-dashboard-1.8.6.jar

1.引入依赖

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

 

2.配置参数

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

 

3.Sentinel 会自动对每一个请求方法(端点)做监听,当有请求时,Sentinel就会被触发

 

Sentinel 流控使用

流控规则

我们认为,每一个请求url都是一个资源或端点,我们点开一个资源的【流控】按钮

QPS 是控制在相同时间内的异步请求数量,Sentinel支持QPS限制和线程数限制。

单机阈值指的是限制的数量,若请求数超出这个值,则多出来的请求会被拒绝,阈值应当在压测之后得出实服务器实际能承受的上限值作为阈值。

 

流控模式-关联

流控模式-关联是指当关联资源的请求发生超过阈值时,被设置的资源将被拒绝,如上图中的 /read 请求设置了关联资源 /write

当 /write 比较繁忙时, /read 就会被拒绝请求,这种需要多出现在数据库的写与读的关系,当数据库的写相对繁忙时,应该优先给写资源,以免读在正在写中的行,而影响数据的真实性

 

流控模式-链路

流控模式-链路 是指,对请求的路径进行筛选,当某一个service中的业务被多个请求所调用时,我只希望对某个请求调用的service业务进行限制,其它的调用不限制,如下图

 

图中的 /common 是被调用的service业务

入口资源则是 调用 service 业务的请求

设置后,只有通过请求 /test2 方法所调用的 /common 才会被限制。

 

链路生效步骤

链路是会被包含到方法中调用的外部方法,比如:

controller 请求方法 a() 会调用 service 业务方法 b()

1.但是链路模式不单止会监控 a() 而且还需要监控 b() ,所以 b() 方法中必须加入注解【@SentinelResource】来标记该方法是需要给Sentinel监控的资源。

2.Sentinel 默认会把 ServletContext 作为请求的根路径,其它controller 方法都是 ServletContext 下的子路径,我们可以看回上面章节的图

忽略红色框,可以看到,所有请求节点中,根链路是【sentinel_spring_web_context】,这使得我们的请求url变成了子链路,所以我们需要做一个配置,关闭统一根链路,而是由我们的请求方法作为根链路,这样根链路下调用的 service 业务方法才得以监控得到:

spring:
  cloud:
    sentinel:
      web-context-unify: false # 关闭context整合

 

Sentinel 流控效果

快速失败

快速失败是一个比较粗暴的效果,即当QPS达到了峰值后,被拒绝的请求直接报出请求失败的异常。无任何缓解措施

 

warm up

预热模式,我们可以设定最大的QPS请求峰值,最小的QPS由Sentinel决定,默认为3。预热时间为5秒

模拟效果是:

1.当某一瞬间发来10个请求时,QPS在3处,此时10个请求,只有3个能通过,7个会直接拒绝

2.当下一瞬间依然发来10个请求时,QPS开始从3个慢慢增加到10个,用时为5秒,当QPS增加到5时,10个请求中5个能通过,5个直接拒绝

3.当5秒过后,QPS已从3增加到10个,此时发来10个请求时,10个请求都能通过。

 

排队等待

当请求数超过设定的QPS峰值时,不会马上拒绝请求,而是这些请求进行排队执行,同时设置超时时间5秒

模拟效果是:

1.当某一瞬间发来10个请求时,若设置QPS为5时,那么这一瞬间,有5个请求被处理,有5个请求等待中

2.若上一瞬间的5个请求还没处理时,下一瞬间又发来10个请求,此时,上一瞬间的5个等待请求被执行,当前瞬间的10个请求等待中。

3.若如此以往,等待的请求会越来越多,后面发来的请求可能会出现等待时间越来越长的情况,若后来的请求等待时间超过5秒(用户设置的),这些等待的请求直接拒绝。

 

热点参数限流

热点参数限流是一种更细粒度的限流方式,是指分别统计参数值相同的请求,判断是否超过QPS阈值。

比如某请求 /goods/{id},当出现多个请求不同参数时

/goods/1

/goods/1

/goods/1

/goods/2

那么 id=1 的有3个,id=2 的有1个。

我们就可以分别能过限流 id=1 的QPS和 id=2 的QPS阈值了。

我们可以在【热点限流】中添加对请求的热点限流:

参数索引:指的是请求方法中的接收参数的位置,从0开始

单机阈值:默认的请求阈值,即如果不另外设置的话,就使用这个阈值

 

让请求生效热点规则

要让请求方法生效热点规则,需要在请求方法前加上注册【@SentinelResource("")】

    @SentinelResource("hot") // 传入设定好的热点限流规则名称
    @GetMapping("/{id}")
    public String test1(@PathVariable("id") Long id){
        return "hhhh";
    }

 

隔离和降级

FeignClient整合Sentinel

在微服务中,我们的服务与服务之间的通信都是使用Fiegn进行的,那么Fiegn是否请求成功,我们是无法知道的,因此我们可以把Fiegn整合到Sentinel中,让Sentinel发现Figen的操作。

实现步骤:

1.Fiegn中配置开启Sentinel

feign:
  sentinel:
    enabled: true # 开启Feign的Sentinel功能

2.给FiegnClient中的代码加入请求失败后的降级逻辑

在SpringCloud的基本章节中,我们使用了Fiegn利用接口来实现请求方法,那么如果这些请求出现错误时,我们可以使用以下两种方法,对请求失败后的兜底操作,如把通用的信息返回给用户不至于直接报错。

  • 方式一:FallbackClass,这种方法无法对远程调用的异常做处理
  • 方式二:FallbackFactory,可以对远程调用的异常做处理,推荐使用。

我们通过编写一个 FallbackFactory 接口的实现,来对Fiegn请求失败时的执行方法:

原有的UserClient请求接口:

@FeignClient(value = "userservice",fallbackFactory = UserFallBackFactory.class)
public interface UserClient {

    @GetMapping("/user/{id}")
    String getUserInfo(@PathVariable Long id);
}

用于兜底UserClient报错的方法:

public class UserFallBackFactory implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable cause) {
        return new UserClient() {
            @Override
            public String getUserInfo(Long id) {
                return "查询用户失败,这里是备用请求方案";
            }
        };
    }
}

然后再在Config中注册这个类为一个Bean即可。

@Configuration
public class FiegnConfig {

    @Bean
    public UserFallBackFactory userFallBackFactory(){
        return new UserFallBackFactory();
    }

}

然后在FiegnClient接口中的@FeignClient注解上加上 fallbackFactory 的值,值为这个兜底类的字节码文件,如上面步骤1中的代码。

 

线程池隔离(仓壁模式)

从上面节章我们知道,线程池模式就像船底的仓壁,每个服务都分配访问线程数,即使某些服务出现问题,也不会影响其它服务的访问

 

 

而FeignClient中支持使用线程池隔离的方式

线程数:是该资源能使用用的tomcat线程数的最大值。也就是通过限制线程数量,实现舱壁模式。

 

熔断降级

熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。既拦截访问该服务的一切请求,而当服务恢复时,断路器会放行访问该服务的请求。

断路器包含三种状态,分别为 关闭断路器:Closed、打开断路器:Open、半打开状态:Half-Open

关闭断路器:Closed -> 断路器关闭,任何请求都会被处理。

打开断路器:Open -> 断路器开启,任何请求都会被直接拒绝。

半打开状态:Half-Open -> 断路器开启时长到期,进行半开状态检测服务是否请求成功,如果成功,则改为Closed,如果依然请求失败,则改为Open

我们可以在簇点链路列表中的熔断按钮进行设置

 

熔断策略:慢调用

慢调用指的是判断服务调用时所耗的时长,比如有一些服务可能出现了问题,调用所需耗时比较大,这种称为慢调用

最大RT : 处理整个请求所需的耗时阈值,如果超出这个阈值则会触发熔断规则。

比例阈值 : 在多个请求中,超出最大RT的请求占总请求数的比例,如果超过了,则会触发熔断规则。

熔断时长 : 熔断并非一直开启,而是暂时开启,所设定的时长即是熔断后开启的时长。

统计时长与最小请求数 : 当熔断进入半开状态时,断路器会在1000毫秒内开放最少5个请求数,如果统计后的数据依然符合熔断规则,则断路器继续开启熔断,否则关闭熔断机制。

 

熔断策略:异常比例

异常比例是指,以抛出异常为统计点的熔断规则,当请求中发生异常抛出的数量在总体请求中的比例达到设定阈值时,断路器开启熔断。

 

熔断策略:异常数

异常数是指设定具体数量的异常抛出值,当请求中抛出的异常超过了预设定好的异常数后,断路器开启熔断。

 

 

授权规则

 

授权规则

授权规则可以对调用方的来源做控制,有白名单和黑名单两种方式

白名单

白名单即仅允许指定 origin 来源的请求

资源名 : 被保护的请求,只有允许的来源才可能访问。

流控应用 : 流控应用不是服务名,而是由我们自定义的Origin

1.通过实现接口 RequestOriginParser 来定义来源:

@Component
public class OriginParser implements RequestOriginParser {

    /**
     * 在这里返回一个 Origin 名称
     * @param httpServletRequest
     * @return
     */
    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        // 自定义设置一个origin规则,比如我自己定义header中带有origin的才允许
        String origin = httpServletRequest.getHeader("origin");

        // 如果请求中不带有 origin 的值,则不允许访问
        if (StringUtils.isEmpty(origin)){
            return "blank";
        }
        
        // 假如header中的origin=gatway,那么就只授权origin为getway的规则
        return origin;
    }
}

 其中 返回的 origin 的值则是上面的流控应用的名称

当我们使用的是统一网关的转发请求的话,我们就可以使用网关的过滤器功能,往header中加入origin的头信息再转发到服务中请求了,关于网关可查看微服务基础。

 

黑名单

黑名单即除指定 origin 来源以外的请求都允许

 

自定义异常结果

我们以上章节中,所有的设定限流后,常发生拒绝后,都是直接返回一串文字

Blocked by Sentinel (flow limiting)

相对来说不太友好

我们可以通过自定义异常的结果返回,通过实现接口 BlockExceptionHandler  的方法来实现自定义异常的结果

@Component
public class ExceptionResult implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws Exception {
        
    }
}

当处理请求被限流、降级、授权拦截时抛出的异常:BlockException

我们可以通过 BlockException 来判断是什么类型的异常,通下异常都为 BlockException的子类

异常 说明
FlowException 限流异常
ParamFlowException 热点参数限流的异常
DegradeException 降级异常
AuthorityException 授权规则异常
SystemBlockException 系统规则异常

举个例子:

@Component
public class ExceptionResult implements BlockExceptionHandler {
    @Override
    public void handle(
            HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse, BlockException e) throws Exception {
        String msg = "未知异常";
        int status = 429;
        if (e instanceof FlowException) {
            msg = "请求被限流了!";
        } else if (e instanceof DegradeException) {
            msg = "请求被降级了!";
        } else if (e instanceof ParamFlowException) {
            msg = "热点参数限流!";
        } else if (e instanceof AuthorityException) {
            msg = "请求没有权限!";
            status = 401;
        }
        httpServletResponse.setContentType("application/json;charset=utf-8");
        httpServletResponse.setStatus(status);
        httpServletResponse.getWriter().println("{\"message\": \"" + msg + "\", \"status\": " + status + "}");
    }
}

 

 

规则持久化

规则管理模式

Sentinel的控制台规则管理有三种模式

原始模式:默认模式,将规则保存在内存中,重启服务后就会丢失

 

Pull 模式 : 控制台将配置的规则推送到Sentinel客户端,而客户端会将配置规则保存在本地文件或数据库中。以后会定时去本地文件或数据库中查询,更新本地规则。

Push 模式 : 控制台将配置规则推送到远程配置中心,例如Nacos. Sentinel客户端监听Nacos,获取配置变更的推送消息,完成本地配置更新。

Nacos 中拉取配置

Sentinel 中的配置可以通过预先在 Nacos 中配置好配置项,然后当带有Sentinel服务启动后,再向Nacos中拉取限流规则,并以yaml配置形式配置到Sentinel中。缺点是Sentinel UI界面上修改的规则不能同步到Nacos中,同时也属于临时规则,只要Sentinel服务重启了,规则也自然没了。

步骤:

1.在微服务中引入依赖坐标(数据源坐标)

        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>

 

2.在微服务中配置Sentinel数据源,以访问Nacos拉取规则

  cloud:
    sentinel:
      enabled: true
      transport:
        dashboard: localhost:8090
      http-method-specify: true # 开启请求方式前缀显示,否则Sentinel中不能区分是post还是get
      datasource:
        ds:   # 自定义的数据源名称(唯一名称)
          nacos:  # 定义是从Nacos获取
            server-addr: 192.168.101.150:8848  # Nacos 地址
            groupId: DEFAULT_GROUP
            data-type: json
            data-id: degrade.json # Nacos中定义规则的文件 Id
            rule-type: degrade # 定义这个规则用于 Sentinel 限流类型(如用于熔断,还是限流,还是降级等其它功能) 

 

3.在Nacos中创建一个配置文件,如上面配置的文件Id为 degrade.json,则我们在Nacos中定义的文件Id也是 degrade.json

对于Sentinel 而言,慢调用降级限流 degrade 包含以下几个规则数据

  • 最大RT: 调用API时响应的时间大于最大RT,则认为该API出现异常
  • 熔断时长:对于被熔断降级的API,需要隔一段时间去尝试调用,看看是否恢复了,这个时间间隔则是熔断时长
  • 比例阈值:值在0.0~1.0之间,当阈值为0.5时,假如有10个调用中有5个以上出现超过最大RT值,则认为应该熔断处理
  • 最小请求数:即放出多少个测试用的请求来判断API是否恢复,即上面说的10个调用
  • 统计时长:在设定的时间内,吸收判断用的请求数。

 

在代码中表示degrade数值的是 DegradeRule 对象,在Nacos中配置规则时,应当按照以下方式进行配置

  • grade:熔断类型(慢调用,异常比例,异常数)
  • count:慢调用是,它表示 最大RT,在异常比例下,它表示异常数量占最小请求数比,在异常数下,它表示异常数据量
  • timeWindow:表示熔断时长
  • slowRatioThreshold:表示比例阈值
  • minRequestAmount:表示最小请求数,默认为5
  • statIntervalMs: 表示统计时长

最后在Nacos中配置的json规则如下:

[
    {
        "resource": "GET:http://xxx-service/xx",
        "count" : 200.0,
        "grade": 0,
        "slowRatioThreshold": 0.5,
        "timeWindow": 10
    }
]

 

至此,当我们的Sentinel服务启动后,就会自动向Nacos拉取规则了。

有关其它规则的配置信息,可以查看 com.alibaba.cloud.sentinel.datasource.config.DataSourcePropertiesConfiguration 类,里面就是包含了所有动态配置规则的规则,datasource 配置 只是一个 Map<String, DataSourcePropertiesConfiguration> 的变量保存规则。

 

如果您喜欢本站,点击这儿不花一分钱捐赠本站

这些信息可能会帮助到你: 下载帮助 | 报毒说明 | 进站必看

修改版本安卓软件,加群提示为修改者自留,非本站信息,注意鉴别

THE END
分享
二维码
打赏
海报
Sentinel 微服务保护
关于服务雪崩的问题 服务雪崩指的是微服务调用链路中的某个服务发生故障,所引起的上层服务全部出现错误的情况。 如下图所示: 当服务A可能调用到服务D,而服务D却发生故障时,服务A的依然不……
<<上一篇
下一篇>>