Java案例怎么实现熔断限流?

wen java案例 75

本文目录导读:

Java案例怎么实现熔断限流?

  1. 限流实现(Rate Limiting)
  2. 熔断实现(Circuit Breaker)
  3. 主流生产框架推荐
  4. 网关层限流(常用方案)
  5. 总结与最佳实践

在Java中实现熔断限流,通常有三种主流方式:手动代码实现使用开源框架(如Sentinel、Resilience4j)、以及依赖网关层(如Spring Cloud Gateway + Redis)

以下是详细的实现方案,从简单到生产级应用。


限流实现(Rate Limiting)

限流的核心是控制访问频率,常见算法有:令牌桶、漏桶、滑动窗口。

使用 Guava RateLimiter(单机版/简单场景)

优点:代码简单,适合单机应用。 缺点:无法跨进程,不适合分布式。

import com.google.common.util.concurrent.RateLimiter;
public class RateLimiterDemo {
    // 每秒生成10个令牌,即每秒最多允许10个请求
    private static final RateLimiter rateLimiter = RateLimiter.create(10.0);
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            // 尝试获取令牌,若获取失败则阻塞直到获取到
            double waitTime = rateLimiter.acquire();
            System.out.println("请求" + i + "等待了" + waitTime + "秒");
            // 业务逻辑
            doBusiness();
        }
    }
    private static void doBusiness() {
        System.out.println("执行业务逻辑...");
    }
}

使用 Redis + Lua 脚本(分布式限流)

原理:利用Redis的原子性操作,记录单位时间内的请求次数。

Lua 脚本(redis-rate-limiter.lua):

local key = KEYS[1]                  -- 限流的key
local limit = tonumber(ARGV[1])      -- 限制次数
local expire = tonumber(ARGV[2])     -- 时间窗口(秒)
local current = redis.call('INCR', key)
if current == 1 then
    redis.call('EXPIRE', key, expire)
end
if current > limit then
    return 0  -- 被限流
else
    return 1  -- 允许通过
end

Java 调用代码

import redis.clients.jedis.Jedis;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
public class RedisRateLimiter {
    private final RedisTemplate<String, String> redisTemplate;
    private final DefaultRedisScript<Long> script;
    public RedisRateLimiter(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
        this.script = new DefaultRedisScript<>();
        this.script.setScriptText(getScriptContent()); // 加载上面Lua脚本
        this.script.setResultType(Long.class);
    }
    public boolean tryAcquire(String key, int limit, int expireSeconds) {
        Long result = redisTemplate.execute(script, 
                        Collections.singletonList(key), 
                        String.valueOf(limit), 
                        String.valueOf(expireSeconds));
        return result != null && result == 1L;
    }
    // 使用示例:限制 userId=123 在10秒内最多只能请求5次
    // tryAcquire("limiter:user:123", 5, 10)
}

熔断实现(Circuit Breaker)

熔断是为了防止级联故障(雪崩),当服务异常率过高时,快速失败(打开电路),避免长时间等待。

手动实现(状态机)

状态:关闭(允许请求) → 打开(快速失败) → 半开(尝试恢复)

public class CircuitBreaker {
    private volatile CircuitState state = CircuitState.CLOSED;
    private final int failureThreshold;       // 触发熔断的失败次数
    private final long timeout;               // 熔断后到半开的时间
    private int failureCount = 0;
    private long lastFailureTime;
    public enum CircuitState {
        CLOSED,   // 关闭(正常)
        OPEN,     // 打开(熔断)
        HALF_OPEN // 半开(尝试恢复)
    }
    public CircuitBreaker(int failureThreshold, long timeout) {
        this.failureThreshold = failureThreshold;
        this.timeout = timeout;
    }
    public synchronized boolean allowRequest() {
        switch (state) {
            case CLOSED:
                return true;
            case OPEN:
                // 检查是否过了熔断时间,是则转为半开
                if (System.currentTimeMillis() - lastFailureTime > timeout) {
                    state = CircuitState.HALF_OPEN;
                    return true;
                }
                return false;
            case HALF_OPEN:
                return true;
        }
        return false;
    }
    public synchronized void recordSuccess() {
        if (state == CircuitState.HALF_OPEN) {
            state = CircuitState.CLOSED;
            failureCount = 0;
        }
    }
    public synchronized void recordFailure() {
        failureCount++;
        lastFailureTime = System.currentTimeMillis();
        if (failureCount >= failureThreshold) {
            state = CircuitState.OPEN;
        }
    }
}

使用方式

public class BusinessService {
    private CircuitBreaker breaker = new CircuitBreaker(5, 10_000); // 5次失败,熔断10秒
    public String call() {
        if (!breaker.allowRequest()) {
            throw new RuntimeException("Circuit breaker is open, request rejected");
        }
        try {
            // 调用下游服务
            String result = callDownstream();
            breaker.recordSuccess();
            return result;
        } catch (Exception e) {
            breaker.recordFailure();
            throw e;
        }
    }
}

主流生产框架推荐

Sentinel(阿里开源的流量防卫兵)

特点

  • 支持实时监控
  • 支持熔断降级、系统自适应保护
  • 支持注解,配置灵活

引入依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.8.6</version>
</dependency>

使用注解实现限流熔断

@Service
public class OrderService {
    // 限流:每秒最多允许20个请求
    @SentinelResource(value = "createOrder",
                      blockHandler = "blockHandlerMethod",
                      fallback = "fallbackMethod")
    public String createOrder(String userId) {
        // 正常业务逻辑
        return "Order created for " + userId;
    }
    // 限流后的处理
    public String blockHandlerMethod(String userId, BlockException e) {
        return "Too many requests, please try later.";
    }
    // 服务异常后的降级
    public String fallbackMethod(String userId, Throwable e) {
        return "Service unavailable, fallback.";
    }
}

Resilience4j(Spring Cloud Circuit Breaker 默认实现)

特点

  • 轻量级,与Hystrix类似但更现代
  • 功能模块化:熔断、重试、限流、隔离

引入依赖

<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot2</artifactId>
    <version>2.0.2</version>
</dependency>

yaml 配置

resilience4j:
  circuitbreaker:
    instances:
      userService:
        registerHealthIndicator: true
        slidingWindowSize: 10        # 滑动窗口大小
        minimumNumberOfCalls: 5      # 最少请求次数
        failureRateThreshold: 50     # 失败率阈值(百分比)
        waitDurationInOpenState: 10s # 熔断保持时间
  ratelimiter:
    instances:
      userService:
        limitForPeriod: 10           # 周期内最大请求数
        limitRefreshPeriod: 1s       # 刷新周期

Java 使用

@RestController
public class UserController {
    @GetMapping("/user/{id}")
    @CircuitBreaker(name = "userService", fallbackMethod = "userFallback")
    @RateLimiter(name = "userService")
    public String getUser(@PathVariable String id) {
        // 调用下游服务
        return restTemplate.getForObject("http://user-service/user/" + id, String.class);
    }
    public String userFallback(Exception e) {
        return "Fallback: Service is unavailable.";
    }
}

网关层限流(常用方案)

Spring Cloud Gateway + RequestRateLimiter

原理:基于Redis的令牌桶算法。

配置

spring:
  cloud:
    gateway:
      routes:
        - id: user-service-route
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
          filters:
            - name: RequestRateLimiter
              args:
                key-resolver: "#{@userKeyResolver}"   # 限流key提取器
                redis-rate-limiter.replenishRate: 10  # 令牌每秒填充数
                redis-rate-limiter.burstCapacity: 20  # 最大令牌数
# 自定义 key-resolver
@Bean
public KeyResolver userKeyResolver() {
    return exchange -> Mono.just(
        exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
    );
}

总结与最佳实践

场景 推荐方案 说明
单机限流 Guava RateLimiter 简单,但无法分布式
分布式限流 Redis + Lua 性能好,适合多实例部署
熔断降级 Resilience4j / Sentinel 生产级,支持监控
网关限流 Spring Cloud Gateway + Redis 统一入口控制
复杂策略 Sentinel 支持热点限流、系统保护、集群流控

关键原则

  1. 限流和熔断要配合使用:限流防止过载,熔断防止级联故障。
  2. 合理设置阈值:基于压测数据调整,避免误杀或失效。
  3. 降级处理:熔断时提供默认的 fallback 返回,避免前端白屏。
  4. 监控和告警:结合 Prometheus + Grafana 监控熔断/限流状态。

抱歉,评论功能暂时关闭!