使用 Spring Cloud Alibaba Sentinel 的熔断降级保护微服务应用

2021-12-06 17:02:10 浏览数 (1)

1. 背景

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

1. Sentinel 介绍

Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能。

1.1 知识

Sentinel 的组成:

  • (1) Sentinel 核心库, 即基本的类库的使用。
  • (2) Dashboard 控制台,即web页面的使用。

核心库介绍

本章节主要说 核心库 的使用。

使用 Sentinel 来进行资源保护,主要分为几个步骤:

  • 定义资源
  • 定义规则
  • 检验规则是否生效

也就是说:

  • 先把可能需要保护的资源定义好(即埋点)
  • 之后再配置规则,规则描述了什么方式来保护资源。
  • 声明了资源,后续在任何时候灵活地定义各种流量控制规则。

资源

资源:可以是指一个服务,一个服务里的方法,或者是一段代码。写代码时,考虑某段代码是否需要保护,如果需要就将之定义为一个资源。

定义资源的方式

sentinel 支持都多种方式来定义资源,常见的有:

  • 方式一:整合到常见的主流框架,比如 Web Servlet、Dubbo、Spring Cloud
  • 方式二:抛出异常的方式,使用 SphU 这个类的 try-catch 方式
  • 方式三:返回布尔值方式定义资源,使用 SphO 提供 if-else 风格的 API
  • 方式四:注解方式定义资源,使用 @SentinelResource 注解 。
  • 方式五:异步调用支持,使用 SphU.asyncEntry 异步方法。

示例有: 抛出异常的方式 来定义资源 使用 SphU 这个类的 try-catch 风格的 API。当“资源”发生了限流之后会抛出 BlockException,然后捕捉异常进行限流之后的逻辑处理。

示例代码如下:

代码语言:javascript复制
try (Entry entry = SphU.entry("resourceName")) {
  // 被保护的业务逻辑
  // do something here...
} catch (BlockException ex) {
  // 资源访问阻止,被限流或被降级
  // 在此处进行相应的处理操作
}

规则

Sentinel 支持以下几种规则:

  • 流量控制规则
  • 熔断降级规则
  • 系统保护规则
  • 来源访问控制规则
  • 热点参数规则

流量控制规则(FlowRule) 支持 QPS 模式(1)或并发线程数模式(0)。

熔断降级规则(DegradeRule) 熔断策略,支持慢调用比例/异常比例/异常数策略

系统保护规则 (SystemRule) 结合应用的 Load、CPU 使用率、总体平均 RT、入口 QPS 和并发线程数等几个维度的监控指标,通过自适应的流控策略,让系统的入口流量和系统的负载达到一个平衡

来源访问控制规则 (AuthorityRule) 即黑名单,白名单规则。

规则的持久化 建议和 nacos 一起使用,方法见本文后面章节。

1.2 在service层使用 Sentinel

一般java web 项目都会有 controller 层, service 层,dao层,我们要在 service 层使用的是可以这么做。

(1)添加依赖

代码语言:javascript复制
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

(2) 在服务层加注解:

代码语言:javascript复制
@Service
public class TestService {

    @SentinelResource(value = "sayHello")
    public String sayHello(String name) {
        return "Hello, "   name;
    }
}

@SentinelResource 注解用来标识资源是否被限流、降级。上述例子上该注解的属性 value 指示了一个资源名称。

@SentinelResource 还提供了其它额外的属性如 blockHandler,blockHandlerClass,fallback 用于表示限流或降级的操作

一般我们需要实现一个降级后的处理,比如上面的 fallback 指示一个降级后字符串返回值告知触发了熔断降级。

另外,Sentinel 控制台提供一个轻量级的控制台,它提供机器发现、单机资源实时监控、集群资源汇总,以及规则管理的功能。您只需要对应用进行简单的配置,就可以使用这些功能。我们在后面用一个章节来介绍它。

1.3 和 Feign 一起使用

Sentinel 适配了组件。

(1) 先引入 spring-cloud-starter-alibaba-sentinel 的依赖 (2) 再引入feign依赖 引入 spring-cloud-starter-openfeign 依赖**,使 Sentinel starter 中的自动化配置类生效:

代码语言:javascript复制
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

(3) 配置文件打开 配置文件打开 Sentinel 对 Feign 的支持:

代码语言:javascript复制
feign.sentinel.enabled=true

示例说明 比如下面的示例中你要了解的:

  • 1.正常的业务调用: /echo/{str}。
    1. 熔断后的异常处理,指定了 fallback 处理,并返回 "echo fallback" 字符串。

详细示例:

代码语言:javascript复制
@FeignClient(name = "service-provider", fallback = EchoServiceFallback.class, configuration = FeignConfiguration.class)
public interface EchoService {
    @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
    String echo(@PathVariable("str") String str);
}

class FeignConfiguration {
    @Bean
    public EchoServiceFallback echoServiceFallback() {
        return new EchoServiceFallback();
    }
}

class EchoServiceFallback implements EchoService {
    @Override
    public String echo(@PathVariable("str") String str) {
        return "echo fallback";
    }
}

1.4 和 RestTemplate 一起使用

支持对 RestTemplate 的服务调用使用 Sentinel 进行保护,加上 @SentinelRestTemplate 注解。

代码语言:javascript复制
@Bean
@SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class)
public RestTemplate restTemplate() {
    return new RestTemplate();
}

说明:@SentinelRestTemplate 注解的属性 @SentinelRestTemplate 注解的属性支持限流(blockHandler, blockHandlerClass)和降级(fallback, fallbackClass)的处理。

比如上面的示例指示了 ExceptionUtil.handleException 是熔断降级后的异常处理方法,该方面用明确的方法签名格式,如下:

代码语言:javascript复制
public class ExceptionUtil {
    public static ClientHttpResponse handleException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException exception) {
        ...
    }
}

方法返回值提供了 SentinelClientHttpResponse 用于构造返回信息。

注意 应用启动的时候会检查 @SentinelRestTemplate 注解对应的限流或降级方法是否存在,如不存在会抛出异常

实际项目中也会在网关层使用,见下一章节。

2. 在 Spring Cloud Gateway 网关中使用

可以结合 Spring Cloud Gateway 一起使用。

  • (1) 添加 spring-cloud-alibaba-sentinel-gateway 依赖。
  • (2) 添加 spring-cloud-starter-gateway 依赖 来让 spring-cloud-alibaba-sentinel-gateway 模块里的 Spring Cloud Gateway 自动化配置类生效:
代码语言:javascript复制
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

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

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
  • (3) 配置文件 指定 spring.cloud.sentinel.filter.enabled 为 false

注意: 网关流控规则数据源类型是 gw-flow,若将网关流控规则数据源指定为 flow 则不生效。

支持两种资源标识维度的限流 Sentinel 提供的 Spring Cloud Gateway 的适配模块可以提供两种资源维度的限流:

  • routeId:即在 gateway 中的路由 routeId。
  • 自定义分组的名称:可以利用 API 来自定义一些分组名,将URL归类在一个组下。
  • 默认不支持 URL 粒度

3. sentinel 的控制台

3.1 Sentinel 控制台包含如下功能:

  • 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。
  • 监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可以实现秒级的实时监控。
  • 规则管理和推送:统一管理推送规则。
  • 鉴权:生产环境中鉴权非常重要。这里每个开发者需要根据自己的实际情况进行定制。

3.2. 启动 sentinel 的控制台

下载最新版本的控制台 jar 包 可以从这个 release 页面 下载。

命令行启动

代码语言:javascript复制
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.2.jar  -Dsentinel.dashboard.auth.username=sentinel -Dsentinel.dashboard.auth.password=123456

说明:

  • -Dserver.port=8080 用于指定 Sentinel 控制台端口为 8080,浏览器从这个端访问。打开页面,默认用户名和密码都是 sentinel。

3.3. 规则管理

管理和新增规则 打开 http://localhost:8080 您可以在 控制台web页 配置修改规则,进行规则管理。

image.png

点击新增规则按钮,如下:

image.png

规则的存储 默认是存储在内存的,应用重启之后该规则会丢失。建议通过一些配置来使用外部存储方式来保存。建议结合 nacos 动态实时的刷新规则。

3.4. 规则推送

规则推送分为 3 种模式,包括:

  • 原始模式
  • Pull 模式
  • Push 模式"。

原始模式

通过 API 将规则推送至客户端并直接更新到内存中, 图例:

代码语言:javascript复制
                             ------> sentinel 客户端1
Sentinel                     ------> sentinel 客户端2
Dashboard
                             ------> sentinel 客户端3

好处: 简单,无依赖; 坏处: 应用重启规则就会消失,不能用于生产环境

Pull模式

在客户端注册一个本地文件数据源:收到控制台推送的规则时,Sentinel 会先更新到内存,然后将规则写入到文件中。

本地文件数据源会定时轮询文件的变更,读取规则。

这样我们既可以在应用本地直接修改文件来更新规则,也可以通过 Sentinel 控制台推送规则。

以本地文件数据源为例,过程如下图所示:

代码语言:javascript复制
                                      /---------- 在内存中更新规则(规则缓存)
                                     /
Sentinel        ----->   Sentinel客户端     ----> 将规则写入本地文件   ----> 本地文件
Dashboard

好处:简单,不引入新的依赖 坏处:无法保证监控数据的一致性

Push模式

Sentinel 控制台 的规则到 统一配置中心(比如nacos),再到各个 客户端。

即: Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel

图例:

代码语言:javascript复制
                       nacos
             (1)    /        
                   /               (2)
                 /               
Sentinel                          |----    sentinel 客户端1
Dashboard                         |----    sentinel 客户端2

生产环境建议使用 PUSH 模式,改造方法见下一章节。

4. 和 nacos 集成

4.1 介绍

默认情况下,规则是存储在内存的,重启后就没了。因此在生产环境建议使用nacos 集成来使用。分成两个步骤:

  • (1) 在sentinel dashboard 控制台的web管理页面创建规则,并将规则存储到nacs。需要改造sentinel 控制台。
  • (2) 客户端应用获得从 nacos 推送(PUSH)而来的 “限流的配置规则”,并加载到sentinel中。

即:在sentinel dashboard 的web页设置限流规则 ---> 规则存储到 nacos ---> 再推送到客户端应用

4.2 改造 sentinel 控制台

改造的目标是:改造 sentinel 控制台,使得在控制台的web页修改的规则保存到nacos中去。 具体改造方法:略。 有同学已经改造好的在这里:https://gitee.com/schonglin/sentinel-nacos

4.3 配置客户端,读nacos数据源。

实现的目标是:从nacos 读取规则并应用到客户端应用中。

4.3.1 修改客户端服务的配置文件,添加一个数据源

下面的示例中,我添加了一个 sentinel 的数据源 ds2, 指定了 nacos服务的地址,data-id 配置文件名,规则是 rule-type 限流类型。

代码语言:javascript复制
spring:
  application:
    name: business
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080 # 指定控制台地址和端口
        port: 8721 # 这个端口和 Sentinel dashborad 做交互
      datasource:
        # 指定一个用于流控规则的数据源(来自nacos)
        ds2:
          nacos:
            server-addr: ${spring.cloud.nacos.discovery.server-addr}
            data-id: ${spring.application.name}-flow-rules  # 比如流控的规则是 {appName}-flow-rules
            group-id: SENTINEL_GROUP # 指定的一个分组名
            data-type: json
            rule-type: flow

4.3.2 在nacos中添加一个限流的配置文件,和数据源名称一致

我在nacos中添加一个限流的配置文件,名字叫做${spring.application.name}-sentinel-flow,它的格式和上面 data-id 要对应上。

代码语言:javascript复制
[
    {
        "resource": "/hello",
        "limitApp": "default",
        "grade": 1,
        "count": 6,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]

上面的配置内容说明:

  • resource:资源名
  • limitApp:调用来源, default 则不区分调用来源
  • grade:限流阈值类型(QPS 或并发线程数);0代表根据并发数量来限流,1代表根据QPS来进行流量控制
  • count:限流阈值,和上面的类型相关
  • strategy:调用关系限流策略
  • controlBehavior:流量控制效果(直接拒绝、Warm Up、匀速排队)
  • clusterMode:是否为集群模式

访问几次接口后,就可以在Sentinel Dashboard 中看到在nacos中配置的规则信息,重启后也可以再次重nacos获取到配置好的规则。

我的示例demo 见: https://github.com/vir56k/java_demo/tree/master/sentinel/sentineldemo3 配合 改造后支持nacos的sentinel 来使用。

5. 参考

Sentinel 控制台 https://github.com/alibaba/Sentinel/wiki/控制台 https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel https://github.com/alibaba/Sentinel/wiki/网关限流 https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel https://github.com/alibaba/Sentinel/wiki/如何使用 https://github.com/alibaba/Sentinel/wiki/在生产环境中使用-Sentinel https://www.cnblogs.com/gyli20170901/p/11279576.html https://github.com/alibaba/Sentinel/wiki/Sentinel-控制台(集群流控管理)#规则配置 https://blog.csdn.net/qq_38723394/article/details/108991518

0 人点赞