一、前言
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。 Sentinel
以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
在前面我们通过以下章节对Sentinel有了基础的了解:
Sentinel:分布式系统的流量防卫兵 | Spring Cloud 19
Sentinel:资源与规则定义 | Spring Cloud 20
Sentinel:原理深入浅出解读 | Spring Cloud 21
Sentinel:流量控制规则定义详解 | Spring Cloud 22
Spring Boot/Cloud集成Sentinel实现流量控制 | Spring Cloud 23
现在开始我们正式学习Sentinel在Spring Boot/Cloud中的集成使用。
书接上回:Spring Boot/Cloud集成Sentinel实现流量控制 | Spring Cloud 23 ,本节进行对以下部分进行集成演示:
-
Sentinel
与Feign
集成 -
Sentinel
与RestTemplate
集成 -
Spring Boot/Cloud
集成Sentinel
开启链路流控模式
二、项目集成 Sentinel
2.1 项目总体结构
- 模块
sentinel-nacos-provider
用于演示自定义限流处理逻辑 - 模块
sentinel-nacos-consumer
用于演示Sentinel
与Feign
集成
用于演示Sentinel
与RestTemplate
集成
用于演示Sentinel
链路流控模式
2.2 sentinel-nacos-provider模块
请见:Spring Boot/Cloud集成Sentinel实现流量控制 | Spring Cloud 23
2.3 sentinel-nacos-consumer模块
2.3.1 引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
完整sentinel/sentinel-nacos-consumer/pom.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.gm</groupId>
<artifactId>sentinel</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>sentinel-nacos-consumer</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</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-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<excludes>
<exclude>**/*.xlsx</exclude>
<exclude>**/*.xls</exclude>
</excludes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<includes>
<include>**/*.xlsx</include>
<include>**/*.xls</include>
</includes>
</resource>
</resources>
</build>
</project>
2.3.2 配置文件信息
完整版src/main/resources/bootstrap.yml
:
server:
port: 3000
spring:
application:
name: @artifactId@
cloud:
nacos:
username: @nacos.username@
password: @nacos.password@
discovery:
server-addr: ${NACOS_HOST:nacos1.kc}:${NACOS_PORT:8848},${NACOS_HOST:nacos2kc}:${NACOS_PORT:8848},${NACOS_HOST:nacos3.kc}:${NACOS_PORT:8848}
sentinel:
transport:
port: 8720
dashboard: 127.0.0.1:8080
web-context-unify: false
feign:
sentinel:
enabled: true # 开启feign对sentinel的支持
2.3.3 服务测试类
完整版 com/gm/sentinel_nacos_consumer/controller/ConsumerController.java
:
import com.gm.sentinel_nacos_consumer.service.ChainService;
import com.gm.sentinel_nacos_consumer.service.ProviderServiceFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ConsumerController {
@Autowired
ProviderServiceFeign providerServiceFeign;
@Autowired
ChainService chainService;
@Autowired
RestTemplate restTemplate;
/**
* 用于测试Feign支持
*
* @return
*/
@RequestMapping(value = "sayHello", method = RequestMethod.GET)
public String sayHello() {
return providerServiceFeign.sayHello("hello world");
}
/**
* 用于测试STRATEGY_DIRECT模式,根据调用方进行限流。
*
* @return
*/
@RequestMapping(value = "sayHelloDirect", method = RequestMethod.GET)
public String sayHelloDirect() {
return chainService.message();
}
/**
* 用于测试STRATEGY_CHAIN模式,根据调用链路入口限流。
*
* @return
*/
@RequestMapping(value = "sayHelloChain", method = RequestMethod.GET)
public String sayHelloChain() {
return chainService.message();
}
/**
* 用于restTemplate限流测试
*
* @return
*/
@RequestMapping(value = "sayHelloRest", method = RequestMethod.GET)
public String sayHelloRestTemplate() {
String url = String.format("http://sentinel-nacos-provider/sayHello?world=hello world");
String result = restTemplate.getForObject(url, String.class);
return result;
}
}
此类中的所需的依赖类,在下文中均有体现。
2.3.4 Sentinel 与 Feign 集成
加入 spring-cloud-starter-openfeign
依赖使 Sentinel starter
中的自动化配置类生效:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
在配置文件中加入 Sentinel
对 Feign
的支持:
feign:
sentinel:
enabled: true # 开启feign对sentinel的支持
新建简单使用示例com/gm/sentinel_nacos_consumer/service/ProviderServiceFeign.java
:
import com.gm.sentinel_nacos_consumer.config.FeignConfiguration;
import com.gm.sentinel_nacos_consumer.service.impl.ProviserServiceFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "sentinel-nacos-provider", fallback = ProviserServiceFallback.class, configuration = FeignConfiguration.class)
public interface ProviderServiceFeign {
@RequestMapping(value = "sayHello", method = RequestMethod.GET)
String sayHello(@RequestParam("world") String world);
}
Feign
对应的接口中的资源名策略定义:httpmethod:protocol://requesturl
。@FeignClient
注解中的所有属性,Sentinel
都做了兼容。
ProviderServiceFeign
接口中方法sayHello
对应的资源名为:GET:http://sentinel-nacos-provider/sayHello
新建FeignClient
的配置类com/gm/sentinel_nacos_consumer/config/FeignConfiguration.java
:
import com.gm.sentinel_nacos_consumer.service.impl.ProviserServiceFallback;
import org.springframework.context.annotation.Bean;
public class FeignConfiguration {
@Bean
public ProviserServiceFallback proviserServiceFallback() {
return new ProviserServiceFallback();
}
}
新建熔断器的处理类com/gm/sentinel_nacos_consumer/service/impl/ProviserServiceFallback.java
:
import com.gm.sentinel_nacos_consumer.service.ProviderServiceFeign;
public class ProviserServiceFallback implements ProviderServiceFeign {
@Override
public String sayHello(String world) {
return "fallback";
}
}
先访问:http://127.0.0.1:3000/sayHello 测试功能是否正常访问:
新建流控规则:
高频刷新:http://127.0.0.1:3000/sayHello ,验证Feign
集成效果:
2.3.5 Sentinel 与 RestTemplate 集成
Spring Cloud Alibaba Sentinel
支持对 RestTemplate
的服务调用使用 Sentinel
进行保护,在构造 RestTemplate bean
的时候需要加上 @SentinelRestTemplate
注解。
com/gm/sentinel_nacos_consumer/config/RestTemplateConfig.java
:
import com.alibaba.cloud.sentinel.annotation.SentinelRestTemplate;
import com.gm.sentinel_nacos_consumer.component.ExceptionUtil;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
@SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class)
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@SentinelRestTemplate
注解的属性支持限流(blockHandler
, blockHandlerClass
)和降级(fallback
, fallbackClass
)的处理。
其中
blockHandler
或fallback
属性对应的方法必须是对应blockHandlerClass
或fallbackClass
属性中的静态方法。该方法的参数跟返回值跟
org.springframework.http.client.ClientHttpRequestInterceptor#interceptor
方法一致,其中参数多出了一个BlockException
参数用于获取Sentinel
捕获的异常。
比如上述 @SentinelRestTemplate
注解中 ExceptionUtil
的 handleException
属性对应的方法声明如下,com/gm/sentinel_nacos_consumer/component/ExceptionUtil.java
:
import com.alibaba.cloud.sentinel.rest.SentinelClientHttpResponse;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpResponse;
public class ExceptionUtil {
public static ClientHttpResponse handleException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException exception) throws Exception {
ClientHttpResponse response = new SentinelClientHttpResponse("请求被限流!");
return response;
}
}
@SentinelRestTemplate
注解的限流(blockHandler
,blockHandlerClass
)和降级(fallback
,fallbackClass
)属性不强制填写。
当使用 RestTemplate
调用被 Sentinel
熔断后,会返回 RestTemplate request block by sentinel
信息,或者也可以编写对应的方法自行处理返回信息。这里提供了 SentinelClientHttpResponse
用于构造返回信息。
Sentinel RestTemplate
限流的资源规则提供两种粒度:
httpmethod:schema://host:port/path
:协议、主机、端口和路径httpmethod:schema://host:port
:协议、主机和端口
http://sentinel-nacos-provider/sayHello
对应的资源名有两种粒度,分别是:GET:http://sentinel-nacos-provider、GET:http://sentinel-nacos-provider/sayHello
因上节与Feign
集成测试过程中已对资源GET:http://sentinel-nacos-provider/sayHello
创建流控规则:
高频刷新:http://127.0.0.1:3000/sayHelloRest,验证RestTemplate
集成效果:
2.3.6 开启链路流控模式
从
Sentinel 1.6.3
版本开始,Sentinel Web filter
默认收敛所有URL
的入口context
,因此链路流控不生效。1.7.0
版本开始通过配置开启链路流控。
引入依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-web-servlet</artifactId>
</dependency>
配置开启链路流控:
- 方式一:
在配置文件新增spring.cloud.sentinel.web-context-unify=false
- 方式二:
添加一个配置类,自己构建CommonFilter
实例:
import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterContextConfig {
/**
* @NOTE 在spring-cloud-alibaba v2.1.1.RELEASE及前,sentinel1.7.0及后,关闭URL PATH聚合需要通过该方式,spring-cloud-alibaba v2.1.1.RELEASE后,可以通过配置关闭:spring.cloud.sentinel.web-context-unify=false
* 手动注入Sentinel的过滤器,关闭Sentinel注入CommonFilter实例,修改配置文件中的 spring.cloud.sentinel.filter.enabled=false
* 入口资源聚合问题:https://github.com/alibaba/Sentinel/issues/1024 或 https://github.com/alibaba/Sentinel/issues/1213
* 入口资源聚合问题解决:https://github.com/alibaba/Sentinel/pull/1111
*/
@Bean
public FilterRegistrationBean sentinelFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new CommonFilter());
registration.addUrlPatterns("/*");
// 入口资源关闭聚合
registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
registration.setName("sentinelFilter");
registration.setOrder(1);
return registration;
}
}
新建com/gm/sentinel_nacos_consumer/service/ChainService.java
:
public interface ChainService {
String message();
}
新建com/gm/sentinel_nacos_consumer/service/impl/ProviserServiceFallback.java
:
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.gm.sentinel_nacos_consumer.service.ChainService;
import org.springframework.stereotype.Service;
@Service
public class ChainServiceImpl implements ChainService {
@SentinelResource(value = "message")
public String message() {
return "hello world";
}
}
调用树如下图所示:
machine-root
/ \
/ \
/sayHelloDirect /sayHelloChain
/ \
/ \
DefaultNode(message) DefaultNode(message)
新建流控规则:
使用
@SentinelResource
注解,将方法message()
定义为资源
高频访问: http://127.0.0.1:3000/sayHelloChain,发现流控规则生效:
在后续章节补充基于
@SentinelResource
注解的异常处理
控制台输出:
高频访问:http://127.0.0.1:3000/sayHelloDirect,一切正常:
开启链路流控模式后,针对资源message
调用,链路入口/sayHelloChain
的流控规则生效,对来自 /sayHelloDirect
的调用漠不关心。
三、源码地址
源码地址:https://gitee.com/gm19900510/springboot-cloud-example