Java高并发系统的限流策略
限流算法
令牌桶(Token Bucket)、漏桶(leaky bucket)和計(jì)數(shù)器算法是最常用的三種限流的算法。
計(jì)數(shù)器限流算法也是比較常用的,主要用來(lái)限制總并發(fā)數(shù),比如數(shù)據(jù)庫(kù)連接池大小、線程池大小、程序訪問(wèn)并發(fā)數(shù)等都是使用計(jì)數(shù)器算法。也是最簡(jiǎn)單粗暴的算法。
使用計(jì)數(shù)器限流示例1:
public class CountRateLimiterDemo { private static AtomicInteger count = new AtomicInteger(0); public static void exec() { if (count.get() >= 5) { System.out.println("請(qǐng)求用戶過(guò)多,請(qǐng)稍后在試!"+System.currentTimeMillis()/1000); } else { count.incrementAndGet(); try { //處理核心邏輯 TimeUnit.SECONDS.sleep(1); System.out.println("--"+System.currentTimeMillis()/1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { count.decrementAndGet(); } } } }使用AomicInteger來(lái)進(jìn)行統(tǒng)計(jì)當(dāng)前正在并發(fā)執(zhí)行的次數(shù),如果超過(guò)域值就簡(jiǎn)單粗暴的直接響應(yīng)給用戶,說(shuō)明系統(tǒng)繁忙,請(qǐng)稍后再試或其它跟業(yè)務(wù)相關(guān)的信息。
弊端:使用 AomicInteger 簡(jiǎn)單粗暴超過(guò)域值就拒絕請(qǐng)求,可能只是瞬時(shí)的請(qǐng)求量高,也會(huì)拒絕請(qǐng)求。
使用計(jì)數(shù)器限流示例2
public class CountRateLimiterDemo { private static Semaphore semphore = new Semaphore(50); public static void exec() { if(semphore.getQueueLength()>100){ System.out.println("當(dāng)前等待排隊(duì)的任務(wù)數(shù)大于100,請(qǐng)稍候再試..."); } try { semphore.acquire(); // 處理核心邏輯 TimeUnit.SECONDS.sleep(1); System.out.println("--" + System.currentTimeMillis() / 1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { semphore.release(); } } }使用Semaphore信號(hào)量來(lái)控制并發(fā)執(zhí)行的次數(shù),如果超過(guò)域值信號(hào)量,則進(jìn)入阻塞隊(duì)列中排隊(duì)等待獲取信號(hào)量進(jìn)行執(zhí)行。如果阻塞隊(duì)列中排隊(duì)的請(qǐng)求過(guò)多超出系統(tǒng)處理能力,則可以在拒絕請(qǐng)求。
相對(duì)Atomic優(yōu)點(diǎn):如果是瞬時(shí)的高并發(fā),可以使請(qǐng)求在阻塞隊(duì)列中排隊(duì),而不是馬上拒絕請(qǐng)求,從而達(dá)到一個(gè)流量削峰的目的。
使用guava的RateLimiter限流示例3
Guava中開(kāi)源工具類RateLimiter是基于令牌桶算法的實(shí)現(xiàn)類,可以簡(jiǎn)單的實(shí)現(xiàn)限流的工作,并根據(jù)系統(tǒng)實(shí)際情況調(diào)整生成token速率。RateLimiter 對(duì)簡(jiǎn)單的令牌桶算法做了一些工程上的優(yōu)化,具體的實(shí)現(xiàn)是 SmoothBursty。需要注意的是,RateLimiter 的另一個(gè)實(shí)現(xiàn) SmoothWarmingUp,就不是令牌桶了,而是漏桶算法。也許是出于簡(jiǎn)單起見(jiàn),RateLimiter 中的時(shí)間窗口能且僅能為 1s,如果想搞其他時(shí)間單位的限流,只能另外造輪子。
RateLimiter 有一個(gè)有趣的特性是「前人挖坑后人跳」,也就是說(shuō) RateLimiter 允許某次請(qǐng)求拿走超出剩余令牌數(shù)的令牌,但是下一次請(qǐng)求將為此付出代價(jià),一直等到令牌虧空補(bǔ)上,并且桶中有足夠本次請(qǐng)求使用的令牌為止。這里面就涉及到一個(gè)權(quán)衡,是讓前一次請(qǐng)求干等到令牌夠用才走掉呢,還是讓它先走掉后面的請(qǐng)求等一等呢?Guava 的設(shè)計(jì)者選擇的是后者,先把眼前的活干了,后面的事后面再說(shuō)。
RateLimiter rateLimiter = RateLimiter.create(2);
System.out.println(rateLimiter.acquire(5));
System.out.println(rateLimiter.acquire(2));
System.out.println(rateLimiter.acquire(1));
輸出
0.0
2.496889
0.992149
?
可以看出,令牌桶每秒只能產(chǎn)生2個(gè)令牌,我們可以第一次取出5個(gè),但是第二個(gè)再去取令牌的時(shí)候,需要等2.5s,也就是第一次令牌取完后,需要等2.5s才能取到令牌。同樣的,第三次取1個(gè)令牌的時(shí)候,也需要等待第二次的1s的時(shí)間。也就是,取的速率可以超過(guò)令牌產(chǎn)生的速率,但是下一次再次去取的時(shí)候,需要阻塞等待。
當(dāng)然也可以使用tryAcquire來(lái)非阻塞的獲取,可以實(shí)時(shí)返回結(jié)果。另外tryAcquire也可以傳入?yún)?shù),也就是等待的時(shí)間,超時(shí)直接返回false。這點(diǎn)等同于常見(jiàn)的lock,tryLock。
?
總結(jié)
以上是生活随笔為你收集整理的Java高并发系统的限流策略的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java分布式应用限流实现
- 下一篇: Nginx系统环境准备