新聞中心
一、限流基礎(chǔ)知識(shí)介紹

創(chuàng)新互聯(lián)建站主要從事網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站設(shè)計(jì)、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)準(zhǔn)格爾,10年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):18980820575
為啥要限流,相信就不用我多說了。
- 比如,我周末去飯店吃飯,但是人太多了,我只能去前臺(tái)拿個(gè)號(hào),等號(hào)碼到我的時(shí)候才能進(jìn)飯店吃飯。如果飯店沒有限流怎么辦?一到飯點(diǎn),人都往里沖,而飯店又處理不了這么多人流,很容易就出事故(飯店塞滿了人,無路可走。飯店的工作人員崩潰了,處理不過來)
- 回到代碼世界上也是一樣的,服務(wù)器能處理的請(qǐng)求數(shù)有限,如果請(qǐng)求量特別大,我們需要做限流(要么就讓請(qǐng)求等待,要么就把請(qǐng)求給扔了)
在代碼世界上,限流有兩種比較常見的算法:
- 令牌桶算法
- 漏桶算法
1.1 什么是漏桶算法
比如,現(xiàn)在我有一個(gè)桶子,綠色那塊是我能裝水的容量,如果超過我能裝下的容量,再往桶子里邊倒水,就會(huì)溢出來(限流):
我們目前可以知道的是:
- 桶子的容量是固定的(是圖上綠色那塊)
- 超出了桶子的容量就會(huì)溢出(要么等待,要么直接丟棄)
OK,現(xiàn)在我們?cè)谕白永锿趥€(gè)洞,讓水可以從洞子里邊流出來:
桶子的洞口的大小是固定的,所以水從洞口流出來的速率也是固定的。
所以總結(jié)下來算法所需的參數(shù)就兩個(gè):
- 桶子的容量
- 漏水的速率
漏桶算法有兩種實(shí)現(xiàn):
- 不允許突發(fā)流量的情況:如果進(jìn)水的速率大于出水的速率,直接舍棄掉多余的水。比如,我的桶子容量能裝100L,但我的桶子出水速率是10L/s。此時(shí),如果現(xiàn)在有100L/s的水進(jìn)來,我只讓10L的水進(jìn)到桶子,其余的都限流。(限定了請(qǐng)求的速度)
- 允許一定的突發(fā)流量情況:我的桶子能裝100L,如果現(xiàn)在我的桶子是空的,那么這100L的水都能進(jìn)我的桶子。我以10L/s的速率將這些水流出,如果還有100L的水進(jìn)來,只能限流了。
經(jīng)過上面的分析我們就知道:
漏桶算法可以平滑網(wǎng)絡(luò)上的突發(fā)流量(因?yàn)槁┧乃俾适枪潭ǖ?
1.2 什么是令牌桶算法
現(xiàn)在我有另外一個(gè)桶子,這個(gè)桶子不用來裝水,用來裝令牌:
令牌會(huì)一定的速率扔進(jìn)桶子里邊,比如我1秒扔10個(gè)令牌進(jìn)桶子:
桶子能裝令牌的個(gè)數(shù)有上限的,比如我的桶子最多只能裝1000個(gè)令牌。
每個(gè)請(qǐng)求進(jìn)來,就會(huì)去桶子拿一個(gè)令牌
- 比如這秒我有1001個(gè)請(qǐng)求,我就去桶子里邊拿1001個(gè)令牌,此時(shí)可能會(huì)出現(xiàn)兩種情況:
- 桶子里邊沒有1001個(gè)令牌,只有1000個(gè),那沒拿到令牌的請(qǐng)求只能被阻塞了(等待)
- 桶子里邊有1001個(gè)令牌,所有請(qǐng)求都可以執(zhí)行。
令牌桶算法支持網(wǎng)絡(luò)上的突發(fā)流量
**漏桶和令牌桶的區(qū)別:**從上面的例子估計(jì)大家也能看出來了,漏桶只能以固定的速率去處理請(qǐng)求,而令牌桶可以以桶子最大的令牌數(shù)去處理請(qǐng)求
二、RateLimiter使用
RateLimiter是Guava的一個(gè)限流組件,我這邊的系統(tǒng)就有用到這個(gè)限流組件,使用起來十分方便。
引入pom依賴:
com.google.guava guava 20.0
RateLimiter它是基于令牌桶算法的,API非常簡單,看以下的Demo:
- public static void main(String[] args) {
- //線程池
- ExecutorService exec = Executors.newCachedThreadPool();
- //速率是每秒只有3個(gè)許可
- final RateLimiter rateLimiter = RateLimiter.create(3.0);
- for (int i = 0; i < 100; i++) {
- final int no = i;
- Runnable runnable = new Runnable() {
- @Override
- public void run() {
- try {
- //獲取許可
- rateLimiter.acquire();
- System.out.println("Accessing: " + no + ",time:"
- + new SimpleDateFormat("yy-MM-dd HH:mm:ss").format(new Date()));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- };
- //執(zhí)行線程
- exec.execute(runnable);
- }
- //退出線程池
- exec.shutdown();
- }
我們可以從結(jié)果看出,每秒只能執(zhí)行三個(gè):
三、分布式限流
RateLimiter是一個(gè)單機(jī)的限流組件,如果是分布式應(yīng)用的話,該怎么做?
可以使用Redis+Lua的方式來實(shí)現(xiàn),大致的lua腳本代碼如下:
- local key = "rate.limit:" .. KEYS[1] --限流KEY
- local limit = tonumber(ARGV[1]) --限流大小
- local current = tonumber(redis.call('get', key) or "0")
- if current + 1 > limit then --如果超出限流大小
- return 0
- else --請(qǐng)求數(shù)+1,并設(shè)置1秒過期
- redis.call("INCRBY", key,"1")
- redis.call("expire", key,"1")
- return current + 1
Java代碼如下:
- public static boolean accquire() throws IOException, URISyntaxException {
- Jedis jedis = new Jedis("127.0.0.1");
- File luaFile = new File(RedisLimitRateWithLUA.class.getResource("/").toURI().getPath() + "limit.lua");
- String luaScript = FileUtils.readFileToString(luaFile);
- String key = "ip:" + System.currentTimeMillis()/1000; // 當(dāng)前秒
- String limit = "5"; // 最大限制
- List
keys = new ArrayList (); - keys.add(key);
- List
args = new ArrayList (); - args.add(limit);
- Long result = (Long)(jedis.eval(luaScript, keys, args)); // 執(zhí)行l(wèi)ua腳本,傳入?yún)?shù)
- return result == 1;
- }
解釋:
- Java代碼傳入key和最大的限制limit參數(shù)進(jìn)lua腳本
- 執(zhí)行l(wèi)ua腳本(lua腳本判斷當(dāng)前key是否超過了最大限制limit)
- 如果超過,則返回0(限流)
- 如果沒超過,返回1(程序繼續(xù)執(zhí)行)
本文題目:生活中隨處可見的限流,在Java中又是怎么應(yīng)用的呢?
本文URL:http://m.5511xx.com/article/cdoosij.html


咨詢
建站咨詢
