本文主要研究的是ratelimit-使用guava來做接口限流的相關(guān)內(nèi)容,具體如下。
一、問題描述
某天a君突然發(fā)現(xiàn)自己的接口請求量突然漲到之前的10倍,沒多久該接口幾乎不可使用,并引發(fā)連鎖反應(yīng)導(dǎo)致整個(gè)系統(tǒng)崩潰。如何應(yīng)對這種情況呢?生活給了我們答案:比如老式電閘都安裝了保險(xiǎn)絲,一旦有人使用超大功率的設(shè)備,保險(xiǎn)絲就會(huì)燒斷以保護(hù)各個(gè)電器不被強(qiáng)電流給燒壞。同理我們的接口也需要安裝上“保險(xiǎn)絲”,以防止非預(yù)期的請求對系統(tǒng)壓力過大而引起的系統(tǒng)癱瘓,當(dāng)流量過大時(shí),可以采取拒絕或者引流等機(jī)制。
二、常用的限流算法
常用的限流算法有兩種:漏桶算法和令牌桶算法。
漏桶算法思路很簡單,請求先進(jìn)入到漏桶里,漏桶以一定的速度出水,當(dāng)水請求過大會(huì)直接溢出,可以看出漏桶算法能強(qiáng)行限制數(shù)據(jù)的傳輸速率。
圖1 漏桶算法示意圖
對于很多應(yīng)用場景來說,除了要求能夠限制數(shù)據(jù)的平均傳輸速率外,還要求允許某種程度的突發(fā)傳輸。這時(shí)候漏桶算法可能就不合適了,令牌桶算法更為適合。如圖2所示,令牌桶算法的原理是系統(tǒng)會(huì)以一個(gè)恒定的速度往桶里放入令牌,而如果請求需要被處理,則需要先從桶里獲取一個(gè)令牌,當(dāng)桶里沒有令牌可取時(shí),則拒絕服務(wù)。
圖2 令牌桶算法示意圖
三、限流工具類ratelimiter
google開源工具包guava提供了限流工具類ratelimiter,該類基于“令牌桶算法”,非常方便使用。該類的接口具體的使用請參考:ratelimiter使用實(shí)踐。
ratelimiter 使用demo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
package ratelimite; import com.google.common.util.concurrent.ratelimiter; public class ratelimiterdemo { public static void main(string[] args) { testnoratelimiter(); testwithratelimiter(); } public static void testnoratelimiter() { long start = system.currenttimemillis(); for ( int i = 0 ; i < 10 ; i++) { system.out.println( "call execute.." + i); } long end = system.currenttimemillis(); system.out.println(end - start); } public static void testwithratelimiter() { long start = system.currenttimemillis(); ratelimiter limiter = ratelimiter.create( 10.0 ); // 每秒不超過10個(gè)任務(wù)被提交 for ( int i = 0 ; i < 10 ; i++) { limiter.acquire(); // 請求ratelimiter, 超過permits會(huì)被阻塞 system.out.println( "call execute.." + i); } long end = system.currenttimemillis(); system.out.println(end - start); } } |
四 guava并發(fā):listenablefuture與ratelimiter示例
概念
listenablefuture顧名思義就是可以監(jiān)聽的future,它是對java原生future的擴(kuò)展增強(qiáng)。我們知道future表示一個(gè)異步計(jì)算任務(wù),當(dāng)任務(wù)完成時(shí)可以得到計(jì)算結(jié)果。如果我們希望一旦計(jì)算完成就拿到結(jié)果展示給用戶或者做另外的計(jì)算,就必須使用另一個(gè)線程不斷的查詢計(jì)算狀態(tài)。這樣做,代碼復(fù)雜,而且效率低下。使用listenablefuture guava幫我們檢測future是否完成了,如果完成就自動(dòng)調(diào)用回調(diào)函數(shù),這樣可以減少并發(fā)程序的復(fù)雜度。
推薦使用第二種方法,因?yàn)榈诙N方法可以直接得到future的返回值,或者處理錯(cuò)誤情況。本質(zhì)上第二種方法是通過調(diào)動(dòng)第一種方法實(shí)現(xiàn)的,做了進(jìn)一步的封裝。
另外listenablefuture還有其他幾種內(nèi)置實(shí)現(xiàn):
settablefuture:不需要實(shí)現(xiàn)一個(gè)方法來計(jì)算返回值,而只需要返回一個(gè)固定值來做為返回值,可以通過程序設(shè)置此future的返回值或者異常信息
checkedfuture: 這是一個(gè)繼承自listenablefuture接口,他提供了checkedget()方法,此方法在future執(zhí)行發(fā)生異常時(shí),可以拋出指定類型的異常。
ratelimiter類似于jdk的信號量semphore,他用來限制對資源并發(fā)訪問的線程數(shù),本文介紹ratelimiter使用
代碼示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
import java.util.concurrent.callable; import java.util.concurrent.executionexception; import java.util.concurrent.executors; import java.util.concurrent.timeunit; import com.google.common.util.concurrent.futurecallback; import com.google.common.util.concurrent.futures; import com.google.common.util.concurrent.listenablefuture; import com.google.common.util.concurrent.listeningexecutorservice; import com.google.common.util.concurrent.moreexecutors; import com.google.common.util.concurrent.ratelimiter; public class listenablefuturedemo { public static void main(string[] args) { testratelimiter(); testlistenablefuture(); } /** * ratelimiter類似于jdk的信號量semphore,他用來限制對資源并發(fā)訪問的線程數(shù) */ public static void testratelimiter() { listeningexecutorservice executorservice = moreexecutors .listeningdecorator(executors.newcachedthreadpool()); ratelimiter limiter = ratelimiter.create( 5.0 ); // 每秒不超過4個(gè)任務(wù)被提交 for ( int i = 0 ; i < 10 ; i++) { limiter.acquire(); // 請求ratelimiter, 超過permits會(huì)被阻塞 final listenablefuture<integer> listenablefuture = executorservice .submit( new task( "is " + i)); } } public static void testlistenablefuture() { listeningexecutorservice executorservice = moreexecutors .listeningdecorator(executors.newcachedthreadpool()); final listenablefuture<integer> listenablefuture = executorservice .submit( new task( "testlistenablefuture" )); //同步獲取調(diào)用結(jié)果 try { system.out.println(listenablefuture.get()); } catch (interruptedexception e1) { e1.printstacktrace(); } catch (executionexception e1) { e1.printstacktrace(); } //第一種方式 listenablefuture.addlistener( new runnable() { @override public void run() { try { system.out.println( "get listenable future's result " + listenablefuture.get()); } catch (interruptedexception e) { e.printstacktrace(); } catch (executionexception e) { e.printstacktrace(); } } } , executorservice); //第二種方式 futures.addcallback(listenablefuture, new futurecallback<integer>() { @override public void onsuccess(integer result) { system.out .println( "get listenable future's result with callback " + result); } @override public void onfailure(throwable t) { t.printstacktrace(); } } ); } } class task implements callable<integer> { string str; public task(string str){ this .str = str; } @override public integer call() throws exception { system.out.println( "call execute.." + str); timeunit.seconds.sleep( 1 ); return 7 ; } } |
guava版本
1
2
3
4
5
|
<dependency> <groupid>com.google.guava</groupid> <artifactid>guava</artifactid> <version> 14.0 . 1 </version> </dependency> |
總結(jié)
以上就是本文關(guān)于ratelimit-使用guava來做接口限流代碼示例的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
原文鏈接:http://blog.csdn.net/JIESA/article/details/50412027