Java Sentinel 学习笔记
为什么需要 Sentinel
上周我们的订单服务在促销活动中挂了。不是什么复杂的原因,就是请求量突然上来,数据库连接池被打满,然后整个服务就像多米诺骨牌一样倒下去。
当时我就在想:如果有个东西能在流量太大的时候帮忙挡一挡,至少不让服务直接崩溃,该多好。
后来同事推荐了 Sentinel,阿里巴巴开源的。用了一段时间,感觉确实能解决问题。
Sentinel 是什么
Sentinel 是一个流量控制组件,主要做三件事:
- 流量控制:限制 QPS,超过阈值的请求直接拒绝
- 熔断降级:下游服务不稳定时,暂时切断调用
- 系统负载保护:根据系统整体负载来控制流量
说白了就是给服务加个保险丝。电流太大的时候先断开,保护整个电路不被烧坏。
快速上手
1. 添加依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.6</version>
</dependency>
2. 定义资源
Entry entry = null;
try {
entry = SphU.entry("hello");
// 你的业务逻辑
System.out.println("hello world");
} catch (BlockException e) {
// 被限流了
System.out.println("blocked!");
} finally {
if (entry != null) {
entry.exit();
}
}
这段代码的意思是:用 SphU.entry() 包裹住你想保护的代码块。如果流量超过阈值,就会抛出 BlockException,你在 catch 里处理被拒绝的请求。
3. 配置规则
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("hello");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(10); // 每秒最多 10 个请求
rules.add(rule);
FlowRuleManager.loadRules(rules);
这里设置 QPS 阈值为 10。超过这个数字的请求会被拒绝。
注解方式更简洁
手动 try-catch 有点啰嗦。Sentinel 提供了注解方式:
@SentinelResource(value = "getUser", blockHandler = "handleBlock")
public User getUser(String userId) {
return userDao.findById(userId);
}
public User handleBlock(String userId, BlockException e) {
return new User("default", "默认用户");
}
blockHandler 指定被限流时的兜底方法。注意这个方法的签名要和原方法保持一致,最后加一个 BlockException 参数。
熔断降级
除了限流,熔断也很重要。
假设你的服务依赖一个第三方 API,这个 API 偶尔会抽风,响应时间从 50ms 飙到 5s。如果不做熔断,你的服务线程池会被这些慢请求占满,新请求进不来。
@SentinelResource(value = "callExternal",
fallback = "fallback",
blockHandler = "handleBlock")
public String callExternal(String param) {
return externalService.call(param);
}
public String fallback(String param, Throwable t) {
return "服务暂时不可用,请稍后重试";
}
这里 fallback 和 blockHandler 的区别是:
blockHandler处理限流(BlockException)fallback处理业务异常(任何 Throwable)
控制台
Sentinel 有个 Dashboard,可以可视化地查看实时流量和配置规则。
启动方式:
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.6.jar
然后在应用里配置连接:
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8080
控制台可以看到每个资源的实时 QPS、拒绝数、响应时间等。也支持动态修改规则,不用重启服务。
一些踩过的坑
资源名要唯一。同一个资源名会被当作同一个限流目标,不管在哪个类里。
blockHandler 方法必须是 public。不然 Sentinel 调用不了。
finally 里一定要 exit()。不然 slot chain 不会被清理,会导致统计错误。
集群限流需要单独部署 token server。单机限流用本地规则就行,但如果要全局限流,得有个中心化的地方计数。
和 Hystrix 对比
之前用过 Hystrix。对比一下:
| 特性 | Sentinel | Hystrix |
|---|---|---|
| 状态 | 活跃开发中 | 停止维护(2018) |
| 限流 | 支持 QPS、线程数 | 只支持线程数 |
| 控制台 | 有,功能丰富 | 简单 |
| 学习曲线 | 较低 | 较高 |
| Spring Cloud 集成 | 有 starter | 有 starter |
Hystrix 已经进入维护模式了,Netflix 官方推荐用 resilience4j 或者 Sentinel。如果本来就在用阿里系的技术栈,选 Sentinel 挺自然的。
小结
Sentinel 解决的是一个很实际的问题:服务扛不住的时候怎么办。与其让服务崩溃,不如有选择地拒绝一些请求,保证剩下的请求能正常处理。
用了几个月下来,感觉 Sentinel 的设计挺务实的。没有太多花哨的概念,上手也快。控制台功能比较全,生产环境用起来挺方便。
如果你们服务也遇到过流量突增导致的问题,可以试试 Sentinel。配置几个规则,可能就能避免一次线上事故。
以上是个人使用体会,Sentinel 还有集群限流、网关限流、热点参数限流等高级功能,这里没展开。有兴趣可以看官方文档:https://sentinelguard.io