技术选型
1. Sentinel
- Sentinel 是阿里巴巴开源的限流、熔断、降级组件,旨在为分布式系统提供可靠的保护机制。
- 它设计用于解决高并发流量下的稳定性问题,并且支持与 Dubbo、Spring Cloud 等多种框架集成。
Sentinel 的功能:home | Sentinel
- 限流:支持基于 QPS(每秒查询数)、并发数量等条件的限流。支持滑动窗口、预热、漏桶等算法。
- 熔断降级:支持失败率、慢调用比例等指标触发熔断,并提供自动恢复机制。
- 热点参数限流:可以基于特定的参数进行限流,如限制特定用户 ID 的请求频率。
- 系统负载保护:可以根据系统的实际负载(如 CPU、内存)动态调整流量。
- 丰富的规则配置:通过配置中心或控制台动态调整限流和熔断规则。
优势
- 功能丰富、提供控制台、更新较频繁、社区活跃、文档清晰,能够快速入门上手。
2. Hystrix
Hystrix 是什么? https://github.com/Netflix/Hystrix
- Hystrix 是 Netflix 开源的一个限流、熔断和降级库。
- 它通过熔断器模式在检测到下游服务失败率过高时中断请求,以防止系统资源耗尽。
目前已经停止维护
3、Resilience4j
https://github.com/resilience4j/resilience4j
Resiience4j 是一个轻量级的熔断、限流、隔离、重试库,它设计灵感来源于 Netflix 的 Hystrix 框架,专门设计为响应式编程和 Java 8+风格下的熔断库。
优点: 它很轻量级,没有外部依赖,特别适合响应式编程风格,
缺点: 相比 Sentinel,功能相对较少,尤其在限流功能上不够丰富。
4、Guava RateLimiter
Ratelimiter 是 Guava 提供的一个限流工具,基于令牌桶算法实现,主要用于对系统的流量进行控制。
缺点:它仅支持限流,且功能相对单一,不能处理熔断等复杂场景。适合不需要复杂熔断或隔离功能的小型
项目,主要用于 单机系统 的流量控制。
5、Redisson RRateLimiter
Redisson 是一个基于 Redis 的 Java 驱动程序,它提供了丰富的分布式工具和数据结构,其中包括分布式
锁、计数器、队列、信号量,以及限流(Rate Limiter)等功能。
Redisson 提供了 Ratelimiter 接口来支持限流操作。你可以定义时间窗口内允许的最大请求数,超出限制
的请求将被阳塞或拒绝。它的限流基于 Redis 的计数器来控制访问频率,确保即使在多台服务器之间,也
能有效地限制请求速率。
总结
- Sentinel 是目前最成熟、功能最强大的分布式限流、熔断、降级组件,适合大规模分布式系统。
- Resilience4j 专为响应式编程设计,适合轻量级应用和微服务架构。
- Guava RateLimiter 适合单机环境下的简单限流需求。
- Redisson RRateLimiter 适合已有 Redis 基础设施的分布式系统,提供高效的分布式限流功能。
- Hystrix 已经停止维护,建议在新项目中避免使用,但对于遗留系统可以继续维护。
本次基于Sentinel 进行实现
实现
整合
1.下载jar包
2.将jar包放在任意非中文、不包含特殊字符的目录下 启动jar包
java -Dserver.port=8131 -jar sentinel-dashboard-1.8.6.jar
3.访问地址 http://localhost:8131 账号密码都是sentinel
建议 参考官方文档选择版本。由于 Spring Boot 3.0,Spring Boot 2.7~2.4和 2.4以下版本之间变化较大,目前企业级客户老项目相关 Spring Boot 版本仍停留在 Spring Boot 2.4 以下,为了同时满足存量用户和新用户不同需求,社区以 Spring Boot 3.0 和 2.4 分别为分界线,同时维护 2022.x、2021.x、2.2.x 三个分支迭代。
本项目 Spring Boot 用的是 2.7,因此使用 Sentinel starter 的版本 2021.0.5.0。在项目中引入依赖:
<!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2021.0.5.0</version>
</dependency>
注意没有配合nacos使用的话要在启动项上面加上访问的控制台地址,启动sentinel时要在java启动命令上加
java -Dserver.port=8131 -jar sentinel-dashboard-1.8.6.jar
整合包支持自动将所有的接口根据 ur 路径识别为资源。启动项目后,随便访问一个接口即可在控制台看效果
监听资源的几种方式
1.定义资源 代码自定义
通过代码定义资源更灵活
Entry entry = null;
// 务必保证finally会被执行
try {
// 资源名可使用任意有业务语义的字符串
entry = SphU.entry("自定义资源名");
// 被保护的业务逻辑
// do something...
} catch (BlockException e1) {
// 资源访问阻止,被限流或被降级
// 进行相应的处理操作
} finally {
if (entry != null) {
entry.exit();
}
}
2.通过注解
//listQuestionBankVOByPag控制台的资源名称
//handleBlockException 限流后走的方法名
//handleFallback 熔断后走的方法名称
@PostMapping("/list/page")
@SentinelResource(value = "listQuestionBankVOByPage",
blockHandler = "BlockException",
fallback = "Fallback")
public BaseResponse<Question> listQuestionBankVOByPage(
@RequestBody Question question) {}
public BaseResponse<Question> BlockException(@RequestBody Questiont question) {
// 限流操作
return ;
}
public BaseResponse<Question> handleFallback(@RequestBody Question question,
Throwable ex) {
// 可以返回本地数据或空数据
return null;
}
@sentinelResource 注解的配置优先于自动识别的配置。这意味,如果注解中定义了特定的限流或熔断策略,这些策略将覆盖默认的或自动识别的配置。
推荐开发模式:优先使用适配包来自动识别资源,然后能运用注解尽量运用注解,最后再选择主动编码定义资源。
定义规则
即限流规则 支持通过代码、控制台(推荐)、配置文件来定义规则。
- 代码定义限流规则
private static void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule1 = new FlowRule();
rule1.setResource(resource);
// Set max qps to 20
rule1.setCount(20);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}
2.通过控制台定义(推荐)
但通过控制台定义的规则,重启项目后就没了
一般推荐使用控制台来配置规则,但如果希望开发者更快启动和学习项目,可以通过编码定义规则,这样不用搭建控制台、而且每次启动项目都会确保规则被创建。
对某个接口进行限流或熔断
资源:listQuestion 接口
整个接口每秒钟不超过 10 次请求
熔断规则:
如果接口异常率超过 10%,或者慢调用(响应时长>3 秒)的比例大于 20%,触发 60 秒熔断
选择使用注解定义资源(即目标接口)+控制台定义规则
@SentinelResource使用注意事项annotation-support | Sentinel
给需要限流的接口添加@SentinelResource接口定义熔断限流后需要走的操作方法
启动项目,注意需加入JM 参数 -Dcsp.sentinel.dashboard.server=localhost:8131 指定控制台地址和端口。
启动项目后,随便访问个接口可在控制台看到自定义的资源名称即
3.添加规则:对要限流的接口新增限流
添加熔断
测试即可
对不同的IP限流或熔断
每个IP 地址每分钟允许调用的某个接口次数不能超过 60 次。
这是根据根据sentinel的热点限流机制实现的 详情参考:热点限流
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
- 商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制
- 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
注意 ;可以先给要限流的接口使用代码定义出来限流规则和熔断规则 可以避免重新启动需要重新定义规则的问题。
/**
* Sentinel 限流熔断规则管理器
*/
@Component
public class SentinelRulesManager {
@PostConstruct
public void initRules() throws Exception {
initFlowRules();
}
/**
* 初始化限流规则
*/
public void initFlowRules() {
// 单 IP 查看题目列表限流规则
ParamFlowRule rule = new ParamFlowRule(SentinelConstant.listQuestionVOByPage)
.setParamIdx(0) // 对第 0 个参数限流,即 IP 地址
.setCount(5) // 每分钟最多 60 次
.setDurationInSec(40); // 规则的统计周期为 60 秒
ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
}
}
而这里我们可以把ip地址作为参数进行统计,限制访问频率过高的即可
方法实现(主要代码)
/**
* 分页获取题目列表(封装类 - 限流版)
* @param questionQueryRequest
* @param request
* @return
*/
public BaseResponse<Page<QuestionVO>> listQuestionVOByPage(
@RequestBody QuestionQueryRequest questionQueryRequest,
HttpServletRequest request) {
// 基于 IP 限流 从这开始基于ip限流
String remoteAddr = request.getRemoteAddr();
Entry entry = null;
try {
entry = SphU.entry(SentinelConstant.listQuestionVOByPage, EntryType.IN, 1, remoteAddr);
// 被保护的业务逻辑
} catch (Throwable ex) {
// 业务异常
if (!BlockException.isBlockException(ex)) {
Tracer.trace(ex);
return ;
}
// 降级操作
if (ex instanceof DegradeException) {
return handleFallback(questionQueryRequest, request, ex);
}
// 限流操作
return ;
} finally {
if (entry != null) {
entry.exit(1, remoteAddr);
}
}
}
/**
* listQuestionVOByPageSentinel 降级操作:直接返回本地数据
*/
public BaseResponse<Page<QuestionVO>> handleFallback(@RequestBody QuestionQueryRequest questionQueryRequest,Throwable ex) {
// 可以返回本地数据或空数据
return ;
}
可以将Sentinel配置规则持续化到本地,这里不做赘述
具体事项方案可参考官方文档 introduction | Sentinel