1. Sentinel介绍
Github: Sentinel
随着微服务的流行,服务和服务之间的稳定性变得越来越重要。 Sentinel以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
Sentinel 具有以下特征:
- 丰富的应用场景: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。
- 完备的实时监控: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
- 完善的 SPI 扩展点: Sentinel 提供简单易用、完善的 SPI 扩展点。您可以通过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。
2. 搭建Sentinel控制台
sentinel控制台介绍文档:https://github.com/alibaba/Sentinel/wiki/控制台
2.1 下载控制台
下载地址:https://github.com/alibaba/Sentinel/releases
也可以使用源码进行编译打包
2.2 启动控制台
java -Dserver.port=8840 \
-Dcsp.sentinel.dashboard.server=localhost:8840 \
-Dproject.name=sentinel-dashboard \
-jar sentinel-dashboard.jar
2.3 使用控制台
控制台地址:http://localhost:8840
默认账号密码:sentinel / sentinel
3. 为项目整合sentinel
3.1 引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
3.2 配置属性
spring:
cloud:
sentinel:
transport:
# 指定sentinel控制台地址
dashboard: 127.0.0.1:8840
3.3 启动项目并查看sentinel控制台
启动后,访问几次项目的url。可以在sentinel控制台看到如下页面。
4. 限流规则
4.1 阈值类型
流量控制主要有两种统计类型:
- 线程数:通过统计当前资源的并发线程数来进行流控
- QPS:通过统计当前资源的QPS来进行流控
4.2 流控模式
- 直接:当前资源达到阈值,就限流自己
- 关联:当关联的资源达到阈值,就限流自己
- 链路:只记录指定链路上的流量
4.3 配置示例
当/echo-feign/hehe
资源的QPS达到1时,进行流控。如下图所示:
关联资源限流,当/sleep
资源的QPS达到1时,对/echo-feign/hehe
资源进行流控
5. SentinelResource注解
5.1 注解属性
基于Sentinel 1.6.3
属性 | 作用 | 是否必须 |
---|---|---|
value | 资源名称 | 是 |
entryType | entry类型,标记流量的方向,取值IN/OUT,默认是OUT | 否 |
blockHandler | 处理BlockException的函数名称。函数要求: 1. 必须是 public 2.返回类型与原方法一致 3. 参数类型需要和原方法相匹配,并在最后加 BlockException 类型的参数。 4. 默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 blockHandlerClass ,并指定blockHandlerClass里面的方法。 |
否 |
blockHandlerClass | 存放blockHandler的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同blockHandler。 | 否 |
fallback | 用于在抛出异常的时候提供fallback处理逻辑。fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。函数要求: 1. 返回类型与原方法一致 2. 参数类型需要和原方法相匹配,Sentinel 1.6开始,也可在方法最后加 Throwable 类型的参数。 3.默认需和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定fallbackClass里面的方法。 |
否 |
fallbackClass【1.6】 | 存放fallback的类。对应的处理函数必须static修饰,否则无法解析,其他要求:同fallback。 | 否 |
defaultFallback【1.6】 | 用于通用的 fallback 逻辑。默认fallback函数可以针对所有类型的异常(除了 exceptionsToIgnore 里面排除掉的异常类型)进行处理。若同时配置了 fallback 和 defaultFallback,以fallback为准。函数要求: 1. 返回类型与原方法一致 2. 方法参数列表为空,或者有一个 Throwable 类型的参数。 3. 默认需要和原方法在同一个类中。若希望使用其他类的函数,可配置 fallbackClass ,并指定 fallbackClass 里面的方法。 |
否 |
exceptionsToIgnore【1.6】 | 指定排除掉哪些异常。排除的异常不会计入异常统计,也不会进入fallback逻辑,而是原样抛出。 | 否 |
exceptionsToTrace | 需要trace的异常 | Throwable |
注:
1.6.0 之前的版本 fallback 函数只针对降级异常(
DegradeException
)进行处理,不能针对业务异常进行处理。若 blockHandler 和 fallback 都进行了配置,则被限流降级而抛出
BlockException
时只会进入blockHandler
处理逻辑。若未配置blockHandler
、fallback
和defaultFallback
,则被限流降级时会将BlockException
直接抛出。从 1.4.0 版本开始,注解方式定义资源支持自动统计业务异常,无需手动调用
Tracer.trace(ex)
来记录业务异常。Sentinel 1.4.0 以前的版本需要自行调用Tracer.trace(ex)
来记录业务异常。
5.2 SentinelResource示例:
public class TestService {
// 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 static 函数.
@SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})
public void test() {
System.out.println("Test");
}
// 原函数
@SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")
public String hello(long s) {
return String.format("Hello at %d", s);
}
// Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.
public String helloFallback(long s) {
return String.format("Halooooo %d", s);
}
// Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
public String exceptionHandler(long s, BlockException ex) {
// Do some log here.
ex.printStackTrace();
return "Oops, error occurred at " + s;
}
}
5.3 参考文档
6. RestTemplate整合Sentinel
给RestTemplate加上@SentinelRestTemplate
注解即可完成整合
代码示例如下:
@Configuration
@Slf4j
public class ContentCenterConfig {
@LoadBalanced
@Bean
@SentinelRestTemplate(blockHandlerClass = RestTemplateExceptionUtil.class, blockHandler = "block", //
fallbackClass = RestTemplateExceptionUtil.class, fallback = "fallback")
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static class RestTemplateExceptionUtil {
public static SentinelClientHttpResponse block(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution, BlockException ex) {
log.warn("Oops: " + ex.getClass().getCanonicalName());
return new SentinelClientHttpResponse("restTemplate block info");
}
public static SentinelClientHttpResponse fallback(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution, BlockException ex) {
log.warn("fallback: " + ex.getClass().getCanonicalName());
return new SentinelClientHttpResponse("restTemplate fallback info");
}
}
}
@SentinelRestTemplate
注解属性:
- blockHandlerClass:限流后处理的类
- blockHandler: 限流后处理的方法
- fallbackClsss:熔断后处理的类
- fallback:熔断后处理的方法
开启或关闭@SentinelRestTemplate注解
resttemplate:
sentinel:
# 为false时表示关闭@SentinelRestTemplate注解
enabled: true
7. Feign整合Sentinel
7.1 配置属性
feign:
sentinel:
enabled: true
7.2 使用fallback自定义流控处理
- 实现FeignClient
- 在
@FeignClient
注解的属性设置为实现类的class
@Component
public class EchoFeignClientFallback implements EchoFeignClient {
@Override
public String echo(String str) {
// 处理流控返回结果
return "feign-fallback-echo";
}
@Override
public String divide(Integer a, Integer b) {
// 处理流控返回结果
return "feign-fallback-divide";
}
}
@FeignClient(name = "user-center", fallback = EchoFeignClientFallback.class)
public interface EchoFeignClient {
/**
* 请求http://user-center/echo/{str}服务
*
* @param str
* @return
*/
@GetMapping("/echo/{str}")
String echo(@PathVariable String str);
/**
* 请求http://user-center/divide服务
*
* @param a
* @param b
* @return
*/
@GetMapping("/divide")
String divide(@RequestParam("a") Integer a, @RequestParam Integer b);
}
7.3 使用fallbackFactory自定义流控处理
@Component
@Slf4j
public class EchoFeignClientFallbackFactory implements FallbackFactory<EchoFeignClient> {
@Override
public EchoFeignClient create(Throwable throwable) {
log.warn("远程调用被限流或降级了", throwable);
return new EchoFeignClient() {
@Override
public String echo(String str) {
// 处理流控返回结果
return "feign-fallback-echo-1";
}
@Override
public String divide(Integer a, Integer b) {
// 处理流控返回结果
return "feign-fallback-divide-1";
}
};
}
}
@FeignClient(name = "user-center", fallbackFactory = EchoFeignClientFallbackFactory.class)
public interface EchoFeignClient {
/**
* 请求http://user-center/echo/{str}服务
*
* @param str
* @return
*/
@GetMapping("/echo/{str}")
String echo(@PathVariable String str);
/**
* 请求http://user-center/divide服务
*
* @param a
* @param b
* @return
*/
@GetMapping("/divide")
String divide(@RequestParam("a") Integer a, @RequestParam Integer b);
}
8. Sentinel使用总结
整合方式 | 使用方式 | 使用方法 |
---|---|---|
编码方式 | API | try...catch...finally... |
注解方式 | @SentinelResource | blockHandler/fallback |
RestTemplate | @SentinelRestTemplate | blockHandler/fallback |
Feign | feign.sentinel.enabled = true | fallback/fallbackFactory |