简述
面向分布式、多语言异构化服务架构的流量治理组件,实现熔断与限流
安装
控制台面板使用docker
进行安装,默认用户名和密码都是sentinel
docker run \
--name sentinel \
-p 8858:8858 \
-p 8719:8719 \
-d \
-e JVM_XMX=256m \
bladex/sentinel-dashboard
整合使用
将微服务纳入Sentinel
监控
这里需要同时使用Nacos
才能正常工作
pom依赖
<properties>
<alibaba.cloud.version>2023.0.1.0</alibaba.cloud.version>
</properties>
<dependencies>
<!-- Sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>${alibaba.cloud.version}</version>
</dependency>
<!-- Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${alibaba.cloud.version}</version>
</dependency>
</dependencies>
配置文件
spring:
application:
name: sentinel-learn-serivce
cloud:
nacos:
discovery:
server-addr: 192.168.102.131:8848 # nacos的地址
username: nacos
password: nacos
sentinel:
transport:
dashboard: 192.168.102.131:8858 # Sentinel控制台地址
port: 8719 # Sentinel控制台API命令交互地址
server:
port: 8401
接口控制器
package com.xlyo.sentinellearnserivce.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 流量控制测试
*/
@RestController
public class FlowLimitController {
@GetMapping("/testA")
public String testA() {
return "test => A";
}
@GetMapping("/testB")
public String testB() {
return "test => B";
}
}
测试
现在访问接口
GET http://localhost:8401/testA
GET http://localhost:8401/testB
然后查看Sentinel
控制台页面
GET http://192.168.102.131:8858/#/dashboard/home
流控模式
流控模式有三种模式
直接
关联
链路
直接模式
设置单机阈值为2
,快速访问多次接口,就会看到直接返回的限流提示
关联模式
当关联的资源达到阈值时,就限流自己(A关联的资源B达到阈值后,限流A自己)
使用JMeter
频繁请求接口/testB
在JMeter
请求的时候请求接口/testA
,会发现被限流
链路模式
来自不同链路的请求对同一个目标访问时,实施针对性的不同限流措施(C请求来访问就限流,D请求来访问就通过)
新增服务层
package com.xlyo.sentinellearnserivce.service;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import org.springframework.stereotype.Service;
@Service
public class FlowLimitService {
@SentinelResource("common")
public void common() {
System.out.println("FlowLimitService => Come in");
}
}
配置文件
spring:
...
sentinel:
...
web-context-unify: false # controller层的方法对service层调用不认为是同一个根链路
上述配置开启后,其每个接口根路径会分开
接口新增
package com.xlyo.sentinellearnserivce.controller;
import com.xlyo.sentinellearnserivce.service.FlowLimitService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 流量控制测试
*/
@RestController
public class FlowLimitController {
...
/**
* C和D都访问flowLimitService.common()方法,阈值达到后对C限流,对D不管
*/
@Autowired
private FlowLimitService flowLimitService;
@GetMapping("/testC")
public String testC() {
flowLimitService.common();
return "test => C";
}
@GetMapping("/testD")
public String testD() {
flowLimitService.common();
return "test => D";
}
}
配置规则
访问测试
现在快速多次访问接口/testC
会被限流,而/testD
则不会
流控效果
其有三种流控效果
快速失败(默认效果)
预热
排队等待
快速失败效果
直接失败,返回限流信息
预热效果
当流量突然增大的时候,我们常会希望系统从空闲状态到繁忙状态的切换时间长一点
其公式为
threshold:单机阈值
coldFactor:冷却因子,默认为3
示例如下配置
则说明
5秒以内,单机阈值为
10/3≈3
5秒过后,单机阈值慢慢到
10
现在快速多次访问接口/testB
,5秒内有几次会出现限流提示,5秒后就很难出现限流提示了
排队等待
主要用于处理间隔性突发流量,在某一秒有大量请求,之后几秒处于空闲状态,希望在接下来的空闲时间进行匀速处理
注意:匀速排队模式暂时不支持 QPS > 1000 的场景
接口
/**
* 排队等待测试
*/
@GetMapping("/testE")
public String testE() {
System.out.println(Instant.now().toEpochMilli() + " => 排队等待...");
return "test => E";
}
配置规则
JMeter
1秒打20
个请求
测试
启动JMeter
,观察控制台输出
可以看到以1秒/个
的速率进行处理,只有前11
个请求到达了,其余的全部超时阻止
熔断规则
其有3种熔断策略
慢调用比例
异常比例
异常数
异常数慢调用比例
1000ms
持续进入5个请求,其响应时间(RT)大于200ms
的请求数量大于比例阈值触发降级,断路器打开
5s
后尝试继续统计响应时间,小于则恢复,大于则继续熔断,等待5s
接口
/**
* 慢调用比例测试
*/
@GetMapping("/testF")
public String testF() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {}
System.out.println(" => 慢调用比例");
return "test => F";
}
JMeter
每秒10个请求,无限请求
测试
启动JMeter
,之后手动访问接口/testF
异常比例
类似于慢调用比例,在单位统计时长,接口出现异常超过比例阈值就触发降级
异常数
类似于异常数,在单位统计时长,接口出现异常数量超过设定数量就触发降级
@SentinelResource
其是一个流量防卫组件注解,用于指定防卫资源,对配置的资源进行流量控制、熔断降级等功能
blockHandler
主要针对sentinel
配置后出现的违规情况处理
接口
/**
* SentinelResource测试1
*/
@SentinelResource(value = "bySentinelResource", blockHandler = "handleBlockHandler")
@GetMapping("/test/resource")
public String testResource() {
return "test => resource";
}
public String handleBlockHandler(BlockException ex) {
return "服务不可用(testResource)";
}
配置流控
此时簇点链路就有上述注解里指定的名称了
之后配置流控规则
测试
快速访问可以看见出现了自定义限流提示
fallback
主要针对程序异常JVM抛出的异常服务降级
接口
/**
* SentinelResource测试2
*/
@SentinelResource(
value = "doActionSentinelResource",
blockHandler = "doActionBlockHandler",
fallback = "doActionFallback"
)
@GetMapping("/test/do/{p1}")
public String doAction(@PathVariable("p1") Integer p1) {
if (p1 == 0) throw new RuntimeException("p1不能为0");
return "doAction => " + p1;
}
// 限流走这
public String doActionBlockHandler(@PathVariable("p1") Integer p1, BlockException ex) {
System.err.println("自定义限流 => " + ex); // FlowException
return "服务不可用(doAction)";
}
// 程序异常走这
public String doActionFallback(@PathVariable("p1") Integer p1, Throwable ex) {
System.err.println("程序发生异常 => " + ex); // p1不能为0
return
配置
测试
blockHandler
GET http://localhost:8401/test/do/114514
fallback
GET http://localhost:8401/test/do/0
热点规则
热点及经常访问的数据,我们希望统计或限制某个热点数据中的访问频次最高的TopN数据,并对其访问进行限流或其它操作
接口
/**
* 热点参数限流测试
* 只要参数p1的QPS超过1秒,马上限流,参数p2不进行处理
*/
@SentinelResource(value = "testHotKey", blockHandler = "dealHandler_testHotKey")
@GetMapping("/testHotKey")
public String testHotKey(
@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2
) {
return "test => hotKey";
}
public String dealHandler_testHotKey(String p1, String p2, BlockException ex) {
return "dealHandler_testHotKey => p1 = " + p1 + ", p2 = " + p2;
}
配置
参数索引:从
0
开始,这里为参数p1
只要参数带p1,且超过阈值就会限流
测试
GET http://localhost:8401/testHotKey?p1=abc
GET HTTP://localhost:8401/testHotKey?p1=abc&p2=xyz
GET http://localhost:8401/testHotKey?p2=xyz
参数例外项
当参数为某个特定值时,采用另一种限流阈值
这里是当参数p1
为no
时,QPS达到200
才会限流
授权规则
根据调用接口的来源判断是否运行执行本次请求
这里提供了2种授权规则
白名单:放行
黑名单:禁止
接口
/**
* 授权规则测试
*/
@GetMapping("/testEmpower")
public String testEmpower() {
return "test => empower";
}
RequestOriginParser
package com.xlyo.sentinellearnserivce.handler;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
@Component
public class MyRequestOriginParser implements RequestOriginParser {
/**
* 获取用来进行授权规则比较的值
*/
@Override
public String parseOrigin(HttpServletRequest request) {
return request.getParameter("server-name");
}
}
配置
测试
GET http://localhost:8401/testEmpower?server-name=test21
GET http://localhost:8401/testEmpower?server-name=test
GET http://localhost:8401/testEmpower?server-name=test2
规则持久化
目前配置的规则在重启或微服务重启后会丢失配置,生成环境中需要将配置规则持久化
我们需要将配置存入Nacos中,只要Nacos
中的配置不删除,规则就持续有效
引入新依赖
<!-- Sentinel 规则持久化 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.8.7</version>
</dependency>
配置文件
spring:
application:
name: sentinel-learn-serivce
cloud:
nacos:
discovery:
server-addr: 192.168.102.131:8848 # nacos的地址
username: nacos
password: nacos
sentinel:
transport:
dashboard: 192.168.102.131:8858 # Sentinel控制台地址
port: 8719 # Sentinel控制台API命令交互地址
web-context-unify: false # controller层的方法对service层调用不认为是同一个根链路
datasource:
ds1: # 限流类型
nacos:
server-addr: 192.168.102.131:8848 # nacos的地址
username: nacos
password: nacos
data-id: ${spring.application.name}-flow
group-id: DEFAULT_GROUP
data-type: json
rule-type: flow # 此处为流控规则
# ds2: # 限流类型
# nacos:
# server-addr: 192.168.102.131:8848 # nacos的地址
# username: nacos
# password: nacos
# data-id: ${spring.application.name}-degrade
# group-id: DEFAULT_GROUP
# data-type: json
# rule-type: degrade # 此处为熔断规则
server:
port: 8401
这里的rule-type
为限流类型,具体定义在com.alibaba.cloud.sentinel.datasource.RuleType
下
FLOW("flow", FlowRule.class), // 流控规则
DEGRADE("degrade", DegradeRule.class), // 熔断规则
PARAM_FLOW("param-flow", ParamFlowRule.class), // 热点规则
SYSTEM("system", SystemRule.class), // 系统保护规则
AUTHORITY("authority", AuthorityRule.class), // 访问控制规则
GW_FLOW("gw-flow", "com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule"),
GW_API_GROUP("gw-api-group", "com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition");
Nacos配置
[
{
"resource":"/testA", // 资源名称
"limitApp":"default", // 来源应用
"grade":1, // 阈值类型,0为线程数,1为QPS
"count":1, // 单机阈值
"strategy":0, // 流控模式,0为直接,1为关联,2为链路
"controlBehavior":0, // 流控效果,0为快速失败,1为WarmUp,2为排队等待
"clusterMode":false // 是否集群
}
]
整合Open Feign统一fallback
整合Open Feign
统一实现fallback
服务降级
整合Gateway服务限流
整合Spring Gateway
实现服务限流