大纲
前言
为了减少开发的复杂度,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 {
@SentinelResource(value = "Hello", blockHandler = "exceptionHandler") @GetMapping("/hello") public String hello() { return "Hello Sentinel!"; }
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
| $ java -Dserver.port=9000 -jar sentinel-dashboard-1.8.0.jar
|
- 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
的命名空间
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-openfeign
、spring-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 的控制台可以看到已经有两个服务注册了,如下图所示:
- 3)浏览器访问
http://127.0.0.1:9000
,打开 Sentinel 的控制台,动态添加流控规则,如下图所示:
特别注意: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
的命名空间
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 的控制台可以看到已经有三个服务注册了,如下图所示:
- 3)这里让 Sentinel 基于 Route 维度进行网关限流,浏览器访问
http://127.0.0.1:9000
,打开 Sentinel 的控制台,在 sentinel-gateway
服务里动态添加网关流控规则,其中 API 名称就是 application.yml
配置文件里的路由 ID,如下图所示:
- 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/**
,” 匹配模式” 选择 前缀
- 3)在 Sentinel 控制台的菜单栏里找到
sentinel-gatway
-> 流控规则
-> 新增网关流控规则
,在表单里的 “API 类型” 选择 API 分组
,”API 名称” 选择刚刚创建的 API 即可
- 4)浏览器访问
http://127.0.0.1:8083/gateway/consumer/hello
,当快速刷新页面时,请求的响应结果变为 系统繁忙,请稍后 ...
,则说明 sentinel-gateway 微服务访问 sentinel-consumer 微服务时,Sentinel 使用自定义 API 成功对网关进行限流了
参考博客