安装

  • 首先去下载**Sentinel-dashboard**的jar包
    • 下面的图片基于**1.8.1**版本
  • sentinel-dashboard 是基于 Spring Boot 开发的控制台。打包后可以直接运行,不需要额外的 Tomcat 等应用容器。Sentinel 控制台不仅能展示服务流控、熔断降级相关的数据,还可以通过配置的方式动态的为 Sentinel 客户端下发流量控制的指令。

    • sentinel-dashboard最低需要JDK1.8版本
    • sentinel-dashboard 1.6后才有登录功能

      启动与配置Sentinel

  • Sentinel本身就是一个SpringBoot应用,下面的jvm参数都可以通过直接修改**jar**包里的**/BOOT-INF/classes/application.properties**(不推荐下面的方式,可能重启设置就失效)

  • 启动控制台的命令:java -jar sentinel-dashboard-1.8.1.jar
    • -Dserver.port=?sentinel控制台默认端口为8080 ,最好设置为不常用端口。即配置里的server.port 应用名称同理。虚拟机参数指定的端口优先级大于配置里配置的
    • -Dproject.name=? 指定应用程序的名称,如规范名称sentinel-dashboard
  • 下面的启动参数视情况采用:

    • -Dcsp.sentinel.heartbeat.client.ip=192.168.xxx.xxx 如果有多张网卡,还需要指定使用哪张网卡接收数据
    • 默认的用户名密码都是sentinel
      • -Dsentinel.dashboard.auth.username=? 修改登录用户名
      • -Dsentinel.dashboard.auth.password=? 修改登录密码
    • -Dserver.servlet.session.timeout=7200 修改session过期时间,默认单位为秒。可以指定单位,如7200为7200秒,60m为60分钟

    • Sentinel概念

      工作流程

  • Sentinel 实现了一个名为 SentinelWebInterceptor的拦截器,Sentinel 和 Spring MVC 的整合就是利用拦截器机制。该拦截器底层工作流程的伪代码如下:

    1. //初始化上下文;
    2. try {
    3. 2. 熔断、流控逻辑的判断,判断当前请求是否能继续执行;
    4. 3. 执行 “真·代码”;
    5. } catch (BlockException e) {
    6. 4. 上述第 2 步未能通过,会抛出 BlockException ,表示请求被拒绝
    7. return;
    8. } catch (Exception e) {
    9. 5. 业务异常,记录、统计异常信息
    10. throw e;
    11. } finally {
    12. 6. 收尾工作:曾经创建的资源该回收的回收,该清除的清除
    13. }
    ContextUtil.enter("上下文名称,例如:sentinel_spring_web_context");
    Entry entry = null;
    try {
      entry = SphU.entry("资源名称,例如:/rpc/openfein/demo", EntryType.IN or EntryType.OUT ); // 这背后有一个 Slot 链
      return doBusiness(); // 这里是执行业务方法,被 Sentinel “保护” 起来了
    } catch (Exception e) {
      if (!(e instanceof BlockException)) Tracer.trace(e);  // 记录调用异常
      throw e;
    } finally {
      if (entry != null) entry.exit(1); // 收尾工作:曾经创建的资源该回收的回收,该清除的清除
      ContextUtil.exit();
    }
    

    整合MVC时的原理

    public SentinelWebInterceptor implements HandlerInterceptor {
    
      public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
          try {
              1. 初始化上下文;
              2. 熔断、流控逻辑的判断,判断当前请求是否能继续执行;
              return true; // 此时 Controller 方法会被调用。Controller 方法就是 3 。
          } catch (BlockException e) {
              4. 上述第 2 步未能通过,会抛出 BlockException ,表示请求被拒绝
              return false;
          }
    
      }
    
      public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
    
      }
    
      public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        if (发生了异常) {
            5. 业务异常。记录、统计异常信息
        }
        6. 收尾工作:曾经创建的资源该回收的回收,该清除的清除
      }
    

    整合RPC的原理

  • Sentinel 通过自己提供 SentinelInvocationHandler 替换 OpenFeign 的 InvocationHandler 实现请求拦截,而它在 SentinelInvocationHandler 的 invoke 方法中中,执行了熔断(和限流)的相关代码

  • Sentinel 处在接口调用的最前端,因此 Sentinel 统计的指标数据即不会受 Ribbon 的重试影响也不会受 OpenFeign 的重试影响。

    invoke() {
    
      1. 初始化上下文;
      try {
          2. 熔断、流控逻辑的判断,判断当前请求是否能继续执行;
          3. result = methodHandler.invoke(); // 真·逻辑:发起 HTTP 网络请求
      } catch (BlockException e) {
          4. 上述第 2 步未能通过,会抛出 BlockException ,表示请求被拒绝
          return;
      } catch (Exception e) {
          5. 业务异常,记录、统计异常信息
          throw e;
      } finally {
          6. 收尾工作:曾经创建的资源该回收的回收,该清除的清除
      }
    }
    

    上下文Context/聚簇节点

  • Context 代表调用链路上下文。在整个调用链路的开始处(即,前面章节所展示的 Sentinel 的执行流程的第一步那里),Sentinel 会创建上下文 Context 对象,并且为它指定一个 name 。

  • 簇点链路是服务模块级别的,resource是接口级别的。如我gateway转发给auth

    • 簇点链路是根据网关的转发关系生成的。如下面表示gateway转给了三个服务模块。image.png

      Resource

  • 工作流程里的try-catch的真伪代码在Sentinel看来就是一份资源,Sentinel会自动为资源赋予资源名称,也可以手动指定

    • 在和Spring MVC整合时,controller方法就相当于资源,方法的URL就作为资源的名称
  • image.png

    熔断降级规则

  • 降级指的是当压力大时,关闭一些不重要的服务。降级可以看成实现熔断和限流的一种手段

  • 熔断降级规则(DegradeRule)包含下面几个重要的属性: | Field | 说明 | | —- | —- | | resource | 资源名,即规则的作用对象 | | grade | 熔断策略,支持 慢调用比例 / 异常比例 / 异常数策略 | | count | 慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);
    异常比例 / 异常数模式下为对应的阈值 | | timeWindow | 熔断时长,单位为 s | | minRequestAmount | 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) | | statIntervalMs | 统计时长(单位为 ms),如 60 * 1000 代表分钟级(1.8.0 引入) | | slowRatioThreshold | 慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) |

流量控制规则

  • 流控规则(FlowRule)包含下面几个重要的属性: | Field | 说明 | | —- | —- | | resource | 资源名,即限流规则的作用对象 | | grade | 限流类型,支持 QPS / 并发线程数 | | count | 限流阈值 | | limitApp | 流控针对的调用来源,若为 default 则不区分调用来源 | | strategy | 调用关系限流策略 | | controlBehavior | 流量控制效果,支持直接拒绝 / Warm Up / 匀速排队 |

添加流控规则

  • 控制台的菜单左侧的 簇点链路流控规则都可以针对 服务接口 添加流控规则
  • 在流控规则页面也有 新增流控规则 按钮,添加完成之后的流控规则,出现在流控规则页面列表中。

资源名称

  • 表示我们针对哪个接口资源进行流控规则配置,如:/departments/{id}

    针对来源

  • 表示针对哪一个服务访问当前接口资源的时候进行限流,default 表示不区分访问来源。

    • 如填写服务名称:xxx-service,表示 xxx-service 访问前接口资源的时候进行限流,其他服务访问该接口资源的时候不限流。

      阈值类型/单机阈值

  • 阈值类型相当于限流的单位,单机阈值相当于数值

  • QPS,每秒钟请求数量。表示每秒钟请求数超过阈值数时限流
  • 线程数开启指定的线程数个线程处理资源请求,即如果当前资源规则的2个线程都被占用时,其他访问失败

    流控模式

  • 流控模式即选择流控的手段

    • 直接:当达到限流标准时就直接限流。
    • 关联:表示关联资源接口重要性高于当前设置的资源。如果关联资源接口访问压力很大,可以牺牲当前设置的这个接口
    • 链路:链路与关联有点像。如果多个资源接口都调用了同一个方法,那么这些接口限流时先限不重要的,重要的不限或者采用更宽松的限制方式

      流控效果

  • 流控效果即流控的手段

    • 快速失败:直接失败,抛出异常,不做任何额外的处理
    • Warm up:它从开始阈值到最大 QPS 阈值会有一个缓冲阶段,一开始的阈值是最大 QPS 阈值的 1/3 ,然后慢慢增长,直到达到最大阈值。
    • 排队等待:让请求以均匀的速度通过,单机阈值为每秒通过数量,其余的排序等待。即,如果单机阈值为 N,那么没 1/N 秒有一个访问『资格』。没有『资格』时,排队等待,直到超时。

      是否集群

  • 默认情况下我们的限流策略都是针对单个服务的,sentinel 提供了集群限流的功能。

除非你的微服务规模特别大,一般不要使用集群模式。集群模式需要各节点与 token server 交互才可以,会增加网络交互次数,一定程度上会拖慢你的服务响应时间。
Sentinel - 图3Sentinel - 图4
Sentinel - 图5

使用链路限流

  • 在共同关联方法上使用@SentinelResource 然后在配置里关闭sentinel的url收敛功能

    #spring-cloud-alibaba 2.2.2.RELEASE以下的版本可能会存在配置无效的情况
    spring:
    cloud:
      sentinel:
        web-context-unify: false     #关闭url收敛功能
    
  • 在被调方法上使用:

    @SentinelResource("doSomething")
    public String doSomething() {
      return "hello world";
    }
    
  • 接下来调用该方法的资源下都会出现该方法Sentinel - 图6

  • 设置该被调方法的流控,入口资源会进行更强的流控,另外一个不会或者很弱

Sentinel - 图7

熔断降级规则

  • 在资源操作里有降级规则,此即熔断降级。
  • 注意熔断降级规则最终的触发手段为熔断,即快速失败。而流控可以选择多种流控的手段
    • 另外触发流控规则的是流量大小,而触发熔断降级规则的错误请求和慢请求
  • Sentinel支持3种降级规则

    • 慢调用比例:当资源在 统计时长 内的请求的 慢请求(响应时间超过你所设置的 n 毫秒)数累计超过指定比例,那么对资源的访问就被熔断,资源进入降级状态,持续若干秒。
    • 异常比例:当资源在 统计时长 内的请求的异常比例超你所指定的比例,那么对资源的访问就被熔断,资源进入降级状态,持续若干秒
    • 异常数:异常数和异常比例差不多,不过异常数是累计异常的数量,而非比值

      自定义熔断返回信息

  • Sentinel 返回的默认信息是 Blocked by Sentinel (flow limiting) ,如果你对默认信息不满意,你可以自定义熔断返回信息。

  • Sentinel 提供了 BlockExceptionHandler接口。当无论因何原因触发了 Sentinel 阻断用户的正常请求,Sentinel 都将『进入』到用户自定义的 BlockExceptionHandler接口的实现类中,执行 handle 方法,并传入当前的请求、响应对象以及异常对象,并以 handle 方法的执行结果作为返回,回传给用户。
  • 通过对 handle 方法的异常参数的判断,你可以直到当前发生了什么状况:

    • 需要说明的是:不止因为熔断这一个原因会导致 BlockExceptionhandler 的 handle 方法的执行,因此,需要对 handle 方法的 BlockException 参数对象进行 instanceof 判断,熔断对应的异常类型正是 DegradeException 。

      @Component
      public class MyBlockExceptionHandler implements BlockExceptionHandler {
      
      @Override
      public void handle(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws Exception {
         String msg = null;
         if (ex instanceof FlowException) {
             msg = "限流了";
         } else if (ex instanceof DegradeException) {
             msg = "熔断了";
         } else {
             msg = "其它原因";
             // ParamFlowException   "热点参数限流";
             // SystemBlockException "系统规则(负载/...不满足要求)";
             // AuthorityException   "授权规则不通过";
         }
         // http 状态码
         response.setStatus(500);
         response.setCharacterEncoding("utf-8");
         response.setHeader("Content-Type", "application/json;charset=utf-8");
         response.setContentType("application/json;charset=utf-8");
         // 利用 spring mvc 默认的 json 库 jackson
         new ObjectMapper().writeValue(response.getWriter(), msg);
      }
      }
      

      使用

  • Sentinel可以分别用在“请求发起方” 和 “请求被调方” 2 方。由于

    • 请求发起方使用的是 OpenFeign ,因此这种情况下 Sentinel 是和RPC进行整合;
    • 请求被调用使用的是 Spring MVC,因此这种情况下 Sentinel 是和 Spring MVC 进行整合。
  • 因为Sentinel有熔断与降级2个功能,因此有4种情况:
    • 在服务发起方项目中,整合 OpenFeign 进行实现熔断功能;
    • 在服务被调方项目中,整合 Spring MVC 进行实现熔断功能;
    • 在服务发起方项目中,整合 OpenFeign 进行实现限流功能;
    • 在服务被调方项目中,整合 Spring MVC 进行实现限流功能;
  • 为避免功能重复,一般是在服务发起方整合RPC实现熔断功能,在服务被调方整合Spring mvc实现限流功能

    依赖

    <!-- 其实真正起作用的是被关联引入的 sentinel-spring-webmvc-adapter 包 -->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    

    配置

  • 配置的作用是让服务连接到**sentinel-dashboard**。我们不需要去编写什么代码,只要服务连接到了**sentinel**控制台(服务接口被访问的情况下才能看到监控信息),我们就能在控制台里查看服务的流量(QPS),平均响应时长等信息,并在控制台里设置熔断与限流 ```yaml spring: cloud: sentinel:

    transport:
      dashboard: 127.0.0.1:8858    #配置控制台的地址,给一个不常用的端口
    

日志设置

logging: level: root: INFO pattern: console: “${CONSOLE_LOG_PATTERN:\ %clr(${LOG_LEVEL_PATTERN:%5p}) \ %clr(|){faint} \ %clr(%-40.40logger{39}){cyan} \ %clr(:){faint} \ %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}}”

<a name="OhAtz"></a>
## 整合Spring MVC实现流控

- 将服务与sentinel控制台连接后,通过sentinel控制台去配置即可,见添加流控规则
<a name="B1EiD"></a>
## 整合Feign实现熔断降级

- 同整合Spring MVC一样,连接到控制台后在控制台里设置熔断规则,需要配置一下feign配合sentinel的启用配置
```java
feign:
  sentinel:
    enabled: true
  • 可以给@FeignClient设置异常时的回调,原先Feign配置远程服务接口是创建个配置类,我们只需要实现该配置类即可 ```java @FeignClient( name = “a-service”,//还有一个参数value,value和name是一个东西 fallback = AServiceClientImpl.class // 设置回调方法的类 ) public interface AServiceClient { … }

//——————— @Component // @FeignClient 接口的实现类必须是单例的,所以使用@Component public class AServiceClientImpl implements AServiceClient {

@Override
public String index() {
    return "a-service index() 的 fallback 方法";
}

@Override
public String slow() {
    return "a-service slow() 的 fallback 方法";
}

}

<a name="mNZMc"></a>
### 接口定义熔断降级时的回调

- 上面地代码可能有缺失。实现远程服务接口地实现类里,触发熔断降级时自动执行地a-service地fallback缺失了。可以通过创建一个自定义的自动触发熔断降级的类实现
```java
@Component
public class RemoteUserFallbackFactory implements FallbackFactory<RemoteUserService>
{
    private static final Logger log = LoggerFactory.getLogger(RemoteUserFallbackFactory.class);

    @Override
    public RemoteUserService create(Throwable throwable)
    {
        log.error("用户服务调用失败:{}", throwable.getMessage());
        return new RemoteUserService()
        {
            @Override
            public R<LoginUser> getUserInfo(String username, String source)
            {
                return R.fail("获取用户失败:" + throwable.getMessage());
            }

            @Override
            public R<Boolean> registerUserInfo(SysUser sysUser, String source)
            {
                return R.fail("注册用户失败:" + throwable.getMessage());
            }
        };
    }
}
@FeignClient(contextId = "remoteUserService", value ="...", fallbackFactory = RemoteUserFallbackFactory.class)
public interface RemoteUserService
{
    @GetMapping("/user/info/{username}")
    public R<LoginUser> getUserInfo(@PathVariable("username") String username);

    @PostMapping("/user/register")
    public R<Boolean> registerUserInfo(@RequestBody SysUser sysUser);
}
  • 还可以通过设置@SentinelResource就可以设置触发的回调详见链接 ```java @Service public class IUserServiceImpl implements IUserService { @Autowired private RestTemplate restTemplate;

    @Bean public RestTemplate restTemplate() {

      return new RestTemplate();
    

    } //value是资源名称,即接口名 blockHandler是设置流控触发的回调 fallback触发熔断降级的回调 @SentinelResource(value = “selectUserByName”, blockHandler = “selectUserByNameBlockHandler”, fallback = “selectUserByNameFallback”) @Override public Object selectUserByName(String username) {

      return restTemplate.getForObject("http://localhost:9201/user/info/" + username, String.class);
    

    }

    // 服务流量控制处理,参数最后多一个 BlockException,其余与原函数一致。 public Object selectUserByNameBlockHandler(String username, BlockException ex) {

      System.out.println("selectUserByNameBlockHandler异常信息:" + ex.getMessage());
      return "{\"code\":\"500\",\"msg\": \"" + username + "服务流量控制处理\"}";
    

    }

    // 服务熔断降级处理,函数签名与原函数一致或加一个 Throwable 类型的参数 public Object selectUserByNameFallback(String username, Throwable throwable) {

      System.out.println("selectUserByNameFallback异常信息:" + throwable.getMessage());
      return "{\"code\":\"500\",\"msg\": \"" + username + "服务熔断降级处理\"}";
    

    }

}

<a name="qA9z1"></a>
### sentinel整合openfiegn的原理

- sentinel通过自己提供的`SentinelInvocationHandler `替换了feign的InvocationHandler,实现了请求拦截,而它在 SentinelInvocationHandler 的 invoke 方法中中,执行了熔断(和限流)的相关代码
- **sentinel处于请求进入后的流程处理的最前端,sentinel统计的指标数据不受ribbon和feign重试的影响**
   - **熔断器的很大作用就是能快速失败,快速失败即不进行请求,所以我们能推断出熔断器是在rpc前面执行的,所以微服务的请求经过为**`**客户端->网关->熔断器->rpcClient->负债均衡**`
```java
invoke() {

    1. 初始化上下文;
    try {
        2. 熔断、流控逻辑的判断,判断当前请求是否能继续执行;
        3. result = methodHandler.invoke(); // 真·逻辑:发起 HTTP 网络请求
    } catch (BlockException e) {
        4. 上述第 2 步未能通过,会抛出 BlockException ,表示请求被拒绝
        return;
    } catch (Exception e) {
        5. 业务异常,记录、统计异常信息
        throw e;
    } finally {
        6. 收尾工作:曾经创建的资源该回收的回收,该清除的清除
    }
}

持久化

  • sentinel可以实现配置规则的持久化,有多种方式。其他的持久化方式就不说了,这里只讲整合nacos实现规则持久化。
  • 整合nacos,配置下要整合的nacos的配置文件信息。再引入一个sentinel持久化的依赖即可
    <dependency>
              <groupId>com.alibaba.csp</groupId>
              <artifactId>sentinel-datasource-nacos</artifactId>
          </dependency>
    
    sentinel:
        datasource: # 持久化配置
          ds1:
            nacos:
              server-addr: 127.0.0.1:8848     #nacos服务的位置
              dataId: sentinel-ruoyi-gateway   #配置文件名
              groupId: DEFAULT_GROUP     #配置文件的组
              data-type: json    #配置保存的类型
              rule-type: flow    #这个暂时不知道什么用,就这样些写吧