Guava RateLimiter 使用

RateLimiter令牌桶算法。令牌桶算法的思想是以固定的速率生成令牌,在请求之前都需要从令牌桶里获取足令牌。当令牌数量不足的时候,请求将被阻塞等待或者返回。RateLimiter常用于限制访问资源的速率。

配置文件
ratelimit.properties
# 限流模块的配置

# 是否开启限流
ratelimit.doRateLimit=false

# 配置超时时间(配置将等待获取,不配置将直接返回),单位毫秒
ratelimit.waitTimeout=20

# 服务限流保护,服务每秒允许的TPS(需评估单个服务所允许的最大TPS)
ratelimit.permitsPerSecond=200

RateLimitConfig

/**
 * 限流配置信息
 * 
 */
@Data
@Component
@ConfigurationProperties(prefix = "ratelimit")
@PropertySource(value = "classpath:ratelimit.properties")
public class RateLimitConfig implements Serializable {
    
    private static final long serialVersionUID = 1L;
    private boolean           doRateLimit      = false;
    private long              waitTimeout;
    private long              permitsPerSecond;

}

/**
 * 限流服务接口
 * 
 */
public interface IRateLimitService {

    /**
     * 尝试获取许可证,获取1个,立即返回非阻塞
     * 
     * @return
     */
    boolean tryAcquire();

    /**
     * 尝试获取多个许可证,立即返回非阻塞
     * 
     * @param permits
     * @return
     */
    boolean tryAcquire(int permits);

    /**
     * 阻塞获取许可证,获取1个,若超过timeout未获取到许可证,则返回false
     * 
     * @param timeout
     * @return
     */
    boolean acquire(long timeout);

    /**
     * 阻塞获取多个许可证,若超过timeout未获取到许可证,则返回false
     * 
     * @param permits
     * @param timeout
     * @return
     */
    boolean acquire(int permits, long timeout);

}

/**
 * Guava RateLimiter的限流实现
 * 
 */
@Service
public class RateLimitServiceImpl implements IRateLimitService {

    private RateLimitConfig config;

    private RateLimiter rateLimiter;

    @Autowired
    public RateLimitServiceImpl(RateLimitConfig config) {
        this.config = config;
        this.rateLimiter = RateLimiter.create(this.config.getPermitsPerSecond());
    }

    @Override
    public boolean tryAcquire() {
        return this.tryAcquire(1);
    }

    @Override
    public boolean tryAcquire(int permits) {
        return rateLimiter.tryAcquire(permits);
    }

    @Override
    public boolean acquire(long timeout) {
        return this.acquire(1, timeout);
    }

    @Override
    public boolean acquire(int permits, long timeout) {
        long start = System.currentTimeMillis();
        for (;;) {
            boolean tryAcquire = rateLimiter.tryAcquire(permits);
            if (tryAcquire) {
                return true;
            }
            long end = System.currentTimeMillis();
            if ((end - start) >= timeout) {
                return false;
            }
        }
    }

}

切面拦截
/**
 * 限流切面
 * 
 */
@Aspect
@Order(-1)
@Component
public class RateLimitAspect {
    private static final Logger LOG = LoggerFactory.getLogger(RateLimitAspect.class);

    private RateLimitConfig config;

    private IRateLimitService rateLimitService;

    @Autowired
    public RateLimitAspect(RateLimitConfig config, IRateLimitService rateLimitService) {
        this.config = config;
        this.rateLimitService = rateLimitService;
    }

    @Pointcut("execution(public * xxx.xxx.*Controller.*(..))")
    public void executionMethod() {}

    @Around(value = "executionMethod()")
    public Object doRateLimit(ProceedingJoinPoint pjp) throws Throwable {
        if (LOG.isDebugEnabled()) {
            LOG.debug("进入限流处理切面!");
        }
        Object result = null;
        // 判断是否限流
        try {
            if (config.isDoRateLimit()) {
                // 开启限流
                boolean acquireResult = false;
                // 1.查看是否配置超时时间
                if (config.getWaitTimeout() == 0L) {
                    // 2.获取令牌
                    acquireResult = rateLimitService.tryAcquire();
                } else {
                    // 2.获取令牌,超时时间内获取令牌
                    acquireResult = rateLimitService.acquire(config.getWaitTimeout());
                }
                if (acquireResult) {
                    // 3.成功获取令牌,放行
                    result = pjp.proceed();
                } else {
                    // 3.失败获取令牌,返回错误码 429 => to many requests
                    result = ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).build();
                }
            } else {
                // 无开启限流,直接放行
                result = pjp.proceed();
            }
        } catch (Throwable e) {
            throw e;
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("限流处理切面结束!");
        }
        return result;
    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值