最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python+redis通過(guò)限流保護(hù)高并發(fā)系統(tǒng)

 更新時(shí)間:2020年04月15日 10:25:38   作者:cool小伙  
這篇文章主要介紹了Python+redis通過(guò)限流保護(hù)高并發(fā)系統(tǒng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

保護(hù)高并發(fā)系統(tǒng)的三大利器:緩存、降級(jí)和限流。那什么是限流呢?用我沒(méi)讀過(guò)太多書(shū)的話來(lái)講,限流就是限制流量。我們都知道服務(wù)器的處理能力是有上限的,如果超過(guò)了上限繼續(xù)放任請(qǐng)求進(jìn)來(lái)的話,可能會(huì)發(fā)生不可控的后果。而通過(guò)限流,在請(qǐng)求數(shù)量超出閾值的時(shí)候就排隊(duì)等待甚至拒絕服務(wù),就可以使系統(tǒng)在扛不住過(guò)高并發(fā)的情況下做到有損服務(wù)而不是不服務(wù)。

舉個(gè)例子,如各地都出現(xiàn)口罩緊缺的情況,廣州政府為了緩解市民買不到口罩的狀況,上線了預(yù)約服務(wù),只有預(yù)約到的市民才能到指定的藥店購(gòu)買少量口罩。這就是生活中限流的情況,說(shuō)這個(gè)也是希望大家這段時(shí)間保護(hù)好自己,注意防護(hù) :)

接下來(lái)就跟大家分享下接口限流的常見(jiàn)玩法吧,部分算法用python+redis粗略實(shí)現(xiàn)了一下,關(guān)鍵是圖解?。∧闫?,你細(xì)品~

固定窗口法

固定窗口法是限流算法里面最簡(jiǎn)單的,比如我想限制1分鐘以內(nèi)請(qǐng)求為100個(gè),從現(xiàn)在算起的一分鐘內(nèi),請(qǐng)求就最多就是100個(gè),這分鐘過(guò)完的那一刻把計(jì)數(shù)器歸零,重新計(jì)算,周而復(fù)始。

偽代碼實(shí)現(xiàn)

def can_pass_fixed_window(user, action, time_zone=60, times=30):
  """
  :param user: 用戶唯一標(biāo)識(shí)
  :param action: 用戶訪問(wèn)的接口標(biāo)識(shí)(即用戶在客戶端進(jìn)行的動(dòng)作)
  :param time_zone: 接口限制的時(shí)間段
  :param time_zone: 限制的時(shí)間段內(nèi)允許多少請(qǐng)求通過(guò)
  """
  key = '{}:{}'.format(user, action)
  # redis_conn 表示redis連接對(duì)象
  count = redis_conn.get(key)
  if not count:
    count = 1
    redis_conn.setex(key, time_zone, count)
  if count < times:
    redis_conn.incr(key)
    return True

  return False

這個(gè)方法雖然簡(jiǎn)單,但有個(gè)大問(wèn)題是無(wú)法應(yīng)對(duì)兩個(gè)時(shí)間邊界內(nèi)的突發(fā)流量。如上圖所示,如果在計(jì)數(shù)器清零的前1秒以及清零的后1秒都進(jìn)來(lái)了100個(gè)請(qǐng)求,那么在短時(shí)間內(nèi)服務(wù)器就接收到了兩倍的(200個(gè))請(qǐng)求,這樣就有可能壓垮系統(tǒng)。會(huì)導(dǎo)致上面的問(wèn)題是因?yàn)槲覀兊慕y(tǒng)計(jì)精度還不夠,為了將臨界問(wèn)題的影響降低,我們可以使用滑動(dòng)窗口法。

滑動(dòng)窗口法

滑動(dòng)窗口法,簡(jiǎn)單來(lái)說(shuō)就是隨著時(shí)間的推移,時(shí)間窗口也會(huì)持續(xù)移動(dòng),有一個(gè)計(jì)數(shù)器不斷維護(hù)著窗口內(nèi)的請(qǐng)求數(shù)量,這樣就可以保證任意時(shí)間段內(nèi),都不會(huì)超過(guò)最大允許的請(qǐng)求數(shù)。例如當(dāng)前時(shí)間窗口是0s~60s,請(qǐng)求數(shù)是40,10s后時(shí)間窗口就變成了10s~70s,請(qǐng)求數(shù)是60。

時(shí)間窗口的滑動(dòng)和計(jì)數(shù)器可以使用redis的有序集合(sorted set)來(lái)實(shí)現(xiàn)。score的值用毫秒時(shí)間戳來(lái)表示,可以利用當(dāng)前時(shí)間戳-時(shí)間窗口的大小來(lái)計(jì)算出窗口的邊界,然后根據(jù)score的值做一個(gè)范圍篩選就可以圈出一個(gè)窗口;value的值僅作為用戶行為的唯一標(biāo)識(shí),也用毫秒時(shí)間戳就好。最后統(tǒng)計(jì)一下窗口內(nèi)的請(qǐng)求數(shù)再做判斷即可。

偽代碼實(shí)現(xiàn)

def can_pass_slide_window(user, action, time_zone=60, times=30):
  """
  :param user: 用戶唯一標(biāo)識(shí)
  :param action: 用戶訪問(wèn)的接口標(biāo)識(shí)(即用戶在客戶端進(jìn)行的動(dòng)作)
  :param time_zone: 接口限制的時(shí)間段
  :param time_zone: 限制的時(shí)間段內(nèi)允許多少請(qǐng)求通過(guò)
  """
  key = '{}:{}'.format(user, action)
  now_ts = time.time() * 1000
  # value是什么在這里并不重要,只要保證value的唯一性即可,這里使用毫秒時(shí)間戳作為唯一值
  value = now_ts 
  # 時(shí)間窗口左邊界
  old_ts = now_ts - (time_zone * 1000)
  # 記錄行為
  redis_conn.zadd(key, value, now_ts)
  # 刪除時(shí)間窗口之前的數(shù)據(jù)
  redis_conn.zremrangebyscore(key, 0, old_ts)
  # 獲取窗口內(nèi)的行為數(shù)量
  count = redis_conn.zcard(key)
  # 設(shè)置一個(gè)過(guò)期時(shí)間免得占空間
  redis_conn.expire(key, time_zone + 1)
  if not count or count < times:
    return True
  return False

雖然滑動(dòng)窗口法避免了時(shí)間界限的問(wèn)題,但是依然無(wú)法很好解決細(xì)時(shí)間粒度上面請(qǐng)求過(guò)于集中的問(wèn)題,就例如限制了1分鐘請(qǐng)求不能超過(guò)60次,請(qǐng)求都集中在59s時(shí)發(fā)送過(guò)來(lái),這樣滑動(dòng)窗口的效果就大打折扣。 為了使流量更加平滑,我們可以使用更加高級(jí)的令牌桶算法和漏桶算法。

令牌桶法

令牌桶算法的思路不復(fù)雜,它先以固定的速率生成令牌,把令牌放到固定容量的桶里,超過(guò)桶容量的令牌則丟棄,每來(lái)一個(gè)請(qǐng)求則獲取一次令牌,規(guī)定只有獲得令牌的請(qǐng)求才能放行,沒(méi)有獲得令牌的請(qǐng)求則丟棄。

偽代碼實(shí)現(xiàn)

def can_pass_token_bucket(user, action, time_zone=60, times=30):
  """
  :param user: 用戶唯一標(biāo)識(shí)
  :param action: 用戶訪問(wèn)的接口標(biāo)識(shí)(即用戶在客戶端進(jìn)行的動(dòng)作)
  :param time_zone: 接口限制的時(shí)間段
  :param time_zone: 限制的時(shí)間段內(nèi)允許多少請(qǐng)求通過(guò)
  """
  # 請(qǐng)求來(lái)了就倒水,倒水速率有限制
  key = '{}:{}'.format(user, action)
  rate = times / time_zone # 令牌生成速度
  capacity = times # 桶容量
  tokens = redis_conn.hget(key, 'tokens') # 看桶中有多少令牌
  last_time = redis_conn.hget(key, 'last_time') # 上次令牌生成時(shí)間
  now = time.time()
  tokens = int(tokens) if tokens else capacity
  last_time = int(last_time) if last_time else now
  delta_tokens = (now - last_time) * rate # 經(jīng)過(guò)一段時(shí)間后生成的令牌
  if delta_tokens > 1:
    tokens = tokens + tokens # 增加令牌
    if tokens > tokens:
      tokens = capacity
    last_time = time.time() # 記錄令牌生成時(shí)間
    redis_conn.hset(key, 'last_time', last_time)

  if tokens >= 1:
    tokens -= 1 # 請(qǐng)求進(jìn)來(lái)了,令牌就減少1
    redis_conn.hset(key, 'tokens', tokens)
    return True
  return False

令牌桶法限制的是請(qǐng)求的平均流入速率,優(yōu)點(diǎn)是能應(yīng)對(duì)一定程度上的突發(fā)請(qǐng)求,也能在一定程度上保持流量的來(lái)源特征,實(shí)現(xiàn)難度不高,適用于大多數(shù)應(yīng)用場(chǎng)景。

漏桶算法

漏桶算法的思路與令牌桶算法有點(diǎn)相反。大家可以將請(qǐng)求想象成是水流,水流可以任意速率流入漏桶中,同時(shí)漏桶以固定的速率將水流出。如果流入速度太大會(huì)導(dǎo)致水滿溢出,溢出的請(qǐng)求被丟棄。

通過(guò)上圖可以看出漏桶法的特點(diǎn)是:不限制請(qǐng)求流入的速率,但是限制了請(qǐng)求流出的速率。這樣突發(fā)流量可以被整形成一個(gè)穩(wěn)定的流量,不會(huì)發(fā)生超頻。

關(guān)于漏桶算法的實(shí)現(xiàn)方式有一點(diǎn)值得注意,我在瀏覽相關(guān)內(nèi)容時(shí)發(fā)現(xiàn)網(wǎng)上大多數(shù)對(duì)于漏桶算法的偽代碼實(shí)現(xiàn),都只是實(shí)現(xiàn)了

根據(jù)維基百科,漏桶算法的實(shí)現(xiàn)理論有兩種,分別是基于 meter 的和基于 queue 的,他們實(shí)現(xiàn)的具體思路不同,我大概介紹一下。

基于meter的漏桶

基于 meter 的實(shí)現(xiàn)相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,其實(shí)它就有一個(gè)計(jì)數(shù)器,然后有消息要發(fā)送的時(shí)候,就看計(jì)數(shù)器夠不夠,如果計(jì)數(shù)器沒(méi)有滿的話,那么這個(gè)消息就可以被處理,如果計(jì)數(shù)器不足以發(fā)送消息的話,那么這個(gè)消息將會(huì)被丟棄。

那么這個(gè)計(jì)數(shù)器是怎么來(lái)的呢,基于 meter 的形式的計(jì)數(shù)器就是發(fā)送的頻率,例如你設(shè)置得頻率是不超過(guò) 5條/s ,那么計(jì)數(shù)器就是 5,在一秒內(nèi)你每發(fā)送一條消息就減少一個(gè),當(dāng)你發(fā)第 6 條的時(shí)候計(jì)時(shí)器就不夠了,那么這條消息就被丟棄了。

這種實(shí)現(xiàn)有點(diǎn)類似最開(kāi)始介紹的固定窗口法,只不過(guò)時(shí)間粒度再小一些,偽代碼就不上了。

基于queue的漏桶

基于 queue 的實(shí)現(xiàn)起來(lái)比較復(fù)雜,但是原理卻比較簡(jiǎn)單,它也存在一個(gè)計(jì)數(shù)器,這個(gè)計(jì)數(shù)器卻不表示速率限制,而是表示 queue 的大小,這里就是當(dāng)有消息要發(fā)送的時(shí)候看 queue 中是否還有位置,如果有,那么就將消息放進(jìn) queue 中,這個(gè) queue 以 FIFO 的形式提供服務(wù);如果 queue 沒(méi)有位置了,消息將被拋棄。

在消息被放進(jìn) queue 之后,還需要維護(hù)一個(gè)定時(shí)器,這個(gè)定時(shí)器的周期就是我們?cè)O(shè)置的頻率周期,例如我們?cè)O(shè)置得頻率是 5條/s,那么定時(shí)器的周期就是 200ms,定時(shí)器每 200ms 去 queue 里獲取一次消息,如果有消息,那么就發(fā)送出去,如果沒(méi)有就輪空。

注意,網(wǎng)上很多關(guān)于漏桶法的偽代碼實(shí)現(xiàn)只實(shí)現(xiàn)了水流入桶的部分,沒(méi)有實(shí)現(xiàn)關(guān)鍵的水從桶中漏出的部分。如果只實(shí)現(xiàn)了前半部分,其實(shí)跟令牌桶沒(méi)有大的區(qū)別噢😯

如果覺(jué)得上面的都太難,不好實(shí)現(xiàn),那么我墻裂建議你嘗試一下redis-cell這個(gè)模塊!

redis-cell

Redis 4.0 提供了一個(gè)限流 Redis 模塊,它叫 redis-cell。該模塊也使用了漏斗算法,并提供了原子的限流指令。有了這個(gè)模塊,限流問(wèn)題就非常簡(jiǎn)單了。 這個(gè)模塊需要單獨(dú)安裝,安裝教程網(wǎng)上很多,它只有一個(gè)指令:

CL.THROTTLE

CL.THROTTLE user123 15 30 60 1
▲ ▲ ▲ ▲ ▲
| | | | └───── apply 1 operation (default if omitted) 每次請(qǐng)求消耗的水滴
| | └──┴─────── 30 operations / 60 seconds 漏水的速率
| └───────────── 15 max_burst 漏桶的容量
└─────────────────── key “user123” 用戶行為

執(zhí)行以上命令之后,redis會(huì)返回如下信息:

> cl.throttle laoqian:reply 15 30 60
1) (integer) 0 # 0 表示允許,1表示拒絕
2) (integer) 16 # 漏桶容量
3) (integer) 15 # 漏桶剩余空間left_quota
4) (integer) -1 # 如果拒絕了,需要多長(zhǎng)時(shí)間后再試(漏桶有空間了,單位秒)
5) (integer) 2 # 多長(zhǎng)時(shí)間后,漏桶完全空出來(lái)(單位秒)

有了上面的redis模塊,就可以輕松對(duì)付大多數(shù)的限流場(chǎng)景了。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Python隊(duì)列Queue實(shí)現(xiàn)詳解

    Python隊(duì)列Queue實(shí)現(xiàn)詳解

    這篇文章主要介紹了Python隊(duì)列Queue實(shí)現(xiàn)詳解,隊(duì)列是一種列表,隊(duì)列用于存儲(chǔ)按順序排列的數(shù)據(jù),隊(duì)列是一種先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),不同的是隊(duì)列只能在隊(duì)尾插入元素,在隊(duì)首刪除元素,需要的朋友可以參考下
    2023-07-07
  • python如何將多個(gè)模型的ROC曲線繪制在一張圖(含圖例)

    python如何將多個(gè)模型的ROC曲線繪制在一張圖(含圖例)

    這篇文章主要給大家介紹了關(guān)于python如何將多個(gè)模型的ROC曲線繪制在一張圖的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-02-02
  • Python圖像處理Pillow庫(kù)的安裝使用

    Python圖像處理Pillow庫(kù)的安裝使用

    本文詳細(xì)介紹了Python第三方庫(kù)Pillow的使用,通過(guò)導(dǎo)入Pillow庫(kù)、打開(kāi)和保存圖像、基本圖像操作以及圖像處理高級(jí)功能的代碼示例,我們了解了Pillow庫(kù)的強(qiáng)大功能和靈活性,感興趣的朋友跟隨小編一起看看吧
    2023-07-07
  • 快速解決cv2.imread()讀取圖像為BGR的問(wèn)題

    快速解決cv2.imread()讀取圖像為BGR的問(wèn)題

    這篇文章主要介紹了快速解決cv2.imread()讀取圖像為BGR的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-03-03
  • Python openpyxl模塊原理及用法解析

    Python openpyxl模塊原理及用法解析

    這篇文章主要介紹了Python openpyxl模塊原理及用法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • python提效小工具之統(tǒng)計(jì)xmind用例數(shù)量(源碼)

    python提效小工具之統(tǒng)計(jì)xmind用例數(shù)量(源碼)

    這篇文章主要介紹了python提效小工具之統(tǒng)計(jì)xmind用例數(shù)量,利用python開(kāi)發(fā)小工具,實(shí)現(xiàn)同一份xmind文件中一個(gè)或多個(gè)sheet頁(yè)的用例數(shù)量統(tǒng)計(jì)功能,需要的朋友可以參考下
    2022-10-10
  • Python的Matplotlib庫(kù)圖像復(fù)現(xiàn)學(xué)習(xí)

    Python的Matplotlib庫(kù)圖像復(fù)現(xiàn)學(xué)習(xí)

    這篇文章主要給大家介紹了關(guān)于如何利用Matplotlib庫(kù)圖像復(fù)現(xiàn),matplotlib模塊提供了很高級(jí)和非常友好的使用方式,使用起來(lái)也是非常方便的,需要的朋友可以參考下
    2021-08-08
  • Python正則表達(dá)式的應(yīng)用詳解

    Python正則表達(dá)式的應(yīng)用詳解

    這篇文章主要介紹了Python中正則表達(dá)式的詳細(xì)教程,正則表達(dá)式是Python學(xué)習(xí)進(jìn)階當(dāng)中的重要內(nèi)容,需要的朋友可以參考下,希望能給你帶來(lái)幫助
    2021-08-08
  • pytorch中的named_parameters()和parameters()

    pytorch中的named_parameters()和parameters()

    這篇文章主要介紹了pytorch中的named_parameters()和parameters()使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Python繪制柱狀圖堆疊圖完整代碼

    Python繪制柱狀圖堆疊圖完整代碼

    有個(gè)朋友要求幫忙繪制堆疊柱狀圖,查閱了一些文檔之后也算是完成了,只是一個(gè)小demo,下面我就記錄一下,這篇文章主要給大家介紹了關(guān)于Python繪制柱狀圖堆疊圖的相關(guān)資料,需要的朋友可以參考下
    2023-09-09

最新評(píng)論

肇东市| 潞西市| 南部县| 措勤县| 木里| 曲沃县| 若尔盖县| 淳安县| 宜君县| 来安县| 日喀则市| 新乐市| 樟树市| 平邑县| 会同县| 连平县| 健康| 方城县| 宜阳县| 平昌县| 鹤山市| 永城市| 三明市| 湟源县| 凉山| 黔东| 鸡西市| 行唐县| 武城县| 芦山县| 长丰县| 隆安县| 五大连池市| 睢宁县| 衡东县| 新蔡县| 霍州市| 平和县| 肇源县| 高淳县| 阿鲁科尔沁旗|