本文共 3055 字,大约阅读时间需要 10 分钟。
RateLimiter 是 Guava 库中基于令牌桶算法实现的限流工具,简单易用,适合根据系统需求调整令牌生成速率。它在抢购限流、网速限制等场景中表现出色。
假设系统每秒最多允许处理 X 个任务,RateLimiter 可以帮助实现这个目标。通过创建一个 RateLimiter 实例,并在每个任务启动前调用 acquire 方法,可以确保每秒不超过指定数量的任务执行。
import com.google.common.util.concurrent.RateLimiter;import java.util.ArrayList;import java.util.List;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Demo1 { public static void main(String[] args) { RateLimiter rateLimiter = RateLimiter.create(0.5); // 每秒最多 0.5 个令牌 List tasks = new ArrayList<>(); for (int i = 0; i < 10; i++) { tasks.add(new UserRequest(i)); } ExecutorService threadPool = Executors.newCachedThreadPool(); for (Runnable runnable : tasks) { System.out.println("等待时间:" + rateLimiter.acquire()); threadPool.execute(runnable); } } private static class UserRequest implements Runnable { private int id; public UserRequest(int id) { this.id = id; } public void run() { System.out.println(id); } }} 运行结果表明,每 2 秒最多执行 1 个任务。通过 acquire 方法可以获取等待时间,确保无过度负载。
在抢购场景中,RateLimiter 可以防止系统被过度负载。例如,数据库每秒可处理 10 个请求,超过限制会导致故障。
package com.tianyalei.controller;import com.google.common.util.concurrent.RateLimiter;import com.tianyalei.model.GoodInfo;import com.tianyalei.service.GoodInfoService;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestControllerpublic class IndexController { @Resource(name = "db") private GoodInfoService goodInfoService; RateLimiter rateLimiter = RateLimiter.create(10); @RequestMapping("/miaosha") public Object miaosha(int count, String code) { System.out.println("等待时间" + rateLimiter.acquire()); if (goodInfoService.update(code, count) > 0) { return "购买成功"; } return "购买失败"; } @RequestMapping("/add") public Object add() { for (int i = 0; i < 100; i++) { GoodInfo goodInfo = new GoodInfo(); goodInfo.setCode("iphone" + i); goodInfo.setAmount(100); goodInfoService.add(goodInfo); } return "添加成功"; }} 测试显示,每秒前 10 个请求无需等待,后续请求每 0.1 秒一个,符合预期效果。
直接使用 RateLimiter 可能导致用户等待,影响体验。通过 tryAcquire 方法设置超时,确保在指定时间内无法获取令牌时立即返回失败。
@RequestMapping("/buy")public Object miao(int count, String code) { // 非阻塞获取令牌 if (!rateLimiter.tryAcquire(1000, TimeUnit.MILLISECONDS)) { System.out.println("短期无法获取令牌,真不幸,排队也瞎排"); return "失败"; } if (goodInfoService.update(code, count) > 0) { System.out.println("购买成功"); return "成功"; } System.out.println("数据不足,失败"); return "失败";} 在实际测试中,每秒大约有 22 个成功请求,78 个失败。通过降级策略,用户体验得到了显著提升。
部署到线上服务器后,测试结果如下:
通过 RateLimiter 的非阻塞方式,可以更好地控制流量,提升系统性能。
转载地址:http://hhcuz.baihongyu.com/