欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Redis分布式限流的幾種實現(xiàn)

 更新時間:2023年12月11日 09:32:11   作者:格林希爾  
分布式限流是指通過將限流策略嵌入到分布式系統(tǒng)中,以控制流量或保護(hù)服務(wù),本文就來介紹一下Redis分布式限流的幾種實現(xiàn),感興趣的可以了解一下

一、 簡介

分布式限流是指通過將限流策略嵌入到分布式系統(tǒng)中,以控制流量或保護(hù)服務(wù),保證系統(tǒng)在高并發(fā)訪問情況下不被過載。

分布式限流可以防止系統(tǒng)因大量請求同時到達(dá)導(dǎo)致壓力過大而崩潰,從而提高系統(tǒng)的穩(wěn)定性和可靠性。同時,它可以使得業(yè)務(wù)資源能夠更好地分配,提高系統(tǒng)的效率。

二、分布式限流

1 數(shù)據(jù)結(jié)構(gòu)

1.1 Redis List

Redis List 是一個可以輸入相同元素和非唯一元素的集合且支持在兩端進(jìn)行快速(O(1))插入和刪除元素。

1.2 Redis Set

Redis Set 是一個無序,但不允許重復(fù)元素的集合。您可以使用這些命令對Redis集進(jìn)行常規(guī)操作: SADD,SREM,SISMEMBER,SMEMBERS等。

1.3 Redis Sorted Set

Redis Sorted Set 是一個有序的、不重復(fù)的元素集合。每個元素都關(guān)聯(lián)著一個浮點數(shù)值(稱為 Score)。通過 Score 可以從小到大排序得到一個有序集合。

2 實現(xiàn)分布式限流

在Redis中可以使用令牌桶算法來實現(xiàn)分布式限速。具體方法為:

Step 1:創(chuàng)建一個列表作為Redis令牌桶

String key = "rate_limit:" + userId;
// 模擬用戶請求訪問
List<String> tokens = redis.lrange(key, 0, -1);

Step 2:設(shè)定令牌桶的基準(zhǔn)參數(shù)

int maxTokens = 50;
long timeInterval = 1 * 1000;
long now = System.currentTimeMillis();

Step 3:計算Redis中令牌數(shù)量

long expiredTokens = tokens.stream().filter(t -> Long.parseLong(t) < now - timeInterval).count();
tokens = tokens.subList((int) expiredTokens, tokens.size());
long remainTokens = maxTokens - tokens.size();

Step 4:基于令牌數(shù)量,判斷是否超過限制

if (remainTokens < 1) {
    throw new RateLimitException("請求太頻繁,請稍后再試!");
}

Step 5:如果沒有超出限制,則更新Redis中令牌數(shù)并設(shè)置過期時間

Long expiresIn = now + timeInterval;
redis.multi();
redis.rpush(key, String.valueOf(expiresIn));
redis.pexpire(key, timeInterval);
redis.exec();

3 實現(xiàn)原理分析

以上代碼所示首先需要建立Redis List用于存儲Token,其次需要設(shè)定令牌桶的基準(zhǔn)參數(shù)(比如最大Token數(shù)量和Token過期間隔等)。在用戶訪問請求時,需要計算Redis中的令牌數(shù)量,根據(jù)規(guī)則對訪問量進(jìn)行限制。如果沒有超過限制,則需要更新Redis List中令牌數(shù)并設(shè)置過期時間;如果超過了限制,則需要返回錯誤信息并拒絕服務(wù)。

整個過程中,需要注意并發(fā)訪問情況下的線程安全問題,并確保流量控制配置的公共協(xié)商,如最大QPS(Queries Per Second),哪些接口需限制流量等。

三、分布式限流算法

在實際的系統(tǒng)設(shè)計中,為了防止某一時刻出現(xiàn)大量請求導(dǎo)致系統(tǒng)崩潰,我們通常會采用限流策略來控制流量,而Redis作為分布式NoSQL數(shù)據(jù)庫,在限流中也有著廣泛的應(yīng)用。下面介紹一些Redis分布式限流的經(jīng)典算法。

1. 計數(shù)器算法

計數(shù)器算法比較簡單,直接利用Redis存儲每個IP或者用戶的請求次數(shù),當(dāng)請求次數(shù)超過預(yù)設(shè)閾值時拒絕服務(wù)。代碼如下:

public boolean isAllowed(String key, int limit, int timeout) {
    Jedis jedis = getJedis();
    long count = jedis.incr(key);
    if (count == 1) {
        jedis.expire(key, timeout);
    }
    boolean allowed = count <= limit;
    if (!allowed) {
        jedis.del(key);
    }
    jedis.close();
    return allowed;
}
  • key:需要限流的用戶標(biāo)識,可根據(jù)IP、UserID等進(jìn)行定義
  • limit:閾值,即允許的最大請求數(shù)
  • timeout:過期時間,對于計數(shù)器算法,一定要設(shè)置過期時間,否則緩存中的請求次數(shù)會一直不斷累加

2. 漏斗算法

漏斗算法的核心思想是將請求按照恒定的速率轉(zhuǎn)換為水流,有效控制請求超出服務(wù)處理能力的情況。漏斗算法實現(xiàn)代碼如下:

public boolean isAllowed(String key, int capacity, double leakRate, int reqCount) {
    Jedis jedis = getJedis();
    long nowTime = System.currentTimeMillis();
    String luaScript =
          "local currentCapacity = tonumber(redis.call('hget', KEYS[1], 'leftCapacity'))\n"
        + "if currentCapacity == nil then\n"
        + "    redis.call('hset', KEYS[1], 'lastTime', ARGV[2])\n"
        + "    redis.call('hset', KEYS[1], 'leftCapacity', ARGV[1] - 1)\n"
        + "    return 1\n"
        + "end\n"
        + "local changeTime = tonumber(redis.call('hget', KEYS[1], 'lastTime'))\n"
        + "local delayMillSeconds = nowTime - changeTime\n"
        + "local currentDelayCount = tonumber(delayMillSeconds*ARGV[3])\n"
        + "local currentCapacity = math.min(currentDelayCount+currentCapacity, ARGV[1])\n"
        + "if currentCapacity >= ARGV[4] then\n"
        + "    return 0\n"
        + "else\n"
        + "    redis.call('hset', KEYS[1], 'leftCapacity', currentCapacity-1)\n"
        + "    redis.call('hset', KEYS[1], 'lastTime', nowTime)\n"
        + "    return 1\n"
        + "end";
    Object result = jedis.eval(
        luaScript,
        Collections.singletonList(key),
        Arrays.asList(String.valueOf(capacity), String.valueOf(nowTime), String.valueOf(leakRate), String.valueOf(reqCount))
    );
    boolean allowed = (result instanceof Long ? (Long) result : 0L) == 1L;
    jedis.close();
    return allowed;
}
  • key:需要進(jìn)行限流的用戶標(biāo)識
  • capacity:漏斗容量,即最大允許請求數(shù)量
  • leakRate:漏嘴流水速率,保證有序的請求到達(dá)
  • reqCount:預(yù)計請求量,用于計算漏斗每次流出的數(shù)量

3. 令牌桶算法

令牌桶算法的特點是以一個固定的速率不斷產(chǎn)生令牌,并將令牌放入到桶中,訪問時若桶為空,則表示請求數(shù)超限。令牌桶算法實現(xiàn)代碼如下:

public boolean isAllowed(String key, int capacity, double rate, int reqCount) {
    long nowTime = System.currentTimeMillis();
    Jedis jedis = getJedis();
    String luaScript =
          "local currentLimit = tonumber(redis.call('get', KEYS[1]) or '0')\n"
        + "if currentLimit + ARGV[1] > tonumber(KEYS[2]) then\n"
        + "    return false\n"
        + "else\n"
        + "    redis.call('incrby', KEYS[1], ARGV[1])\n"
        + "    redis.call('expire', KEYS[1], ARGV[2])\n"
        + "    return true\n"
        + "end";
    Object result = jedis.eval(luaScript, 2, key, String.valueOf(capacity), String.valueOf(reqCount), String.valueOf(rate * (nowTime / 1000)));
    boolean allowed = (result instanceof Boolean ? (Boolean) result : false);
    jedis.close();
    return allowed;
}
  • key:需要進(jìn)行限流的用戶標(biāo)識
  • capacity:桶容量
  • rate:令牌發(fā)放速率
  • reqCount:請求數(shù)量

四、分布式限流實戰(zhàn)

1. 單機(jī)限流實現(xiàn)

假設(shè)我們有一個需求,需要限制每個IP一分鐘內(nèi)最多只能發(fā)送100個請求??梢酝ㄟ^Redis的INCR、EXPIRE等API操作來簡單實現(xiàn)單機(jī)限流。

public boolean isAllowed(String ip, int limit, int interval) {
    Jedis jedis = getJedis();
    String key = "ip:" + ip;
    long count = jedis.incr(key);
    if (count == 1) {
        jedis.expire(key, interval);
    }
    boolean allowed = count <= limit;
    if (!allowed) {
        jedis.del(key);
    }
    jedis.close();
    return allowed;
}

2. 基于Redis Clusters的分布式限流實現(xiàn)

當(dāng)業(yè)務(wù)規(guī)模擴(kuò)大時,單機(jī)的限流已經(jīng)無法滿足需求,這時候需要考慮使用Redis Clusters實現(xiàn)分布式限流。Clusers擴(kuò)展了原先Redis的功能,不僅支持橫向擴(kuò)展,而且提高了整個集群的可用性。限流算法同上,只是需要使用把數(shù)據(jù)分配到Cluser內(nèi)不同的節(jié)點上。

public boolean isAllowed(String ip, int limit, int interval) {
    JedisCluster jedis = getJedisCluster();
    String key = "ip:" + ip;
    long count = jedis.incr(key);
    if (count == 1) {
        jedis.expire(key, interval);
    }
    boolean allowed = count <= limit;
    if (!allowed) {
        jedis.del(key);
    }
    return allowed;
}

五、基于Redis分布式限流的優(yōu)化

1. 緩存擊穿

1.1 問題描述

在高并發(fā)場景下,如果存在大量的緩存未命中請求,將會導(dǎo)致訪問底層數(shù)據(jù)存儲系統(tǒng),這種情況被稱為緩存擊穿。

1.2 解決方案

1.2.1 使用互斥鎖

/**
 * 獲取緩存值方法
 * @param key 緩存鍵值
 * @return 緩存值
 */
public String getCacheValue(String key) {
    String value = cache.get(key);
    if (value == null) { //緩存未命中
        //使用互斥鎖
        Lock lock = redisson.getLock(key);
        if (lock.tryLock()) { //嘗試獲取鎖
            try {
                value = cache.get(key); //再次嘗試獲取緩存
                if (value == null) { //如果仍然未命中,從數(shù)據(jù)庫中獲取
                    value = db.get(key);
                    cache.put(key, value); //將查詢結(jié)果放入緩存
                }
            } finally {
                lock.unlock(); //釋放鎖
            }
        } else {
            Thread.sleep(100); //自旋一段時間后重試
            return getCacheValue(key);
        }
    }
    return value;
}

1.2.2 使用預(yù)熱機(jī)制

預(yù)熱機(jī)制是指在系統(tǒng)啟動的時候,提前加載熱點數(shù)據(jù)到緩存中,以減少緩存未命中請求。預(yù)熱的方式可以使用定時任務(wù)或者其他方式,在系統(tǒng)低峰期進(jìn)行加載。

2. 熱點key問題的解決方案

2.1 問題描述

在高并發(fā)場景下,如果某個key的請求量過大,將會導(dǎo)致這個key成為熱點key,從而導(dǎo)致緩存雪崩問題。

2.2 解決方案

2.2.1 分布式鎖

/**
 * 獲取緩存值方法
 * @param key 緩存鍵值
 * @return 緩存值
 */
public String getCacheValue(String key) {
    String value = cache.get(key);
    if (value == null) { //緩存未命中
        //使用分布式鎖
        RLock lock = redisson.getFairLock(key);
        if (lock.tryLock()) { //嘗試獲取鎖
            try {
                value = cache.get(key); //再次嘗試獲取緩存
                if (value == null) { //如果仍然未命中,從數(shù)據(jù)庫中獲取
                    value = db.get(key);
                    cache.put(key, value); //將查詢結(jié)果放入緩存
                }
            } finally {
                lock.unlock(); //釋放鎖
            }
        } else {
            Thread.sleep(100); //自旋一段時間后重試
            return getCacheValue(key);
        }
    }
    return value;
}

2.2.2 分布式緩存

使用分布式緩存將數(shù)據(jù)均勻地分散到多個節(jié)點上,從而避免單點瓶頸問題。

3. 并發(fā)競爭優(yōu)化

3.1 問題描述

在高并發(fā)場景下,對于某些資源的并發(fā)訪問將會導(dǎo)致性能瓶頸,需要進(jìn)行并發(fā)競爭優(yōu)化。

3.2 解決方案

3.2.1 使用限流器

限流器類似于信號燈,用于控制并發(fā)請求的數(shù)量,在高峰期可以采用漏桶算法或令牌桶算法進(jìn)行限流。

3.2.2 使用異步線程池

對于一些耗時的操作,可以使用異步線程池進(jìn)行處理,從而避免阻塞主線程,提升系統(tǒng)的并發(fā)能力。

到此這篇關(guān)于Redis分布式限流的幾種實現(xiàn)的文章就介紹到這了,更多相關(guān)Redis分布式限流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Django使用redis配置緩存的方法

    Django使用redis配置緩存的方法

    Redis是一個內(nèi)存數(shù)據(jù)庫由于其性能極高,因此經(jīng)常作為中間件、緩存使用,緩存某些內(nèi)容是為了保存昂貴計算的結(jié)果,這樣就不必在下次執(zhí)行計算,接下來通過本文給大家分享redis配置緩存的方法,感興趣的朋友一起看看吧
    2021-06-06
  • Redis和數(shù)據(jù)庫 數(shù)據(jù)同步問題的解決

    Redis和數(shù)據(jù)庫 數(shù)據(jù)同步問題的解決

    這篇文章主要介紹了Redis和數(shù)據(jù)庫 數(shù)據(jù)同步問題的解決操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • Redis exists命令bug分析(案例詳解)

    Redis exists命令bug分析(案例詳解)

    Redis EXISTS 命令用于檢查給定 key 是否存在,本文重點給大家介紹Redis exists命令bug分析,感興趣的朋友跟隨小編一起看看吧
    2022-02-02
  • 了解Redis常見應(yīng)用場景

    了解Redis常見應(yīng)用場景

    Redis是一個key-value存儲系統(tǒng),現(xiàn)在在各種系統(tǒng)中的使用越來越多,大部分情況下是因為其高性能的特性,被當(dāng)做緩存使用,這里介紹下Redis經(jīng)常遇到的使用場景
    2021-06-06
  • Redis一鍵巡檢腳本的實現(xiàn)

    Redis一鍵巡檢腳本的實現(xiàn)

    在使用Redis作為數(shù)據(jù)存儲的時候,定期進(jìn)行巡檢是非常重要的,本文主要介紹了Redis一鍵巡檢腳本的實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2024-06-06
  • Redis解決優(yōu)惠券秒殺應(yīng)用案例

    Redis解決優(yōu)惠券秒殺應(yīng)用案例

    這篇文章主要介紹了Redis解決優(yōu)惠券秒殺應(yīng)用案例,本文先講了搶購問題,指出其中會出現(xiàn)的多線程問題,提出解決方案采用悲觀鎖和樂觀鎖兩種方式進(jìn)行實現(xiàn),然后發(fā)現(xiàn)在搶購過程中容易出現(xiàn)一人多單現(xiàn)象,需要的朋友可以參考下
    2022-11-11
  • redis分布式鎖的8大坑總結(jié)梳理

    redis分布式鎖的8大坑總結(jié)梳理

    這篇文章主要介紹了redis分布式鎖的8大坑總結(jié)梳理,使用redis的分布式鎖,我們首先想到的可能是setNx命令,文章圍繞setNx命令展開詳細(xì)的內(nèi)容介紹,感興趣的小伙伴可以參考一下
    2022-07-07
  • Redis簡易延時隊列的實現(xiàn)示例

    Redis簡易延時隊列的實現(xiàn)示例

    在實際的業(yè)務(wù)場景中,經(jīng)常會遇到需要延時處理的業(yè)務(wù),本文就來介紹有下Redis簡易延時隊列的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • Redis的Zset類型及相關(guān)命令詳細(xì)講解

    Redis的Zset類型及相關(guān)命令詳細(xì)講解

    這篇文章主要介紹了Redis的Zset類型及相關(guān)命令的相關(guān)資料,有序集合Zset是一種Redis數(shù)據(jù)結(jié)構(gòu),它類似于集合Set,但每個元素都有一個關(guān)聯(lián)的分?jǐn)?shù)score,并且可以根據(jù)分?jǐn)?shù)對元素進(jìn)行排序,需要的朋友可以參考下
    2025-01-01
  • 基于Redis有序集合實現(xiàn)滑動窗口限流的步驟

    基于Redis有序集合實現(xiàn)滑動窗口限流的步驟

    滑動窗口算法是一種基于時間窗口的限流算法,通過動態(tài)地滑動窗口,可以動態(tài)調(diào)整限流的速率,Redis有序集合可以用來實現(xiàn)滑動窗口限流,本文介紹基于Redis有序集合實現(xiàn)滑動窗口限流,感興趣的朋友一起看看吧
    2024-12-12

最新評論