令牌桶算法( Token Bucket
)和 Leaky Bucket 效果一樣但方向相反的算法,更加容易理解.隨着時間流逝,系統會按恆定 1/QPS
時間間隔(如果 QPS=100
,則間隔是 10ms
)往桶里加入 Token
(想象和漏洞漏水相反,有個水龍頭在不斷的加水),如果桶已經滿了就不再加了.新請求來臨時,會各自拿走一個 Token
,如果沒有 Token
可拿了就阻塞或者拒絕服務.
@Autowired private JedisClientService jedisClient; public boolean acquire(String key, Integer permits, long currMillSecond) { try { //針對新用戶創建令牌桶 if (!jedisClient.exists(key)) { jedisClient.hset(key, "last_mill_second", String.valueOf(currMillSecond)); jedisClient.hset(key, "curr_permits", "0"); jedisClient.hset(key, "max_permits", "50"); jedisClient.hset(key, "rate", "400"); return true; } //獲取令牌桶信息,上一個令牌時間,當前可用令牌數,最大令牌數,令牌消耗速率 List<String> limitInfo = jedisClient.hmget(key, "last_mill_second", "curr_permits", "max_permits", "rate"); long lastMillSecond = Long.parseLong(limitInfo.get(0)); Integer currPermits = Integer.valueOf(limitInfo.get(1)); Integer maxPermits = Integer.valueOf(limitInfo.get(2)); Double rate = Double.valueOf(limitInfo.get(3)); //向桶里面添加令牌 Double reversePermitsDouble = ((currMillSecond - lastMillSecond) / 1000) * rate; Integer reversePermits = reversePermitsDouble.intValue(); Integer expectCurrPermits = reversePermits + currPermits; Integer localCurrPermits = Math.min(expectCurrPermits, maxPermits); //添加令牌之后更新時間 if (reversePermits > 0) { jedisClient.hset(key, "last_mill_second", String.valueOf(currMillSecond)); } //判斷桶里面剩余的令牌數目 if (localCurrPermits - permits >= 0) { jedisClient.hset(key, "curr_permits", String.valueOf(localCurrPermits - permits)); return true; } else { jedisClient.hset(key, "curr_permits", String.valueOf(localCurrPermits)); return false; } } catch (Exception e) { e.printStackTrace(); return false; } }
參考文章: https://blog.csdn.net/tianyaleixiaowu/article/details/74942405