最佳实践
本章总结了 Sentinel 在生产环境中的最佳实践,帮助你更好地使用 Sentinel 保障服务的稳定性。
整体规划
1. 分层保护策略
在生产环境中,建议采用多层保护策略:
┌─────────────────────┐
│ 系统保护规则 │ ← 最后一道防线
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ 熔断降级规则 │ ← 处理不稳定依赖
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ 流量控制规则 │ ← 控制入口流量
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ 业务逻辑 │
└─────────────────────┘
每一层保护都有其职责:
- 流量控制规则:保护系统不被超出处理能力的流量冲垮
- 熔断降级规则:处理依赖服务不稳定的情况
- 系统保护规则:兜底保护,防止系统整体过载
2. 资源命名规范
良好的资源命名可以提高可维护性和可读性:
// 推荐:使用有业务含义的名称
@SentinelResource(value = "order:service:createOrder")
public Order createOrder(OrderRequest request) { ... }
@SentinelResource(value = "user:service:getUser")
public User getUser(String userId) { ... }
// 不推荐:使用无意义的名称
@SentinelResource(value = "method1")
public void process() { ... }
命名规范建议:
- 使用模块前缀:
module:service:method - 使用动词:
create、get、update、delete - 区分读写操作:
read:、write:
3. 合理划分资源粒度
资源粒度的划分需要在保护效果和管理复杂度之间取得平衡:
// 粒度太粗:无法精细化控制
@SentinelResource(value = "orderService")
public class OrderService { ... }
// 粒度太细:管理复杂
@SentinelResource(value = "orderService.createOrder.step1")
public void step1() { ... }
@SentinelResource(value = "orderService.createOrder.step2")
public void step2() { ... }
// 推荐:按接口级别划分
@SentinelResource(value = "order:create")
public Order createOrder() { ... }
@SentinelResource(value = "order:query")
public Order queryOrder() { ... }
流量控制最佳实践
1. 阈值设置
阈值的设置应该基于压测数据和实际业务情况:
QPS 阈值:
// 推荐:设置为压测最大 QPS 的 70%-80%
// 假设压测最大 QPS 为 1000,设置 700-800
FlowRule rule = new FlowRule();
rule.setResource("api:order:create");
rule.setCount(800);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
线程数阈值:
// 推荐:设置为线程池大小的 70%-80%
// 或者根据依赖服务的处理能力设置
FlowRule rule = new FlowRule();
rule.setResource("external:payment");
rule.setCount(50); // 限制并发调用数
rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);
2. 流控效果选择
根据业务场景选择合适的流控效果:
| 场景 | 推荐效果 | 说明 |
|---|---|---|
| API 网关 | 直接拒绝 | 快速失败,让客户端重试或降级 |
| 秒杀系统 | Warm Up | 预热系统,避免冷启动问题 |
| 消息处理 | 匀速排队 | 削峰填谷,平滑处理 |
| 内部服务 | 直接拒绝 | 快速响应,避免资源占用 |
Warm Up 配置示例:
FlowRule rule = new FlowRule();
rule.setResource("seckill:api");
rule.setCount(1000);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
rule.setWarmUpPeriodSec(30); // 30秒预热时间
匀速排队配置示例:
FlowRule rule = new FlowRule();
rule.setResource("message:process");
rule.setCount(100);
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
rule.setMaxQueueingTimeMs(500); // 最大排队 500ms
3. 关联限流使用场景
关联限流适用于资源争抢场景:
// 场景:数据库读写争抢,写操作优先
// 当写操作 QPS 过高时,限制读操作
FlowRule writeRule = new FlowRule();
writeRule.setResource("db:write");
writeRule.setCount(100);
writeRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
FlowRule readRule = new FlowRule();
readRule.setResource("db:read");
readRule.setCount(200);
readRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
readRule.setStrategy(RuleConstant.STRATEGY_RELATE);
readRule.setRefResource("db:write"); // 关联写操作
// 当写操作 QPS 接近 100 时,开始限制读操作
熔断降级最佳实践
1. 阈值设置原则
慢调用阈值:
// 设置为 P99 响应时间的 2-3 倍
// 假设 P99 响应时间为 200ms,设置 400-600ms
DegradeRule rule = new DegradeRule("external:service");
rule.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType());
rule.setCount(500); // 500ms
rule.setSlowRatioThreshold(0.5); // 50% 慢调用比例
异常比例阈值:
// 根据业务容错能力设置
// 一般设置为 30%-50%
DegradeRule rule = new DegradeRule("external:service");
rule.setGrade(CircuitBreakerStrategy.ERROR_RATIO.getType());
rule.setCount(0.3); // 30% 异常比例
2. 熔断时长设置
熔断时长的设置需要考虑下游服务的恢复时间:
// 推荐:30-60 秒
// 太短:下游可能还没恢复
// 太长:影响服务可用性
rule.setTimeWindow(30);
对于不同类型的服务,可以调整:
- 数据库服务:10-30 秒(可能快速恢复)
- 外部 API:30-60 秒(恢复时间不确定)
- 下游微服务:20-40 秒
3. 降级处理策略
提供有意义的降级响应:
@Service
public class ProductService {
@SentinelResource(value = "getProductDetail",
fallback = "getProductDetailFallback")
public ProductDetail getProductDetail(String productId) {
return remoteProductService.getDetail(productId);
}
// 降级策略:返回缓存的精简数据
public ProductDetail getProductDetailFallback(String productId, Throwable t) {
// 记录日志
log.warn("获取商品详情降级, productId={}, error={}", productId, t.getMessage());
// 尝试从缓存获取
ProductDetail cached = cacheService.getProductDetailCache(productId);
if (cached != null) {
return cached;
}
// 返回精简数据
return ProductDetail.builder()
.id(productId)
.name("商品信息暂时不可用")
.available(false)
.build();
}
}
4. 区分 blockHandler 和 fallback
@SentinelResource(value = "processOrder",
blockHandler = "handleBlock", // 限流/熔断时调用
fallback = "handleFallback") // 业务异常时调用
public Order processOrder(OrderRequest request) {
// 业务逻辑
}
// 限流或熔断时的处理
public Order handleBlock(OrderRequest request, BlockException e) {
return Order.blocked("系统繁忙,请稍后重试");
}
// 业务异常时的处理
public Order handleFallback(OrderRequest request, Throwable t) {
log.error("订单处理异常", t);
return Order.failed("服务暂时不可用");
}
系统保护最佳实践
1. 组合使用多种规则
private void initSystemRules() {
List<SystemRule> rules = new ArrayList<>();
// CPU 保护(最灵敏)
SystemRule cpuRule = new SystemRule();
cpuRule.setHighestCpuUsage(0.8); // CPU 80%
// 平均 RT 保护
SystemRule rtRule = new SystemRule();
rtRule.setAvgRt(1000); // 平均响应时间 1 秒
// 并发线程保护
SystemRule threadRule = new SystemRule();
threadRule.setMaxThread(200); // 最大并发 200
// 入口 QPS 保护
SystemRule qpsRule = new SystemRule();
qpsRule.setQps(5000); // 总入口 QPS 5000
rules.add(cpuRule);
rules.add(rtRule);
rules.add(threadRule);
rules.add(qpsRule);
SystemRuleManager.loadRules(rules);
}
2. 入口流量标记
系统保护规则只对入口流量生效,需要正确标记:
// 入口流量(Web 请求)
@GetMapping("/api/users/{id}")
public User getUser(@PathVariable String id) {
// Web 适配器会自动标记为入口流量
return userService.getUser(id);
}
// 内部调用
public void internalCall() {
// 手动定义资源时,默认为出口流量,不受系统保护
Entry entry = SphU.entry("internalResource");
// ...
}
// 如果需要系统保护,需要显式指定
public void protectedInternalCall() {
Entry entry = SphU.entry("internalResource", EntryType.IN); // 标记为入口
// ...
}
规则管理最佳实践
1. 使用动态规则源
生产环境必须使用动态规则源,支持规则的动态更新:
spring:
cloud:
sentinel:
datasource:
flow:
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name}-flow-rules
groupId: SENTINEL_GROUP
rule-type: flow
degrade:
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name}-degrade-rules
groupId: SENTINEL_GROUP
rule-type: degrade
system:
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name}-system-rules
groupId: SENTINEL_GROUP
rule-type: system
2. 规则版本管理
在 Nacos 等配置中心中,可以利用版本管理功能:
- 保留历史规则配置
- 支持规则回滚
- 记录规则变更历史
3. 规则审计
建立规则变更审批流程:
- 在测试环境验证规则
- 通过代码评审
- 分批次灰度发布
- 监控生效情况
监控告警最佳实践
1. 监控关键指标
// 注册状态变化监听器
EventObserverRegistry.getInstance().addStateChangeObserver("alert",
(prevState, newState, rule, snapshotValue) -> {
if (newState == CircuitBreaker.State.OPEN) {
// 发送告警
alertService.sendAlert(
"熔断器打开: " + rule.getResource(),
"触发值: " + snapshotValue
);
}
});
2. 接入监控平台
将 Sentinel 监控数据接入 Prometheus、Grafana 等监控平台:
// 通过 HTTP API 获取监控数据
curl http://localhost:8719/metric?startTime=xxx&endTime=xxx
// 或自定义 MetricExporter
public class SentinelMetricExporter {
@Scheduled(fixedRate = 1000)
public void exportMetrics() {
for (ResourceWrapper resource : ResourceWrapperRegistry.getAll()) {
ClusterNode node = ClusterBuilderSlot.getClusterNode(resource.getName());
if (node != null) {
// 导出指标到监控系统
Metrics.export("sentinel_pass_qps", node.passQps());
Metrics.export("sentinel_block_qps", node.blockQps());
Metrics.export("sentinel_rt", node.avgRt());
}
}
}
}
3. 告警规则配置
建议配置的告警项:
| 告警项 | 阈值建议 | 说明 |
|---|---|---|
| 限流触发率 | > 10% | 大量请求被限流 |
| 熔断器打开 | 任何打开 | 依赖服务异常 |
| CPU 使用率 | > 80% | 系统负载过高 |
| 平均 RT | > P99 * 2 | 响应变慢 |
| 异常比例 | > 10% | 错误率升高 |
性能优化最佳实践
1. 滑动窗口配置
// 增加滑动窗口精度(代价是更多内存)
SampleCountProperty.updateGlobalSampleCount(4); // 默认 2
IntervalProperty.updateGlobalInterval(1000); // 默认 1000ms
2. 避免过度埋点
// 不推荐:对每个小方法都埋点
@SentinelResource(value = "method1")
public void method1() { ... }
@SentinelResource(value = "method2")
public void method2() { ... }
// 推荐:对关键接口埋点
@SentinelResource(value = "api:order:create")
public Order createOrder() {
// 内部方法不需要单独埋点
validateOrder();
saveOrder();
notifyUser();
}
3. 规则数量控制
- 单个资源的规则数量控制在 10 个以内
- 总规则数量控制在合理范围(如 1000 个以内)
- 定期清理不再使用的规则
部署架构最佳实践
1. 控制台高可用
┌─────────────┐
│ 负载均衡器 │
└──────┬──────┘
│
┌────────┴────────┐
│ │
┌──────▼──────┐ ┌──────▼──────┐
│ Dashboard │ │ Dashboard │
│ Instance 1 │ │ Instance 2 │
└──────┬──────┘ └──────┬──────┘
│ │
└────────┬────────┘
│
┌──────▼──────┐
│ Nacos │
│ (规则存储) │
└─────────────┘
2. 规则配置分离
不同环境使用不同的规则配置:
Nacos 配置:
├── dev
│ ├── order-service-flow-rules
│ └── order-service-degrade-rules
├── test
│ ├── order-service-flow-rules
│ └── order-service-degrade-rules
└── prod
├── order-service-flow-rules
└── order-service-degrade-rules
3. 灰度发布支持
通过规则配置支持灰度发布:
// 对灰度实例设置不同的限流阈值
FlowRule rule = new FlowRule();
rule.setResource("api:newFeature");
rule.setCount(10); // 灰度实例低阈值
常见问题规避
1. 避免规则配置错误
// 错误:阈值为 0
rule.setCount(0); // 所有请求都会被拒绝
// 错误:资源名不匹配
@SentinelResource(value = "getUser")
public User getUserInfo() { ... } // 规则配置的是 "getUserInfo"
// 正确:仔细核对配置
rule.setResource("getUser");
rule.setCount(100);
2. 避免死锁
// 错误:在 entry 之前获取锁
synchronized(lock) {
Entry entry = SphU.entry("resource"); // 如果被阻塞,锁不会释放
}
// 正确:先 entry 再获取锁
Entry entry = SphU.entry("resource");
try {
synchronized(lock) {
// 业务逻辑
}
} finally {
entry.exit();
}
3. 正确处理异常
// 错误:吞掉 BlockException
try {
Entry entry = SphU.entry("resource");
entry.exit();
} catch (Exception e) {
// BlockException 被吞掉,无法区分限流和业务异常
}
// 正确:单独处理 BlockException
try {
Entry entry = SphU.entry("resource");
try {
// 业务逻辑
} finally {
entry.exit();
}
} catch (BlockException e) {
// 限流处理
}
检查清单
生产环境上线前的检查清单:
- 所有规则配置了动态数据源
- 控制台鉴权配置正确
- 降级逻辑已实现并测试
- 关键资源添加了监控告警
- 规则阈值经过压测验证
- 日志配置正确
- 异常处理完备
- 文档已更新