1. 程式人生 > >TPS限流

TPS限流

lis exceptio spring token lib 子類 compare ken int

限流是高可用服務需要具備的能力之一 ,粗暴簡單的就像我們之前做的並發數控制。好一點的有tps限流,可用令牌桶等算法實現。《億級流量網站架構核心技術》一書P67限流詳解也有講。dubbo提供了此機制,TpsLimitFilter。guava也提供了相應的工具類RateLimiter。

1. dubbo提供的tps限流

1.1 demo使用

1.1.1 在source folder下放置,META-INF/dubbo/com.alibaba.dubbo.rpc.Filter

裏面寫入 tpslimiter=com.alibaba.dubbo.rpc.filter.TpsLimitFilter
表示將TpsLimitFilter這個filter的擴展給裝配給dubbo

1.1.2 provider側的配置

<dubbo:service interface="org.simonme.dubbo.demo.provider.service.HelloService" ref="m00001.app001.xx.helloService" filter="tpslimiter">
    <dubbo:parameter key="tps" value="5" />
</dubbo:service>

意思對HelloService 這個接口在provider側做tps為5的限流,默認間隔是60s,可以通過tps.interval

這個parameter進行配置,單位是毫秒。註意此處配置的tps為5,不是每秒限制通過5個以內的請求,而是單位時間間隔內通過5個以內的請求。關於單位時間間隔前面解釋了。

1.2 如果超限了是什麽現象?

客戶端會拋出rpc調用異常:
com.alibaba.dubbo.rpc.RpcException: Failed to invoke service org.simonme.dubbo.demo.provider.service.HelloService.sayHello because exceed max service tps

1.3 原理分析

主要是DefaultTPSLimiterStatItem

兩個類配合完成。采用的是令牌桶算法,實現在StatItem類中。
大意是:
每隔一個單位時間後重置令牌桶中令牌的數量,然後每次請求來的時候減1,減到小於0時,拒絕請求。

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();
}

註意:此實現依賴系統時間,如果想用相對時間實現,可以參見這裏 dubbo的TPS限流模塊在運行時系統時間發生變化的情況下限流不能正常工作 #2345。

2. spring cloud

對於采用spring cloud的架構的項目,可以借助guava的RateLimiter來實現ZuulFilter的子類來達成tps限流的目的。

TPS限流