基于 Sentinel 作熔断

2020-05-25 16:26:37 浏览数 (1)

我们都知道Spring cloud 作熔断降级的组件 Hystrix,Spring cloud 之熔断机制(实战)一文中,也讲述了如何使用 Hystrix,这是大家一直耳熟能详的。其实阿里的一款神器 Sentinel,也可以提供熔断降级的功能。

Sentinel简介

随着微服务的发展,服务们之间的稳定性变得越来越重要。Sentinel 以流量作为切入点,从流量控制、熔灾降级、系统负载保护等多个维度来保护服务的稳定性。Sentinel 具有以下特征:完备的实时监控、广泛的开源生态、完善的 SPI 扩展点、积累丰富的场景。

首先,我们先简单看看 Sentinel 与 Hystrix 的区别:

功能

Sentinel

Hystrix

隔离策略

信号量隔离策略

线程池/信号量隔离策略

熔断降级

基于响应时间、异常次数或异常比例

基于异常比例

动态规则配置

支持多数据源

支持多数据源

限流

基于QPS

有限的支持

系统自适应保护

支持

不支持

控制台管理

可配置规则、查看监控、服务发现等

简单的链路监控查看

基于注解的支持

支持

支持

处理规则

支持预热、匀速器、排队模式

不支持

扩展性

多个扩展

插件形式

上面看到 Sentinel 能实现的功能也很多的,而且在熔断方面跟 Hystrix 差不多,甚至更好,所以接下来我们看看利用 Sentinel 来实现熔断的逻辑。

首先,服务生产者还是按照 Spring Cloud Kubernetes之实战二服务注册与发现 一文来进行,这里利用了简单的k8s作为服务的注册与发现功能。跟

eureka、zk、nacos 等配置都基本类似。只不过这里不用那些了,只用k8s来处理即可。

对于服务消费者,首先配置依赖:

代码语言:javascript复制
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.8.RELEASE</version>
        <relativePath/>
    </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <swagger.version>2.6.1</swagger.version>
    <xstream.version>1.4.7</xstream.version>
    <pageHelper.version>4.1.6</pageHelper.version>
    <fastjson.version>1.2.51</fastjson.version>
    <springcloud.version>Greenwich.SR4</springcloud.version>
    <!-- <springcloud.version>2.1.8.RELEASE</springcloud.version> -->
    <springcloud.kubernetes.version>1.1.1.RELEASE</springcloud.kubernetes.version>
    <mysql.version>5.1.46</mysql.version>
    <springcloud.alibaba.version>0.2.2.RELEASE</springcloud.alibaba.version>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-alibaba-dependencies</artifactId>
        <version>${springcloud.alibaba.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>

      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>${springcloud.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <exclusions>
        <exclusion>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-undertow</artifactId>
    </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <!-- spring-cloud-starter-oauth2包含了 spring-cloud-starter-security -->
        <!-- <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency> -->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-actuator-autoconfigure</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes-config</artifactId>
            </dependency>

    <!-- springcloud-k8s-discovery -->

    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-commons</artifactId>
        </dependency>

    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-kubernetes-core</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-kubernetes-discovery</artifactId>
        </dependency>

    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
        </dependency>

    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
</dependencies>

接下来就需要配置 Sentinel 的监听:

代码语言:javascript复制
spring:
  application:
    name: operate-git-service
  cloud:
    sentinel:
      enabled: true #Sentinel自动化配置是否生效
      eager: true #取消Sentinel控制台懒加载
      log:
        dir: /data/sentinel/logs #Sentinel日志文件所在的目录
      transport:
        dashboard: localhost:8080 #sentinel的Dashboard地址
        heartbeatIntervalMs: 500 #应用与Sentinel控制台的心跳间隔时间 or heartbeat-interval-ms
        port: 8719

以上配置完成后,我们写一个测试类 TestController:

代码语言:javascript复制
@GetMapping(value = "/getUser")
//@SentinelResource(value = "hello",blockHandler = "apiBlockHandlerException", blockHandlerClass = MyExceptionUtil.class) //fallback = "testFallBack"
public Response<Object> getUser() {
  return gitClientService.getUser();
}

接着写一个Service:

代码语言:javascript复制
@SentinelResource(value = "hello",blockHandler = "apiBlockHandlerException", blockHandlerClass = MyExceptionUtil.class, fallback = "testFallBack")
@Override
public Response<Object> getUser() {
  return Response.ok(200, 0, "success", "data");
}

public Response<Object> testFallBack() {
  return Response.ok(200, -5, "testFallBack ...", null);
}

限流异常处理类:
public class MyExceptionUtil {
  /**
   *
   * @author Damon
   * @date 2020年3月24日
   *
   */

  /**
   * 
   * 基于API函数的限流
   * @param ex
   * @return
   * @author Damon 
   * @date 2020年3月24日
   *
   */
  public static Response<Object> apiBlockHandlerException(BlockException ex) {
    return Response.ok(200, -4, "block 。。。。", null);
  }
 }

大家可以发现,controller 与 service 中都可以有注解 @SentinelResource,而且在 Spring cloud 之多种方式限流(实战)一文中,我们知道 Sentinel 默认为所有的 HTTP 服务提供限流埋点,而且有默认的限流提示(对于接口),只需要控制配置限流规则即可。这里,我们加了 @SentinelResource,其提供了属性 blockHandler、blockHandlerClass 限流的处理逻辑,同时,该注解也提供了 fallback 属性可以直接加熔断的处理函数。

同样,我们先打开 Sentinel 的Dashboard,看看其服务实时监控(如果没有则先请求几个接口):

请求的几个簇点链路:

我们可以针对函数本身作任何的规则设置,也可以针对注解@SentinelResource资源来进行设置规则:

发现,对于方法,直接加限流或降级规则设置后,其返回的结果都是:

代码语言:javascript复制
Blocked by Sentinel (flow limiting)

而对于注解 @SentinelResource,设置限流规则:

访问其接口时,发现返回:

代码语言:javascript复制
{"message":{"status":200,"code":-4,"message":"block 。。。。"},"data":null}

设置熔断规则:

访问后,返回:

代码语言:javascript复制
{"message":{"status":200,"code":-5,"message":"testFallBack ..."},"data":null}

从上面的测试结果来看:

  • Sentinel 默认给所有的Http服务 设下埋点,只要定义好规则(限流、熔断),其都会按照规则执行,并且返回默认的信息: Blocked by Sentinel (flow limiting)
  • Sentinel 中设置的注解 @SentinelResource 中,包括属性 value、blockHandler、blockHandlerClass、fallback,其中中间两个是对限流定义的类与其出路逻辑函数,而 fallback 是针对熔断规则设置的处理函数。
  • 设置的规则中,限流的处理函数中,必须要有BlockException ex 这个参数,返回值要与原函数的返回值一致。而熔断的处理函数中,其函数的参数与返回值必须与原函数的都完全一致。

OK,Sentinel 作为熔断机制来处理,操作结束。

0 人点赞