本文目录导读:

在Java分布式系统中,“降载”(降级与限流)和“削峰填谷”是保障系统稳定性的核心手段,下面从策略层面和代码实现层面系统梳理一下常用的降载方案。
核心策略分层
限流(Rate Limiting)
- 目的:控制进入系统的请求速率,防止流量冲垮系统。
- 常用算法:
- 计数器(固定窗口):简单但存在临界突变问题。
- 滑动窗口:解决固定窗口的突发问题。
- 漏桶(Leaky Bucket):平滑流量,强制固定速率。
- 令牌桶(Token Bucket):允许一定突发,更常用(如 Guava RateLimiter、Sentinel)。
- 粒度:单机限流(本地内存) vs 分布式限流(Redis + Lua)。
降级(Degradation)
- 目的:当系统压力过大或依赖服务不可用时,主动舍弃非核心功能,保证核心功能可用。
- 常见手段:
- 返回默认值/缓存值:如商品详情页当推荐服务失败时,展示空推荐或缓存推荐。
- 静态化/缓存兜底:直接返回静态页面或本地缓存。
- 熔断(Circuit Breaker):统计调用失败率,达到阈值直接拒绝调用(Hystrix、Resilience4j、Sentinel)。
- 系统级降级:关闭部分非关键线程池、禁止写操作、只读模式。
削峰填谷
- 目的:将瞬间高峰流量平缓地分摊到一段时间内处理,防止系统过载。
- 手段:
- 消息队列(MQ):请求先入队列,下游按能力消费(Kafka、RocketMQ、RabbitMQ)。
- 异步处理:立即返回“处理中”,后台慢慢跑。
- 缓冲区:如 Netty 的写缓冲区、内存队列 + 批量 flush。
常用开源组件落地
| 组件 | 功能 | 适用场景 |
|---|---|---|
| Sentinel | 限流、熔断、降级、热点参数限流、系统自适应保护 | 微服务架构,功能最全面,支持实时监控 |
| Hystrix (已维护模式) | 熔断、隔离、降级 | 老项目还在用,适合线程池隔离场景 |
| Resilience4j | 轻量级熔断、限流、重试、隔离 | 响应式编程友好,与 Spring Cloud Gateway 配合好 |
| Guava RateLimiter | 单机令牌桶限流 | 简单单机场景,不需要分布式协调 |
| Redisson RRateLimiter | 分布式令牌桶(基于Redis) | 多实例统一限流 |
分布式限流落地示例
基于 Redis + Lua 的滑动窗口限流(纯手写)
-- keys[1] = 限流key
-- ARGV[1] = 窗口大小(秒)
-- ARGV[2] = 最大请求数
local key = KEYS[1]
local window = tonumber(ARGV[1])
local maxRequests = tonumber(ARGV[2])
local now = redis.call('TIME')[1] -- 获取redis时间(秒)
redis.call('ZADD', key, now, now)
redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
local count = redis.call('ZCARD', key)
redis.call('EXPIRE', key, window)
return count <= maxRequests
基于 Sentinel 的注解式限流(推荐)
@SentinelResource(value = "queryOrder", blockHandler = "blockHandlerForQuery")
public Order queryOrder(String id) {
// 正常业务逻辑
}
public Order blockHandlerForQuery(String id, BlockException ex) {
// 降级逻辑:返回缓存数据或错误提示
return new Order("FALLBACK", "系统繁忙,请稍后重试");
}
流量控制规则(控制台/代码配置):
{
"resource": "queryOrder",
"count": 100, // QPS 阈值
"grade": 1, // 0=线程数, 1=QPS
"controlBehavior": 2 // 0=快速失败, 1=Warm Up, 2=排队等待
}
降级与熔断实战
微服务内部降级(Feign + Sentinel)
@FeignClient(name = "product-service", fallback = ProductFallback.class)
public interface ProductClient {
@GetMapping("/product/{id}")
Product getProduct(@PathVariable("id") Long id);
}
@Component
public class ProductFallback implements ProductClient {
@Override
public Product getProduct(Long id) {
// 返回降级数据
return new Product(id, "降级商品信息", 0.0);
}
}
熔断器配置(Resilience4j)
resilience4j.circuitbreaker:
instances:
userService:
registerHealthIndicator: true
slidingWindowSize: 10 # 统计窗口大小(请求数)
minimumNumberOfCalls: 5 # 最小请求数(低于此值不触发熔断)
failureRateThreshold: 50 # 失败率阈值(%)
waitDurationInOpenState: 10s # 熔断后等待时间
permittedNumberOfCallsInHalfOpenState: 3 # 半开状态下允许的请求数
削峰填谷-消息队列版
利用 MQ 做流量蓄水池
// 生产者:接收请求后直接发消息
public void createOrder(OrderRequest req) {
kafkaTemplate.send("order-topic", req);
return "订单已提交,处理中";
}
// 消费者:按自身能力消费(可通过配置消费线程数 + 流量控制)
@KafkaListener(topics = "order-topic", concurrency = "5")
public void processOrder(ConsumerRecord<String, OrderRequest> record) {
// 这里可以叠加本地限流
orderService.create(record.value());
}
配合批量消费:
spring.kafka.listener: type: BATCH batch-size: 100 # 每批最多100条 poll-timeout: 1000ms # 等待时间
系统级自适应降载(进阶)
基于 CPU/内存负载的限流(Sentinel 系统自适应保护)
// 配置:当系统负载 > 80% 时自动拒绝新请求 SystemRule rule = new SystemRule(); rule.setHighestSystemLoad(0.8); // CPU 负载 rule.setAvgRt(1000); // 平均响应时间(ms) rule.setQps(10000); // 最大 QPS SystemRuleManager.loadRules(Collections.singletonList(rule));
线程池隔离降级(Hystrix 方式,推荐用 Sentinel 替代)
// 将不关键的服务放在独立的线程池中,线程池满时自动降级
@HystrixCommand(
groupKey = "recommendation",
commandKey = "getRecommendation",
threadPoolKey = "recommendation-pool",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "10"),
@HystrixProperty(name = "maxQueueSize", value = "50")
},
fallbackMethod = "getRecommendationFallback"
)
public List<Product> getRecommendation(String userId) {
return recommendationService.get(userId);
}
降载设计原则总结
| 原则 | 具体做法 |
|---|---|
| 快速失败 | 限流触及时直接返回 429(Too Many Requests),不占用资源 |
| 有损服务 | 核心功能保100%可用,非核心功能降级(如推荐、日志、统计) |
| 异步化 | 非实时请求丢 MQ,不要同步等待 |
| 削峰 | 利用 MQ 或本地缓冲队列,把瞬时压力平摊 |
| 兜底数据 | 所有依赖外部服务的地方都要有降级数据(默认值、缓存) |
| 监控告警 | 降载触发时要记录日志 + 推送告警,便于排查 |
推荐技术栈组合
Spring Cloud Gateway (网关层) —— Sentinel 限流 + 请求聚合
↓
微服务 A —— Sentinel (限流 + 熔断) + Resilience4j (重试)
↓
微服务 B —— 同 A,外加 MQ 削峰(RocketMQ/Kafka)
↓
数据库/缓存 —— 连接池限流、读写分离、缓存降级
一句话总结:降载的核心是 “放弃非核心,保护核心”,技术上通过 限流控制入口流量、降级处理异常依赖、MQ 削平流量尖峰 三招组合使用。
如果需要针对某个具体场景(如秒杀、大数据导入、实时推送)的降载方案,可以进一步细化讨论。