常見的限流方式有:計數器、滑動窗口、漏斗和令牌桶算法。
計數器 VS 滑動窗口
計數器算法是最簡單的算法,可以看成是滑動窗口的低精度實現。滑動窗口由於需要存儲多份的計數器(每一個格子存一份),所以滑動窗口在實現上需要更多的存儲空間。也就是說,如果滑動窗口的精度越高,需要的存儲空間就越大。
漏桶算法 VS 令牌桶算法
漏桶算法和令牌桶算法最明顯的區別是令牌桶算法允許流量一定程度的突發。因為默認的令牌桶算法,取走token是不需要耗費時間的,也就是說,假設桶內有100個token時,那么可以瞬間允許100個請求通過。
令牌桶算法由於實現簡單,且允許某些流量的突發,對用戶友好,所以被業界采用地較多。當然我們需要具體情況具體分析,只有最合適的算法,沒有最優的算法。
dubbo 默認實現的是滑動窗口的方式。
@Activate(group = Constants.PROVIDER, value = Constants.TPS_LIMIT_RATE_KEY) public class TpsLimitFilter implements Filter { private final TPSLimiter tpsLimiter = new DefaultTPSLimiter(); @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { if (!tpsLimiter.isAllowable(invoker.getUrl(), invocation)) { throw new RpcException( "Failed to invoke service " + invoker.getInterface().getName() + "." + invocation.getMethodName() + " because exceed max service tps."); } return invoker.invoke(invocation); } }
public class DefaultTPSLimiter implements TPSLimiter { // 存儲每個服務的key和每個服務限制的請求數。 private final ConcurrentMap<String, StatItem> stats = new ConcurrentHashMap<String, StatItem>(); @Override public boolean isAllowable(URL url, Invocation invocation) { int rate = url.getParameter(Constants.TPS_LIMIT_RATE_KEY, -1); // 窗口內的最大請求數 long interval = url.getParameter(Constants.TPS_LIMIT_INTERVAL_KEY, Constants.DEFAULT_TPS_LIMIT_INTERVAL); // 窗口大小 String serviceKey = url.getServiceKey(); // 服務唯一key if (rate > 0) { StatItem statItem = stats.get(serviceKey); if (statItem == null) { stats.putIfAbsent(serviceKey, new StatItem(serviceKey, rate, interval)); statItem = stats.get(serviceKey); } return statItem.isAllowable(); } else { StatItem statItem = stats.get(serviceKey); if (statItem != null) { stats.remove(serviceKey); } } return true; } }
public boolean isAllowable() { long now = System.currentTimeMillis(); if (now > lastResetTime + interval) { // 重置窗口 token.set(rate); lastResetTime = now; } int value = token.get(); boolean flag = false; while (value > 0 && !flag) { flag = token.compareAndSet(value, value - 1); // 每來一個請求減少一個許可 value = token.get(); } return flag; }
總結:dubbo 默認的 限流實現比較簡單,用戶可以自定義限流策略。業界令牌桶用的比較多。