RateLimit-使用guava來做接口限流代碼示例
本文主要研究的是RateLimit-使用guava來做接口限流的相關(guān)內(nèi)容,具體如下。
一、問題描述
某天A君突然發(fā)現(xiàn)自己的接口請求量突然漲到之前的10倍,沒多久該接口幾乎不可使用,并引發(fā)連鎖反應(yīng)導(dǎo)致整個系統(tǒng)崩潰。如何應(yīng)對這種情況呢?生活給了我們答案:比如老式電閘都安裝了保險絲,一旦有人使用超大功率的設(shè)備,保險絲就會燒斷以保護(hù)各個電器不被強電流給燒壞。同理我們的接口也需要安裝上“保險絲”,以防止非預(yù)期的請求對系統(tǒng)壓力過大而引起的系統(tǒng)癱瘓,當(dāng)流量過大時,可以采取拒絕或者引流等機制。
二、常用的限流算法
常用的限流算法有兩種:漏桶算法和令牌桶算法。
漏桶算法思路很簡單,請求先進(jìn)入到漏桶里,漏桶以一定的速度出水,當(dāng)水請求過大會直接溢出,可以看出漏桶算法能強行限制數(shù)據(jù)的傳輸速率。

圖1 漏桶算法示意圖
對于很多應(yīng)用場景來說,除了要求能夠限制數(shù)據(jù)的平均傳輸速率外,還要求允許某種程度的突發(fā)傳輸。這時候漏桶算法可能就不合適了,令牌桶算法更為適合。如圖2所示,令牌桶算法的原理是系統(tǒng)會以一個恒定的速度往桶里放入令牌,而如果請求需要被處理,則需要先從桶里獲取一個令牌,當(dāng)桶里沒有令牌可取時,則拒絕服務(wù)。

圖2 令牌桶算法示意圖
三、限流工具類RateLimiter
google開源工具包guava提供了限流工具類RateLimiter,該類基于“令牌桶算法”,非常方便使用。該類的接口具體的使用請參考:RateLimiter使用實踐。
RateLimiter 使用Demo
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個任務(wù)被提交
for (int i = 0; i < 10; i++) {
limiter.acquire();
// 請求RateLimiter, 超過permits會被阻塞
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的擴展增強。我們知道Future表示一個異步計算任務(wù),當(dāng)任務(wù)完成時可以得到計算結(jié)果。如果我們希望一旦計算完成就拿到結(jié)果展示給用戶或者做另外的計算,就必須使用另一個線程不斷的查詢計算狀態(tài)。這樣做,代碼復(fù)雜,而且效率低下。使用ListenableFuture Guava幫我們檢測Future是否完成了,如果完成就自動調(diào)用回調(diào)函數(shù),這樣可以減少并發(fā)程序的復(fù)雜度。
推薦使用第二種方法,因為第二種方法可以直接得到Future的返回值,或者處理錯誤情況。本質(zhì)上第二種方法是通過調(diào)動第一種方法實現(xiàn)的,做了進(jìn)一步的封裝。
另外ListenableFuture還有其他幾種內(nèi)置實現(xiàn):
SettableFuture:不需要實現(xiàn)一個方法來計算返回值,而只需要返回一個固定值來做為返回值,可以通過程序設(shè)置此Future的返回值或者異常信息
CheckedFuture: 這是一個繼承自ListenableFuture接口,他提供了checkedGet()方法,此方法在Future執(zhí)行發(fā)生異常時,可以拋出指定類型的異常。
RateLimiter類似于JDK的信號量Semphore,他用來限制對資源并發(fā)訪問的線程數(shù),本文介紹RateLimiter使用
代碼示例
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個任務(wù)被提交
for (int i = 0; i < 10; i++) {
limiter.acquire();
// 請求RateLimiter, 超過permits會被阻塞
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版本
<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)專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
相關(guān)文章
Spring Boot Admin微服務(wù)應(yīng)用監(jiān)控的實現(xiàn)
這篇文章主要介紹了Spring Boot Admin微服務(wù)應(yīng)用監(jiān)控,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
IDEA設(shè)置maven修改settings.xml配置文件無法加載倉庫的解決方案
這篇文章主要介紹了IDEA設(shè)置maven修改settings.xml配置文件無法加載倉庫的解決方案,幫助大家更好的利用IDEA進(jìn)行JAVA的開發(fā)學(xué)習(xí),感興趣的朋友可以了解下2021-01-01
SpringMVC中Json數(shù)據(jù)格式轉(zhuǎn)換
本文主要介紹了SpringMVC中Json數(shù)據(jù)格式轉(zhuǎn)換的相關(guān)知識。具有很好的參考價值。下面跟著小編一起來看下吧2017-03-03
學(xué)習(xí)Java之如何正確地向上轉(zhuǎn)型與向下轉(zhuǎn)型
面向?qū)ο蟮牡谌齻€特征是多態(tài),實現(xiàn)多態(tài)有三個必要條件:繼承、方法重寫和向上轉(zhuǎn)型,在學(xué)習(xí)多態(tài)之前,我們還要先學(xué)習(xí)Java的類型轉(zhuǎn)換,本篇文章就來帶大家認(rèn)識什么是類型轉(zhuǎn)換,看看類型轉(zhuǎn)換都有哪幾種情況,以及如何避免類型轉(zhuǎn)換時出現(xiàn)異常2023-05-05
ArrayList在for循環(huán)中使用remove方法移除元素方法介紹
這篇文章主要介紹了ArrayList在for循環(huán)中使用remove方法移除元素的內(nèi)容,介紹了具體代碼實現(xiàn),需要的朋友可以參考下。2017-09-09

