SpringCloudAlibaba-熔断_限流(Sentinel)
Sentinel
概述
https://github.com/alibaba/sentinel
https://sentinelguard.io/zh-cn/docs/introduction.html
有Hystrix,为什么需要Sentinel?
Hystrix
1、Hystrix需要自己搭建监控平台
2、Hystrix没有一套web界面给我们进行 更细粒度化的配置(流量监控、速率监控、服务熔断、服务降级...)
Sentinel
1、单独的组件
2、界面化的细粒度配置
what
1、Sentinel 是面向分布式、多语言异构化服务架构的 流量治理组件;
主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性;
Sentinel基本概念
资源
资源是 Sentinel 的关键概念;
可以是 Java 应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码;
只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来;
大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源;
规则
围绕资源的实时状态设定的规则;
可以包括流量控制规则、熔断降级规则以及系统保护规则;
所有规则可以动态实时调整;
How
安装Sentinel控制台
Sentinel 的使用可以分为两个部分:
- 核心库(Java 客户端):不依赖任何框架/库,能够运行于 Java 8 及以上的版本的运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持(见 主流框架适配)。
- 控制台(Dashboard):Dashboard 主要负责管理推送规则、监控、管理机器信息等。
https://github.com/alibaba/Sentinel/releases
运行前提
jdk8以上环境、8080端口不能被占用
java -jar xxxx.jar
访问 http://localhost:8080/#/login
默认 账号/密码 sentinel/sentinel
创建8401微服务 且被 Sentinel监控
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
server:
port: 8401
spring:
application:
name: sentinel
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080 #sentinel dashboard地址
port: 8719 #默认端口,如被占用,从8719+1开始,直至找到未被使用的端口
management:
endpoints:
web:
exposure:
include: '*'
@EnableDiscoveryClient
@SpringBootApplication
public class Sentinel8401 {
public static void main(String[] args) {
SpringApplication.run(Sentinel8401.class, args);
}
}
@RestController
public class FlowLimitController {
@GetMapping(value = "/testA")
public String testA(){
return "testA";
}
@GetMapping(value = "/testB")
public String testB(){
return "testB";
}
}
Sentinel采用懒加载,只有访问服务,在Sentinel dashboard才能看到;
流控规则(流量控制)
https://sentinelguard.io/zh-cn/docs/flow-control.html
概念


阈值类型
QPS:
每秒请求数;
当QPS达到阈值,进行限流;
线程数:
支持实际处理任务的线程数;
当实际请求的线程数 > 支持的线程数,进行限流;
流控模式
直接(默认)
直接 -> 快速失败 (系统默认)

/testB达到阈值,进行限流
关联

testA达到阈值,限流testB
流控效果
快速失败
直接抛异常
warm up
阈值/coldFactor(默认3),经过预热时长,达到阈值

10/3 经过5秒 达到阈值10
排队等待
匀速排队;
严格控制请求通过的时间间隔;
对应 漏桶算法;

每秒均匀处理一个请求
降级规则
https://sentinelguard.io/zh-cn/docs/circuit-breaking.html
概念

Sentinel的断路器没有 半开状态;
How
RT

testC 必须在200ms内处理响应,否则开启熔断,经过2s窗口期,关闭熔断
异常比例

testC 异常比例>0.2,开启熔断,经过2s窗口期,关闭熔断;
异常数

testC 单位时间内异常次数达到3次,开启熔断,经过61s窗口期,关闭熔断;
热点Key限流
https://sentinelguard.io/zh-cn/docs/parameter-flow-control.html
@SentinelResource的blockHandler 仅处理后台配置出现的异常;
How
@GetMapping(value = "/testD")
@SentinelResource(value = "testDResource", blockHandler = "handle_testD")
public String testD(@RequestParam(value = "p1", required = false) String p1, @RequestParam(value = "p2", required = false) String p2){
return "---testD";
}
public String handle_testD(String p1,String p2, BlockException blockException){
return "o(╥﹏╥)o handle_testD";
}
http://localhost:8401/testD?p1=a 当p1参数达到阈值,开启熔断
http://localhost:8401/testD?p1=a&p2=b 当p1参数达到阈值,开启熔断
http://localhost:8401/testD?p2=b 无论是否达到阈值,都不会开启熔断

testDResource 资源下标为0的参数 QPS>1 时,熔断开启,服务降级处理handle_testD,经过1s窗口期,熔断关闭;
参数例外项
当某个参数值是指定值时,期望该参数的阈值可以提升至某个值;

testDResource 资源下标为0的参数 QPS>1 时,熔断开启;
当 下标为0的参数值=5,QPS<=200;
系统规则
https://sentinelguard.io/zh-cn/docs/system-adaptive-protection.html
阈值类型
- Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的
maxQps * minRt计算得出。设定参考值一般是CPU cores * 2.5。 - CPU usage(1.5.0+ 版本):当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0)。
- RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。

谨慎使用
@SentinelResource
资源名称限流 + blockHandle
@GetMapping(value = "/byResource")
@SentinelResource(value = "byResourceResource", blockHandler = "handle_byResource")
public String testE(@RequestParam(value = "p1", required = false) String p1){
return "---testD";
}
public String handle_byResource(String p1, BlockException blockException){
return "o(╥﹏╥)o handle_byResource" + blockException.getClass().getCanonicalName();
}

byResourceResource QPS>2时开启熔断,handle_byResource进行熔断处理;
URL限流 + blockHandle
@GetMapping(value = "/byURL")
@SentinelResource(value = "byURL")
public String byURL(@RequestParam(value = "p1", required = false) String p1){
return "---byURL";
}

/byURL QPS>1时,开启熔断;
目前存在的问题
1、系统默认的,没有体现业务要求
2、自定义处理 与 业务代码耦合
3、每个业务方法都要建一个 处理方法,代码臃肿
4、全局统一的处理没有体现
自定义BlockHandler
public class MyBlockHandler {
public static String handler1(BlockException blockException){
return "---handler1 o(╥﹏╥)o";
}
public static String handler2(String p1, BlockException blockException){
return "---handler2 o(╥﹏╥)o";
}
}
@GetMapping(value = "/myBlockHandle")
@SentinelResource(value = "myBlockHandleResource" ,blockHandlerClass = MyBlockHandler.class, blockHandler = "handler2")
public String myBlockHandle(@RequestParam(value = "p1", required = false) String p1){
return "---myBlockHandle";
}

@SentinelResource详解
https://sentinelguard.io/zh-cn/docs/annotation-support.html
服务熔断
nacos+ribbon+sentinel
provider
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
server:
port: 9001
spring:
application:
name: nacos-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848
management:
endpoints:
web:
exposure:
include: '*'
@SpringBootApplication
@EnableDiscoveryClient
public class NacosProviderDemoApplication9001 {
public static void main(String[] args) {
SpringApplication.run(NacosProviderDemoApplication9001.class, args);
}
}
@RestController
public class EchoController {
@Value(value = "${server.port}")
private String port;
@GetMapping(value = "/echo/{string}")
public String echo(@PathVariable String string) {
return "Hello Nacos Discovery " + string + "port:" + port;
}
}
consumer
<!-- spring-cloud-starter-alibaba-nacos-discovery 集成了ribbon -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
server:
port: 84
spring:
application:
name: nacos-ribbon-cosumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080 #sentinel dashboard地址
port: 8719 #默认端口,如被占用,从8719+1开始,直至找到未被使用的端口
service-url: #消费者要调用的服务名称
nacos-user-service: http://nacos-provider
@Configuration
public class Config {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
@EnableDiscoveryClient
@SpringBootApplication
public class NacosConsumer84 {
public static void main(String[] args) {
SpringApplication.run(NacosConsumer84.class, args);
}
}
@RestController
public class Controller {
@Value(value = "${service-url.nacos-user-service}")
private String serviceUrl;
@Autowired
private RestTemplate restTemplate;
@GetMapping(value = "/nacos/consumer/{s}")
@SentinelResource(
value = "test",
fallback = "handleTestFallback", //fallback负责运行时异常
blockHandler = "handleTestBlockHandler", //blockHandler负责sentinel平台配置规则
exceptionsToIgnore = {NullPointerException.class} //指定异常将不会被 fallback或blockHandler
)
public String test(@PathVariable(value = "s") String s){
if (s.equalsIgnoreCase("a")){
throw new IllegalArgumentException("IllegalArgumentException...");
}
else if (s.equalsIgnoreCase("b")){
throw new NullPointerException("NullPointerException...");
}
return restTemplate.getForObject(serviceUrl + "/echo/"+s, String.class);
}
public String handleTestFallback(String s, Throwable e){
return "handleTestFallback...s="+s + "msg="+e.getMessage();
}
public String handleTestBlockHandler(String s, BlockException e){
return "handleTestBlockHandler...s="+s + "msg="+e.getMessage();
}
}
nacos+openfeign+sentinel
provider
如上
consumer
<dependencies>
<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>
<!-- spring-cloud-starter-alibaba-nacos-discovery 集成了ribbon -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</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-sentinel</artifactId>
</dependency>
</dependencies>
server:
port: 85
spring:
application:
name: nacos-ribbon-cosumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080 #sentinel dashboard地址
port: 8719 #默认端口,如被占用,从8719+1开始,直至找到未被使用的端口
feign:
sentinel:
enabled: true #激活sentinel对feign支持
@FeignClient(value = "nacos-provider", fallback = MyFeignClientFallBack.class)
public interface MyFeignClient {
@GetMapping(value = "/echo/{s}")
String test(@PathVariable(value = "s") String s);
}
@Component
public class MyFeignClientFallBack implements MyFeignClient{
public String test(String s) {
return "myFeignClientFallBack...";
}
}
@RestController
public class Controller {
@Autowired
private MyFeignClient myFeignClient;
@GetMapping(value = "/nacos/consumer/{s}")
@SentinelResource(
value = "test",
fallback = "handleTestFallback", //fallback负责运行时异常
blockHandler = "handleTestBlockHandler", //blockHandler负责sentinel平台配置规则
exceptionsToIgnore = {NullPointerException.class} //指定异常将不会被 fallback或blockHandler
)
public String test(@PathVariable(value = "s") String s){
if (s.equalsIgnoreCase("a")){
throw new IllegalArgumentException("IllegalArgumentException...");
}
else if (s.equalsIgnoreCase("b")){
throw new NullPointerException("NullPointerException...");
}
return myFeignClient.test(s);
}
public String handleTestFallback(String s, Throwable e){
return "handleTestFallback...s="+s + "msg="+e.getMessage();
}
public String handleTestBlockHandler(String s, BlockException e){
return "handleTestBlockHandler...s="+s + "msg="+e.getMessage();
}
}
熔断框架对比


sentinel规则持久化
why
应用重启后,sentinel配置的规则将会全部消失;
How
将sentinel配置的规则持久化到nacos
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- sentinel规则持久化到nacos-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
server:
port: 8401
spring:
application:
name: sentinel
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080 #sentinel dashboard地址
port: 8719 #默认端口,如被占用,从8719+1开始,直至找到未被使用的端口
datasource: #sentinel规则持久化到nacos数据库配置
ds1:
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name} #应用名称
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
management:
endpoints:
web:
exposure:
include: '*'
@GetMapping(value = "/byResource")
public String testE(@RequestParam(value = "p1", required = false) String p1){
return "---testD";
}
@EnableDiscoveryClient
@SpringBootApplication
public class Sentinel8401 {
public static void main(String[] args) {
SpringApplication.run(Sentinel8401.class, args);
}
}
nacos-server后台规则配置


浙公网安备 33010602011771号