【Sentinel】简单入门以及整合SpringBoot、OpenFeign和RestTemplate

1. 分布式系统会遇到的问题

在分布式系统中,由于网络原因或自身的原因,服务一般无法保证 100% 可用。如果一个服务出现了问题,调用这个服务就会出现线程阻塞的情况,此时若有大量的请求涌入,就会出现多条线程阻塞等待,进而导致服务瘫痪。

由于服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的 “雪崩效应” 。

在这里插入图片描述

雪崩发生的原因多种多样,最常见原因: 程序Bug,大流量请求,硬件故障,缓存击穿

  • 程序Bug: 比说我们以前我们支付模块增加一个功能,我们监听一个Rocketmq消息然后进行业务的处理,至于什么业务处理我们就不说了,上线后就报警了,我们的支付成功的订单直线下降,这是什么原因,我们查看日志有很多消费消息的日志,一直在打,我们马上回滚,然后排查问题,我们发现线上的功能消费设置的是CONSUME_FROM_FIRST_OFFSET 这个是一个新的订阅组第一次启动从队列的最前位置开始消费,后续再启动接着上次消费的进度开始消费,而我们监听的消息是已经存在的,所以里面存在了很多的消息,为此消费端一直消费对应的消息,导致一直运行,从而影响了支付的功能。我们改为CONSUME_FROM_LAST_OFFSET【一个新的订阅组第一次启动从队列的最后位置开始消费后续再启动接着上次消费的进度开始消费】。
  • 大流量请求:在秒杀和大促开始前,如果准备不充分,瞬间大量请求会造成服务提供者的不可用。
  • 硬件故障:可能为硬件损坏造成的服务器主机宕机, 网络硬件故障造成的服务提供者的不可访问。
  • 缓存击穿:一般发生在缓存应用重启, 缓存失效时高并发,所有缓存被清空时,以及短时间内大量缓存失效时。大量的缓存不命中, 使请求直击后端,造成服务提供者超负荷运行,引起服务不可用。

我们无法完全杜绝雪崩源头的发生,只有做好足够的容错,保证在一个服务发生问 题,不会影响到其它服务的正常运行。也就是"雪落而不雪崩"。

2. 常见的容错办法

要防止雪崩的扩散,我们就要做好服务的容错,容错说白了就是保护自己不被猪队友拖垮的一些措施, 下面介绍常见的服务容错思路和组件

2.1 常见的思路

常见的容错思路有隔离、超时、限流、熔断、降级这几种,下面分别介绍一下。

2.1.1 隔离

大的方向我们可以进行物理隔离,比如说我们可以把用户实例分为好几个组,一个组为4台为核心服务提供,另一组是2台为非核心的组提供服务,这样就进行了物理隔离,如果我们进入某个实例内我们想,比如我们的RPC调用我们调用的请求都会先进入一个队列里面然后再消费,那一个出了问题,也会影响其他的服务调用,那我们为每个服务的调用都会创建一个队列,这样进行了队列隔离,同时我们我在后面设置一个单独的线程池进行线程池隔离,但是如果我们后面访问的下游如果没有隔离会有什么问题,是不是还会出现问题,比如我们后面进行访问的时候都是一个数据库。

在这里插入图片描述
在这里插入图片描述

2.1.2 超时

在上游服务调用下游服务的时候,上游服务设置一个最大响应时间,如果超过这个时间,下游未作出反应,上游服务就断开请求,释放掉线程。

2.1.3 限流

限制请求核心服务提供者的流量,使大流量拦截在核心服务之外,这样可以更好的保证核心服务提供者不出问题,对于一些出问题的服务可以限制流量访问。

  • 计数器固定窗口算法
  • 计数器滑动窗口算法
  • 漏桶算法
  • 令牌桶算法

2.1.4 熔断

在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。

这种牺牲局部,保全整体的措施就叫做熔断。

服务熔断一般有三种状态:

  • 熔断关闭状态(Closed)

    服务没有故障时,熔断器所处的状态,对调用方的调用不做任何限制。

  • 熔断开启状态(Open)

    后续对该服务接口的调用不再经过网络,直接执行本地的fallback方法。

  • 半熔断状态(Half-Open)

    尝试恢复服务调用,允许有限的流量调用该服务,并监控调用成功率。如果成功率达到预期,则说明服务已恢复,进入熔断关闭状态;如果成功率仍旧很低,则重新进入熔断关闭状态。
    在这里插入图片描述

2.1.5 降级

所谓降级就是我们调用的服务异常超时等原因不能正常返回的情况下,我们返回一个缺省的值。

【由于降级经常和熔断一起使用,所以就会有熔断降级的说法。】

3. 常用的容错组件

SentinelHystrixresilience4j
隔离策略信号量隔离(并发线程数限流)线程池隔离/信号量隔离信号量隔离
熔断降级策略基于慢调用比例、异常比例、异常数基于异常比例基于异常比例、响应时间
实时统计实现滑动窗口(LeapArray)滑动窗口(基于 RxJava)Ring Bit Buffer
动态规则配置支持近十种动态数据源支持多种数据源有限支持
扩展性多个扩展点插件的形式接口的形式
基于注解的支持支持支持支持
单机限流基于 QPS,支持基于调用关系的限流有限的支持Rate Limiter
集群流控支持不支持不支持
流量整形支持预热模式与匀速排队控制效果不支持简单的 Rate Limiter 模式
系统自适应保护支持不支持不支持
热点识别/防护支持不支持不支持
多语言支持Java/Go/C++JavaJava
Service Mesh 支持支持 Envoy/Istio不支持不支持
控制台提供开箱即用的控制台,可配置规则、实时监控、机器发现等简单的监控查看不提供控制台,可对接其它监控系统

4. 什么是Sentinel

Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。

Sentinel 具有以下特征:

  • 丰富的应用场景:Sentinel承接了阿里巴巴近 10 年的双十一大促流量的核心场景, 例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
  • 完备的实时监控:Sentinel提供了实时的监控功能。通过控制台可以看到接入应用的单台机器秒级数据, 甚至 500 台以下规模的集群的汇总运行情况。
  • 广泛的开源生态:Sentinel提供开箱即用的与其它开源框架/库的整合模块, 例如与 Spring Cloud、Dubbo、gRPC 的整合。只需要引入相应的依赖并进行简单的配置即可快速地接入Sentinel。
  • 完善的 SPI 扩展点:Sentinel提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel 分为两个部分:

  • 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有Java 运行时环境,同时对Dubbo/Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于Spring Boot开发,打包后可以直接运行,不需要额外的Tomcat等应用容器。

4.1 基本的概念

  • 资源

资源就是Sentinel要保护的东西,它可以是Java程序中的任何东西。

  • 规则

规则就是用来定义如何保护资源的。

4.2 Sentinel的设计理念

4.2.1 流量控制(上游)

流量控制在网络传输中是一个常用的概念,它用于调整网络包的数据。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。

在这里插入图片描述

4.2.2 熔断降级(下游)

当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败(或者其他处理方式),避免影响到其它的资源而导致级联故障。

在这里插入图片描述
在这里插入图片描述

5. 快速开始,整合SpringBoot

  • 引入依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
  • 修改配置
spring: 
	cloud:
		sentinel: 
			transport:
                port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可
                dashboard: localhost:8080 # 指定控制台服务的地址

5.1 引入控制台

# 直接使用jar命令启动项目(控制台本身是一个SpringBoot项目)
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar

把dashboard自己也当成一个资源加入到了dashboard中来进行监控,如果不想把dashboard自己加入控制台监控可以使用简单启动指令如下:

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

其他可用参数

用户可以通过如下参数进行配置:

-Dsentinel.dashboard.auth.username=sentinel 用于指定控制台的登录用户名为 sentinel;

-Dsentinel.dashboard.auth.password=123456 用于指定控制台的登录密码为 123456;如果省略这两个参数,默认用户和密码均为 sentinel;

-Dserver.servlet.session.timeout=7200 用于指定 Spring Boot 服务端 session 的过期时间,如 7200 表示 7200 秒;60m 表示 60 分钟,默认为 30 分钟;

访问http://localhost:8080/#/login ,默认用户名密码: sentinel/sentinel

Sentinel 会在客户端首次调用的时候进行初始化,开始向控制台发送心跳包,所以要确保客户端有访问量;
在这里插入图片描述

6. Sentinel 规则

在这里插入图片描述

  • 资源名:唯一名称,默认是请求路径,可自定义。
  • 针对来源:Sentinel可以针对调用者进行限流,填写【微服务名】,指定对哪个微服务进行限流 ,默认default(不区分来源,全部限制)。
  • 阈值类型/单机阈值:
    • QPS(每秒请求数量): 当调用该接口的QPS达到阈值的时候,进行限流。
    • 线程数:当调用该接口的线程数达到阈值的时候,进行限流。
  • 是否集群:暂不需要集群

6.1 流控规则

6.1.1 流控模式

6.1.1.1 简单配置(QPS)
  • 代码
@RestController
public class SimpleController {
    @GetMapping("/simple/qps")
    public String qps(){
        //模拟一次网络延时
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return  "简单测试qps";
    }
}
  • 流控规则
    在这里插入图片描述
  • 测试

快速访问/simple/qps 接口,观察效果。
此时发现,当QPS > 1的时候,服务就不能正常响应,而是返回Blocked by Sentinel (flow limiting)结果。

在这里插入图片描述

6.1.1.2简单配置(线程数)

并发数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。

【为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。】

Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置。

  • 代码
@GetMapping("/simple/thread")
public String testThread(){
    //模拟一次网络延时
    try {
        Thread.sleep(2*1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return  "简单测试线程";
}
  • 流控规则
    在这里插入图片描述
    快速访问/simple/thread接口
6.1.1.3 关联流控模式

当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写得速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_dbwrite_db 这两个资源分别代表数据库读写,我们可以给 read_db 设置限流规则来达到写优先的目的:设置 strategyRuleConstant.STRATEGY_RELATE 同时设置 refResourcewrite_db。这样当写库操作过于频繁时,读数据的请求会被限流。

在这里插入图片描述

  • 代码
@RestController
public class FlowControllerModleContoller {

    @GetMapping("/flowModle/read")
    public String readRead(Integer orderId){
        return  "读资源";
    }
    @GetMapping("/flowModle/write")
    public String relation2(Integer orderId){
        return  "写资源";
    }
}
  • 流控规则
    在这里插入图片描述
  • 通过jmeter测试进行压力测试

大量请求写的接口,当写的接口达到设置的阈值时就会对读的接口进行限流,给读让开资源不进行争抢。

6.1.1.4 链路流控模式

链路流控模式指的是,当从某个接口过来的资源达到限流条件时,开启限流。它的功能有点类似于针对 来源配置项,区别在于:针对来源是针对上级微服务,而链路流控是针对上级接口,也就是说它的粒度更细

在这里插入图片描述

  • 代码
@Autowired
private UserService userService;

@GetMapping("/linkModle/test1")
public String test1(Integer userId){
    userService.getUser();
    return  "成功";
}
@GetMapping("/linkModle/test2")
public String test2(Integer userId){
    userService.getUser();
    return  "成功";
}

模拟资源,添加service

@Service
public class UserService {

    @SentinelResource(value = "getUser")
    public String getUser(){
        return "获取到用户信息";
    }
}
  • 流控规则

  • 添加配置

我们需要将web-context-unify参数设置为false,将其配置为false既可以根据不同的URL进行链路限流,如果不配置将不会生效
在这里插入图片描述

  • 测试

当/linkModle/test1 访问 userService 达到阈值时就会对test1进行限流,但是不会影响test2

6.1.2 流控效果

当 QPS 超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:快速失败(直接拒绝)、Warm Up(预热)、匀速排队(排队等待)。对应 FlowRule 中的 controlBehavior 字段。
在这里插入图片描述


在这里插入图片描述

6.1.2.1 快速失败(默认):

当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException 这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。

6.1.2.2 预热(Warm up):

它从开始阈值到最大QPS阈值会有一个缓冲阶段,一开始的阈值是最大QPS阈值的1/3,然后慢慢增长,直到最大阈值。

即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。

冷加载因子: codeFactor 默认是3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。

通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
在这里插入图片描述

6.1.2.3 排队等待

让请求以均匀的速度通过,单机阈值为每秒通过数量,其余的排队等待; 它还会让设置一个超时时间,当请求超过超时间时间还未处理,则会被丢弃。

该方式的作用如下图所示:
在这里插入图片描述

这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。

注意:匀速排队模式暂时不支持 QPS > 1000 的场景。【因为他的单位毫秒,所以最多是1秒通过一个,也就是1000qps】

6.2 熔断规则

除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。

6.3.1 慢比例调用

慢调用比例 (SLOW_REQUEST_RATIO):选择以慢调用比例作为阈值,需要设置允许的慢调用 RT(即最大的响应时间),请求的响应时间大于该值则统计为慢调用。当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且慢调用的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求响应时间小于设置的慢调用 RT 则结束熔断,若大于设置的慢调用 RT 则会再次被熔断。

在这里插入图片描述

  • RT:响应时间,指系统对请求作出响应的时间。
  • 比例阈值:进行熔断的判断依据之一,数字是 [0.0, 1.0]意思是[0%-100%]
  • 垄断时长:触发熔断之后等待多久再次进行尝试
  • 最小请求数:进行熔断的判断依据之一,设置的调用最小请求数。
  • 统计时长:时间的判断依据。

6.3.2 异常比例

当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。

在这里插入图片描述

6.3.3 异常数

当单位统计时长内的异常数目超过阈值之后会自动进行熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。

注意:异常降级仅针对业务异常,对 Sentinel 限流降级本身的异常(BlockException)不生效。

在这里插入图片描述

6.3 热点规则

热点参数流控规则是一种更细粒度的流控规则, 它允许将规则具体到参数上。

何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top N 数据,并对其访问进行限制。比如:

  • 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制。
  • 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制。

热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
在这里插入图片描述
注意:

  • 热点规则需要使用@SentinelResource(“resourceName”)注解,否则不生效。
  • 参数必须是7种基本数据类型才会生效。

在这里插入图片描述
这里有个坑,经过上面的配置只是对接口的限流,如果具体到参数需要再热点规则中重新设置参数

具体到参数值限流,配置参数值为1,限流阈值为3

在这里插入图片描述

6.4 授权规则

很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过:

  • 若配置白名单,则只有请求来源位于白名单内时才可通过;
  • 若配置黑名单,则请求来源位于黑名单时不通过,其余的请求通过。

例如:活动和订单都会调用用户系统获取用户信息,我们可以将活动设置为黑名单
在这里插入图片描述
在这里插入图片描述

这边最重要的就是流控应用其实这个位置要填写的是来源标识,Sentinel提供RequestOriginParser 接口来处理来源。

只要Sentinel保护的接口资源被访问,Sentinel就会调用RequestOriginParser 的实现类去解析访问来源。

  • 准备两个服务,服务A和服务B,然后服务A请求服务B的接口,在服务器A请求服务B的接口上新增heard(我这里图省事用的Gateway增加请求头)
spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        service: msb-stock
    sentinel:
      transport:
        port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可
        dashboard: localhost:8080 # 指定控制台服务的地址
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        #路由ID全局唯一
        - id: msb-order-route
          uri: http://localhost:8001
          predicates:
            - Path=/testRestTemplate/**
          filters:
            - AddRequestHeader=serviceName,root
  • 在服务B上告诉Sentinel你的标识符是什么
@Component
public class RequestOriginParserDefinition implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest request) {
        String serviceName = request.getHeader("serviceName");
        return serviceName;
    }
}
  • 在控制台配置黑白名单
    在这里插入图片描述

当serviceName = root时就是白名单可以通过,反之不能。

6.5系统规则

系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。

系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。

系统规则支持以下的模式:

  • Load 自适应(仅对 Linux/Unix-like 机器生效):系统的 load1 作为启发指标,进行自适应系统保护。当系统 load1 超过设定的启发值,且系统当前的并发线程数超过估算的系统容量时才会触发系统保护(BBR 阶段)。系统容量由系统的 maxQps * minRt 估算得出。设定参考值一般是 CPU cores * 2.5
  • CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0),比较灵敏。
  • 平均 RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
  • 并发线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
  • 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

6.6 全局流控异常统一处理

BlockException异常统一处理

springwebmvc接口资源限流入口在HandlerInterceptor的实现类AbstractSentinelInterceptor的preHandle方法中,对异常的处理是BlockExceptionHandler的实现类

@Slf4j
@Component
public class CustomBlockExceptionHandler implements BlockExceptionHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        log.info("BlockExceptionHandler BlockException================"+e.getRule());

        Result r = null;

        if (e instanceof FlowException) {
            r = Result.error(100,"接口限流了");

        } else if (e instanceof DegradeException) {
            r = Result.error(101,"服务降级了");

        } else if (e instanceof ParamFlowException) {
            r = Result.error(102,"热点参数限流了");

        } else if (e instanceof SystemBlockException) {
            r = Result.error(103,"触发系统保护规则了");

        } else if (e instanceof AuthorityException) {
            r = Result.error(104,"授权规则不通过");
        }

        //返回json数据
        response.setStatus(500);
        response.setCharacterEncoding("utf-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        new ObjectMapper().writeValue(response.getWriter(), r);

    }
}

7. Sentinel 持久化

推送模式说明优点缺点
原始模式API将规则推送至客户端并直接更新到内存中,扩展写数据源简单、无任何依赖不保证一致性;规则保存在内存中,重启不建议用于生产环境
PULL模式客户端主动向某个规则管理中心定期轮询拉取规则,<br />这个规则中心可以是 RDBMS.文件等简单、无任何依赖;规则持久化不保证一致性;实时性不保证,拉取过于频繁,也可能会有性能问题
PUSH模式规则中心统一推送,客户端通过注册监听<br />方式有更好的实时性和一致性保证。生产Nacos<br />、Zookeeper等配置中心。这种方式有更好的实时性和一致性保证。<br />生产环境下一般采用push模式的数据源。规则持久化;一致性;快速引入第三方依赖

7.1 push持久化到Nacos

生产环境下一般更常用的是 push 模式的数据源。对于 push 模式的数据源,如远程配置中心(ZooKeeper, Nacos, Apollo等等),我们在sentinel Dashboard配置的规则会同步到Nacos中, Sentinel Dashboard也会从Nacos中获取规则,Nacos会讲规则推送给Sentinel客户端

在这里插入图片描述

  • 下载已经改好的服务端:sentinel-dashboard-nacos.jar

  • 更改客户端:

    • 增加依赖
    	<dependency>
       	  	  	<groupId>com.alibaba.csp</groupId>
       		  	 <artifactId>sentinel-datasource-nacos</artifactId>
    	</dependency>
    
    • 添加配置
spring:
  cloud:
    sentinel:
      datasource:
        # 名称随意
        flow:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-flow-rules
            groupId: SENTINEL_GROUP
            # 规则类型,取值见:
            # org.springframework.cloud.alibaba.sentinel.datasource.RuleType
            rule-type: flow
        degrade:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-degrade-rules
            groupId: SENTINEL_GROUP
            rule-type: degrade
        system:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-system-rules
            groupId: SENTINEL_GROUP
            rule-type: system
        authority:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-authority-rules
            groupId: SENTINEL_GROUP
            rule-type: authority
        param-flow:
          nacos:
            server-addr: localhost:8848
            dataId: ${spring.application.name}-param-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: param-flow

这个配置可以写到一个通用配置里面去

8. Sentinel整合RestTemplate

  • 引入restTemplate
@EnableDiscoveryClient
@SpringBootApplication
public class OrderApplication {

    @Bean
    @LoadBalanced
    @SentinelRestTemplate(blockHandler = "handleException",blockHandlerClass= GlobalException.class
    ,fallback = "fallback",fallbackClass = GlobalException.class)
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class);
    }
}

流控异常回调类

public class GlobalException {

    public static SentinelClientHttpResponse handleException(HttpRequest request,
                                                             byte[] body, ClientHttpRequestExecution execution, BlockException e) {
        Result r = null;
        if (e instanceof FlowException) {
            r = Result.error(100,"接口限流了");

        } else if (e instanceof DegradeException) {
            r = Result.error(101,"服务降级了");

        } else if (e instanceof ParamFlowException) {
            r = Result.error(102,"热点参数限流了");

        } else if (e instanceof SystemBlockException) {
            r = Result.error(103,"触发系统保护规则了");

        } else if (e instanceof AuthorityException) {
            r = Result.error(104,"授权规则不通过");
        }

        try {
            return new SentinelClientHttpResponse(new ObjectMapper().writeValueAsString(r));
        } catch (JsonProcessingException ex) {
            ex.printStackTrace();
        }
        return null;
    }

    public static SentinelClientHttpResponse fallback(HttpRequest request,
                                                      byte[] body, ClientHttpRequestExecution execution, BlockException ex) {
        Result r = Result.error(-2, "===业务异常===");
        try {
            return new SentinelClientHttpResponse(new ObjectMapper().writeValueAsString(r));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }
}
  • 使用
@RestController
public class RestTemplateController {
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/testRestTemplate/{userId}")
    public String getUser(@PathVariable Integer userId){
        String result = restTemplate.getForObject("http://msb-user:10001/user/" + userId, String.class);
        return result;
    }
}
  • 设置流控规则
    在这里插入图片描述

9. Sentinel整合OpenFeign

  • 引入依赖
<!--sentinel客户端-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
 <!-- openfeign 远程调用 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  • 在配置文件中开启Feign对Sentinel的支持
feign:
	sentinel: 
		enabled: true
  • 创建限流回调处理类(不创建也可以)
@Component
public class FallbackOrderFeignServiceFactory implements FallbackFactory<UserFeignService> {
    @Override
    public UserFeignService create(Throwable throwable) {
        return new UserFeignService() {
            @Override
            public String getUserName(Integer userId) {
                if (throwable instanceof FlowException) {
                    return Result.error(100,"接口限流了");
                }

                return Result.error(-1,"=======服务降级了========");
            }
        };
    }
}

  • 创建OpenFeign 接口
//value用于指定调用nacos下哪个微服务
//fallbackFactory指定限流处理类
@FeignClient(value = "msb-user",fallbackFactory = FallbackOrderFeignServiceFactory.class)
public interface UserFeignService {
    @GetMapping("/user/{userId}")
    Result getUserName(@PathVariable Integer userId);
}
  • 调用OpenFeign接口
@RestController
public class FeignOrderController {

    @Autowired
    private UserFeignService userFeignService;

    @RequestMapping("/getOrders/{userId}")
    public Result getOrders(@PathVariable("userId") Integer userId){
        Result resultUserInfo = userFeignService.getUserName(userId);
        /**
         * 处理其它业务
         */
        return resultUserInfo;
    }
}
  • 修改流控规则
    在这里插入图片描述
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨洋阳和羊羊羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值