Sentinel 入门教程 - 整合篇

大纲

前言

为了减少开发的复杂度,Sentinel 对大部分的主流框架,例如 Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux,Reactor 等都做了适配,只需要引入对应的依赖即可方便的整合 Sentinel。如果要实现 Spring Cloud 和 Sentinel 的整合,可以通过引入 Spring Cloud Alibaba Sentinel 来整合 Sentinel。Spring Cloud Alibaba 是阿里巴巴开源的,致力于提供微服务开发的一站式解决方案。Spring Cloud Alibaba 默认为 Sentinel 整合了 Servlet、RestTemplate、FeignClient 和 Spring WebFlux。Sentinel 在 Spring Cloud 生态中,不仅补全了 Hystrix 在 Servlet 和 RestTemplate 这一块空白,而且还完全兼容了 Hystrix 在 FeignClient 中限流降级的用法,并且支持运行时灵活地配置和调整限流降级规则。

Sentinel 整合 Spring Cloud

1.0、版本说明

本案例使用各开源组件的版本说明如下,点击下载完整的案例代码

  • Sentinel 1.8.0
  • Spring Boot 2.1.18.RELEASE
  • Spring Cloud Greenwich.SR6
  • Spring Cloud Alibaba Sentinel 2.1.3.RELEASE

1.1、引入 Maven 依赖

添加 spring-cloud-starter-alibaba-sentinel 依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.18.RELEASE</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring-cloud.version>Greenwich.SR6</spring-cloud.version>
<spring-cloud-starter-sentinel>2.1.3.RELEASE</spring-cloud-starter-sentinel>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>${spring-cloud-starter-sentinel}</version>
</dependency>
</dependencies>

1.2、创建主启动类

1
2
3
4
5
6
7
@SpringBootApplication
public class SentinelApplication {

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

1.3、创建 Controller 测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@RestController
public class TestController {

/**
* 定义资源
* value:资源名称
* blockHandler:限流处理的方法
*
* @return
*/
@SentinelResource(value = "Hello", blockHandler = "exceptionHandler")
@GetMapping("/hello")
public String hello() {
// 使用限流规则
return "Hello Sentinel!";
}

/**
* 原方法被限流的时候调用此方法
*
* @param e
* @return
*/
public String exceptionHandler(BlockException e) {
e.printStackTrace();
return "系统繁忙,请稍候 ...";
}
}

1.4、配置 Sentinel 控制台

application.yml 配置文件里,指定 Sentinel 控制台的地址和端口

1
2
3
4
5
6
7
8
9
10
server:
port: 8080

spring:
application:
name: sentinel-spring-cloud
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:9000

1.5、测试代码

  • 1)启动 Sentinel 控制台
1
$ java -Dserver.port=9000 -jar sentinel-dashboard-1.8.0.jar
  • 2)启动 Spring Cloud 应用,浏览器访问 http://127.0.0.1:8080/hello,若响应结果返回 Hello Sentinel!,说明应用启动成功

  • 3)浏览器访问 http://127.0.0.1:9000,打开 Sentinel 控制台,动态添加流控规则,如下图所示:

sentinel-spring-cloud-add-rule

  • 4)浏览器再次访问 http://127.0.0.1:8080/hello,当快速刷新页面时,请求的响应结果变为 系统繁忙,请稍后 ...,,则说明 Sentinel 的流控规则生效了

Sentinel 整合 OpenFeign

Sentinel 适配了 OpenFeign 组件,如果想使用,除了引入 spring-cloud-starter-alibaba-sentinel 依赖之外,还需要以下两个步骤:

  • 在配置文件里打开 Sentinel 对 OpenFeign 的支持:feign.sentinel.enabled=true
  • 加入 spring-cloud-starter-openfeign 依赖使 Sentinel starter 中的自动化配置类生效

2.0、版本说明

本案例使用各开源组件的版本说明如下,其中服务注册中心使用 Nacos,若改为使用 Eureka,只需要在案例里将 Nacos 相关的配置(Maven 依赖 + YAML 配置)替换掉即可,点击下载完整的案例代码

  • Sentinel 1.8.0
  • Nacos Server 1.4.0
  • Spring Boot 2.1.18.RELEASE
  • Spring Cloud Greenwich.SR6
  • Spring Cloud Alibaba Sentinel 2.1.3.RELEASE
  • Spring Cloud Alibaba Nacos Config 2.1.3.RELEASE

2.1、案例目标

实现 sentinel-consumer 微服务通过 OpenFeign 访问 sentinel-provider 微服务时的流量控制

2.2、准备工作

启动 Sentinel 控制台

1
$ java -Dserver.port=9000 -jar sentinel-dashboard-1.8.0.jar

启动 Nacos Server,并在 Nacos Server 的控制面台里,创建名称为 dev 的命名空间

sentinel-nacos-create-namespace

2.3、创建 Maven 父工程

创建 Maven 父工程,配置好工程需要的父级依赖,目的是为了更方便管理与简化配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.18.RELEASE</version>
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring-cloud.version>Greenwich.SR6</spring-cloud.version>
<spring-cloud-starter-sentinel>2.1.3.RELEASE</spring-cloud-starter-sentinel>
<spring-cloud-starter-nacos.version>2.1.3.RELEASE</spring-cloud-starter-nacos.version>
</properties>

<!-- 管理依赖 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>${spring-cloud-starter-sentinel}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${spring-cloud-starter-nacos.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

<!-- 利用传递依赖,公共部分 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

2.4、创建 Sentinel Provider 工程

引入 Maven 依赖

1
2
3
4
5
6
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>

创建主启动类,添加 @EnableDiscoveryClient 注解,启用服务发现功能,并将服务注册到 Nacos

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {

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

创建 Controller 测试类

1
2
3
4
5
6
7
8
9
10
11
@RestController
public class ProviderController {

private Logger LOG = LoggerFactory.getLogger(ProviderController.class);

@GetMapping("/hello")
public String hello() {
LOG.info("provider invoke ... ");
return "Hello Sentinel!";
}
}

创建 application.yml 配置文件,添加 Nacos 注册中心的地址和端口等信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 8080
servlet:
context-path: /provider

spring:
application:
name: sentinel-provider
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: 4bfcbae8-8c37-417d-89e4-d5134e23eb18
cluster-name: DEFAULT

2.5、创建 Sentinel Consumer 工程

引入 Maven 依赖,包括 spring-cloud-starter-openfeignspring-cloud-starter-alibaba-sentinel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>

创建 Feign Client 的接口类

1
2
3
4
5
6
@FeignClient(value = "sentinel-provider", fallback = FallbackService.class)
public interface FeignAgent {

@GetMapping("/provider/hello")
public String hello();
}

创建处理限流、降级的回调类

1
2
3
4
5
6
7
8
@Component
public class FallbackService implements FeignAgent {

@Override
public String hello() {
return "系统繁忙,请稍候 ...";
}
}

创建主启动类,添加 @EnableDiscoveryClient@EnableFeignClients 注解

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ConsumerApplication {

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

创建 Controller 测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
public class ConsumerController {

@Autowired
private FeignAgent feignAgent;

private Logger LOG = LoggerFactory.getLogger(ConsumerController.class);

@GetMapping("/hello")
public String hello() {
LOG.info("consumer invoke ... ");
return feignAgent.hello();
}
}

创建 application.yml 配置文件,添加 Nacos 注册中心的地址和端口等信息,并启用 Sentinel 对 OpenFeign 的支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server:
port: 8082
servlet:
context-path: /consumer

spring:
application:
name: sentinel-consumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: 4bfcbae8-8c37-417d-89e4-d5134e23eb18
cluster-name: DEFAULT
sentinel:
transport:
dashboard: 127.0.0.1:9000

feign:
sentinel:
enabled: true

2.6、测试代码

  • 1)分别启动 sentinel-consumer、sentinel-provider 应用
  • 2)浏览器访问 http://127.0.0.1:8082/consumer/hello,若响应结果为 Hello Sentinel!,说明两个应用启动成功,同时在 Nacos 的控制台可以看到已经有两个服务注册了,如下图所示:

sentinel-nacos-services

  • 3)浏览器访问 http://127.0.0.1:9000,打开 Sentinel 的控制台,动态添加流控规则,如下图所示:

sentinel-feign-add-flow-rule

特别注意:Sentinel 与 Feign 整合时,流控规则的编写格式为 HTTP请求方式:协议://服务名/请求路径跟参数,例如:GET:http://sentinel-provider/provider/hello

  • 4)浏览器再次访问 http://127.0.0.1:8082/consumer/hello,当快速刷新页面时,请求的响应结果变为 系统繁忙,请稍后 ...,则说明 sentinel-consumer 微服务通过 OpenFeign 访问 sentinel-provider 微服务时,Sentinel 的流控规则生效了

Sentinel 网关限流

Sentinel 支持对 Spring Cloud Gateway、Zuul 1.x、Zuul 2.x 等主流的 API Gateway 进行限流。

Sentinel 整合 Gateway

从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:

  • route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
  • 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组

3.0、案例说明

本案例是在上述 Sentinel 整合 OpenFeign 案例的基础上开发的,注册中心依旧使用 Nacos,其中主要的变化是新创建了 sentinel-gateway 工程,因此下面只给出新增或者更改后的代码和配置,点击下载完整的案例代码。

3.1、案例目标

实现 sentinel-gateway 微服务访问 sentinel-consumer 微服务时的流量控制,其中 sentinel-consumer 微服务通过 OpenFeign 访问 sentinel-provider 微服务时的流量控制在上述 Sentinel 整合 OpenFeign 案例已经实现了,完整的调用流程为 sentinel-gateway –> sentinel-consumer –> sentinel-provider。

3.2、准备工作

启动 Sentinel 控制台

1
$ java -Dserver.port=9000 -jar sentinel-dashboard-1.8.0.jar

启动 Nacos Server,并在 Nacos Server 的控制面台里,创建名称为 dev 的命名空间

sentinel-nacos-create-namespace

3.3、更改 Maven 父工程

添加 spring-cloud-alibaba-sentinel-gateway 依赖

1
2
3
4
5
6
7
8
9
<properties>
<spring-cloud-starter-sentinel>2.1.3.RELEASE</spring-cloud-starter-sentinel>
</properties>

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
<version>${spring-cloud-starter-sentinel}</version>
</dependency>

3.4、创建 Sentinel Gateway 工程

引入 Maven 依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<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>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>

创建 Gateway 的配置类,用于定义被限流或者降级时处理的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Configuration
public class GatewayConfiguration {

/**
* 初始化
*/
@PostConstruct
public void init() {
// 设置被限流或者降级处理时的回调方法
GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {

// 被限流或者降级时处理的方法
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
return ServerResponse.status(200).syncBody("系统繁忙,请稍后 ...");
}
});
}
}

创建主启动类,添加 @EnableDiscoveryClient 注解

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {

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

创建 application.yml 配置文件,由于这里指定了 context-path,因此在路由规则配置中需要使用 StripPrefix 参数将访问进来的 URL 中的 context-path 截取掉,否则 sentinel-gateway 微服务访问 sentinel-consumer 微服务时,会出现 404 错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
server:
port: 8083
servlet:
context-path: gateway

spring:
application:
name: sentinel-gateway
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: 4bfcbae8-8c37-417d-89e4-d5134e23eb18
cluster-name: DEFAULT
sentinel:
transport:
dashboard: 127.0.0.1:9000
gateway:
routes:
- id: sentinel-gateway-route
uri: lb://sentinel-consumer
predicates:
- Path=/${server.servlet.context-path}/consumer/hello/**
filters:
- StripPrefix=1

若在 application.yml 配置文件里没有配置 context-path,那么路由规则配置可以使用以下的写法:

1
2
3
4
5
6
7
8
9
10
11
server:
port: 8083

spring:
cloud:
gateway:
routes:
- id: sentinel-gateway-route
uri: lb://sentinel-consumer
predicates:
- Path=/consumer/hello/**

3.5、测试代码

  • 1)分别启动 sentinel-gateway、sentinel-consumer、sentinel-provider 应用
  • 2)浏览器访问 http://127.0.0.1:8083/gateway/consumer/hello,若响应结果为 Hello Sentinel!,说明三个应用启动成功,同时在 Nacos 的控制台可以看到已经有三个服务注册了,如下图所示:

sentinel-gatway-services

  • 3)这里让 Sentinel 基于 Route 维度进行网关限流,浏览器访问 http://127.0.0.1:9000,打开 Sentinel 的控制台,在 sentinel-gateway 服务里动态添加网关流控规则,其中 API 名称就是 application.yml 配置文件里的路由 ID,如下图所示:

sentinel-gateway-add-flow-rule

  • 4)浏览器再次访问 http://127.0.0.1:8083/gateway/consumer/hello,当快速刷新页面时,请求的响应结果变为 系统繁忙,请稍后 ...,则说明 sentinel-gateway 微服务访问 sentinel-consumer 微服务时,Sentinel 的网关流控规则生效了

3.6、使用自定义 API 进行限流

从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:

  • route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
  • 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组

  • 1)在 Sentinel 控制台里,删除所有与 sentinel-gateway 应用相关的网关流控规则

  • 2)在 Sentinel 控制台的菜单栏里找到 sentinel-gatway -> API 管理 -> 新增 API 分组,由于上面在 application.yml 配置文件中指定了 context-path,因此表单里的” 匹配串” 为 /gateway/consumer/hello/**,” 匹配模式” 选择 前缀

sentinel-gateway-add-api

sentinel-gateway-add-api-2

  • 3)在 Sentinel 控制台的菜单栏里找到 sentinel-gatway -> 流控规则 -> 新增网关流控规则,在表单里的 “API 类型” 选择 API 分组,”API 名称” 选择刚刚创建的 API 即可

sentinel-gateway-api-add-flow-rule

  • 4)浏览器访问 http://127.0.0.1:8083/gateway/consumer/hello,当快速刷新页面时,请求的响应结果变为 系统繁忙,请稍后 ...,则说明 sentinel-gateway 微服务访问 sentinel-consumer 微服务时,Sentinel 使用自定义 API 成功对网关进行限流了

参考博客