對于小不點的項目來說,RateLimiter配合ConcurrentHashMap可以對使用者進行簡單的限流,防止使用者頻繁刷量或者高頻請求。
RateLimiter 是 Guava 下的一個包,采用的是令牌桶算法:以一個恒定的速率向固定容量大小的桶中放入令牌,當有流量來的時候從桶中取出一個令牌。如果桶中沒有可用的令牌時就丢棄請求或者阻塞。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5SN2EGZ2YTZjBjYkZjN5YmY2QTN0ITN3QGO5kzM2EGM58CX5d2bs92Yl1iclB3bsVmdlR2LcNWaw9CXt92Yu4GZjlGbh5yYjV3Lc9CX6MHc0RHaiojIsJye.png)
ConcurrentHashMap 是一個可以在并發環境下使用的 HashMap,通俗點說就是線程安全的。
大緻的思路也很簡單:當一個請求過來時,根據使用者的 ID 從 ConcurrentHashMap 取出 RateLimiter,然後嘗試取出令牌,如果擷取失敗,則提示使用者請求過于頻繁;否則進行業務處理。
首先在 Controller 類中建立一個 ConcurrentHashMap 對象。
private final static Map<Integer, RateLimiter> outMoneyLimitMap = new ConcurrentHashMap<>();
1
然後是請求的處理代碼。
public ModelAndView submitOut() {
RateLimiter rateLimiter = null;
if (outMoneyLimitMap.containsKey(uid)) {
logger.debug("包含有該uid");
rateLimiter = outMoneyLimitMap.get(uid);
} else {
logger.debug("沒有該uid");
// 需要新增,一秒内 0.1 個令牌(10秒内一個令牌)
rateLimiter = RateLimiter.create(0.1);
outMoneyLimitMap.put(uid, rateLimiter);
}
if(!rateLimiter.tryAcquire()) {
throw new OrderException("請求過于頻繁");
}
// 正常業務處理
}
RateLimiter.create() 可以建立一個限流器,參數可以是 double,例子中為 0.1,即 10 秒内一個令牌(便于模拟)。
然後通過 rateLimiter.tryAcquire() 嘗試擷取令牌,如果擷取失敗,就提示使用者。否則正常業務處理。
整體思路非常非常簡單,實作起來也非常容易,是一種非常好用的限流示例。