日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

c++ lua 可以做什么_Redis令牌桶算法(全网最全,后续可以接入lua做原子性操作)...

發(fā)布時(shí)間:2024/1/23 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c++ lua 可以做什么_Redis令牌桶算法(全网最全,后续可以接入lua做原子性操作)... 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一 、場(chǎng)景描述

在開(kāi)發(fā)接口服務(wù)器的過(guò)程中,為了防止客戶端對(duì)于接口的濫用,保護(hù)服務(wù)器的資源, 通常來(lái)說(shuō)我們會(huì)對(duì)于服務(wù)器上的各種接口進(jìn)行調(diào)用次數(shù)的限制。比如對(duì)于某個(gè) 用戶,他在一個(gè)時(shí)間段(interval)內(nèi),比如 1 分鐘,調(diào)用服務(wù)器接口的次數(shù)不能夠 大于一個(gè)上限(limit),比如說(shuō) 100 次。如果用戶調(diào)用接口的次數(shù)超過(guò)上限的話,就直接拒絕用戶的請(qǐng)求,返回錯(cuò)誤信息。

服務(wù)接口的流量控制策略:分流、降級(jí)、限流等。本文討論下限流策略,雖然降低了服務(wù)接口的訪問(wèn)頻率和并發(fā)量,卻換取服務(wù)接口和業(yè)務(wù)應(yīng)用系統(tǒng)的高可用。

二、常用的限流算法

1、漏桶算法

漏桶(Leaky Bucket)算法思路很簡(jiǎn)單,水(請(qǐng)求)先進(jìn)入到漏桶里,漏桶以一定的速度出水(接口有響應(yīng)速率),當(dāng)水流入速度過(guò)大會(huì)直接溢出(訪問(wèn)頻率超過(guò)接口響應(yīng)速率),然后就拒絕請(qǐng)求,可以看出漏桶算法能強(qiáng)行限制數(shù)據(jù)的傳輸速率.示意圖如下:

可見(jiàn)這里有兩個(gè)變量,一個(gè)是桶的大小,支持流量突發(fā)增多時(shí)可以存多少的水(burst),另一個(gè)是水桶漏洞的大小(rate)。

因?yàn)槁┩暗穆┏鏊俾适枪潭ǖ膮?shù),所以,即使網(wǎng)絡(luò)中不存在資源沖突(沒(méi)有發(fā)生擁塞),漏桶算法也不能使流突發(fā)(burst)到端口速率.因此,漏桶算法對(duì)于存在突發(fā)特性的流量來(lái)說(shuō)缺乏效率.

2、令牌桶算法

令牌桶算法(Token Bucket)和 Leaky Bucket 效果一樣但方向相反的算法,更加容易理解.隨著 時(shí)間流逝,系統(tǒng)會(huì)按恒定1/QPS時(shí)間間隔(如果QPS=100,則間隔是10ms)往桶里加入Token(想象和漏洞漏水相反,有個(gè)水龍頭在不斷的加水),如果桶已經(jīng)滿了就不再加了.新請(qǐng)求來(lái)臨時(shí),會(huì)各自拿走一個(gè)Token,如果沒(méi)有Token可拿了就阻塞或者拒絕服務(wù).

令牌桶的另外一個(gè)好處是可以方便的改變速度. 一旦需要提高速率,則按需提高放入桶中的令牌的速率. 一般會(huì)定時(shí)(比如100毫秒)往桶中增加一定數(shù)量的令牌, 有些變種算法則實(shí)時(shí)的計(jì)算應(yīng)該增加的令牌的數(shù)量.

三、基于PHP+Redis實(shí)現(xiàn)的令牌桶算法

<?php namespaceApi\Lib; /** * 限流控制 */ classRateLimit { private$minNum=60;//單個(gè)用戶每分訪問(wèn)數(shù) private$dayNum=10000;//單個(gè)用戶每天總的訪問(wèn)量 publicfunctionminLimit($uid){ $minNumKey=$uid.'_minNum'; $dayNumKey=$uid.'_dayNum'; $resMin=$this->getRedis($minNumKey,$this->minNum,60); $resDay=$this->getRedis($minNumKey,$this->minNum,86400); if(!$resMin['status']||!$resDay['status']){ exit($resMin['msg'].$resDay['msg']); } } publicfunctiongetRedis($key,$initNum,$expire){ $nowtime=time(); $result=['status'=>true,'msg'=>'']; $redisObj=$this->di->get('redis'); $redis->watch($key); $limitVal=$redis->get($key); if($limitVal){ $limitVal=json_decode($limitVal,true); $newNum=min($initNum,($limitVal['num']-1)+(($initNum/$expire)*($nowtime-$limitVal['time']))); if($newNum>0){ $redisVal=json_encode(['num'=>$newNum,'time'=>time()]); }else{ return['status'=>false,'msg'=>'當(dāng)前時(shí)刻令牌消耗完!']; }}else{ $redisVal=json_encode(['num'=>$initNum,'time'=>time()]); } $redis->multi(); $redis->set($key,$redisVal); $rob_result=$redis->exec(); if(!$rob_result){ $result=['status'=>false,'msg'=>'訪問(wèn)頻次過(guò)多!']; } return$result; } }

代碼要點(diǎn):

1:首先定義規(guī)則

單個(gè)用戶每分鐘訪問(wèn)次數(shù)($minNum),單個(gè)用戶每天總的訪問(wèn)次數(shù)($dayNum),接口總的訪問(wèn)次數(shù)等不同的規(guī)則。

2:計(jì)算速率

該代碼示例以秒為最小的時(shí)間單位,速率=訪問(wèn)次數(shù)/時(shí)間($initNum / $expire)

3:每次訪問(wèn)后補(bǔ)充的令牌個(gè)數(shù)計(jì)算方式

獲取上次訪問(wèn)的時(shí)間即上次存入令牌的時(shí)間,計(jì)算當(dāng)前時(shí)刻與上次訪問(wèn)的時(shí)間差乘以速率就是此次需要補(bǔ)充的令牌個(gè)數(shù),注意補(bǔ)充令牌后總的令牌個(gè)數(shù)不能大于初始化的令牌個(gè)數(shù),以補(bǔ)充數(shù)和初始化數(shù)的最小值為準(zhǔn)。

4:程序流程

第一次訪問(wèn)時(shí)初始化令牌個(gè)數(shù)($minNum),存入Redis同時(shí)將當(dāng)前的時(shí)間戳存入以便計(jì)算下次需要補(bǔ)充的令牌個(gè)數(shù)。第二次訪問(wèn)時(shí)獲取剩余的令牌個(gè)數(shù),并添加本次應(yīng)該補(bǔ)充的令牌個(gè)數(shù),補(bǔ)充后如何令牌數(shù)>0則當(dāng)前訪問(wèn)是有效的可以訪問(wèn),否則令牌使用完畢不可訪問(wèn)。先補(bǔ)充令牌再判斷令牌是否>0的原因是由于還有速率這個(gè)概念即如果上次剩余的令牌為0但是本次應(yīng)該補(bǔ)充的令牌>1那么本次依然可以訪問(wèn)。

5:針對(duì)并發(fā)的處理

使用Redis的樂(lè)觀鎖機(jī)制

四、Redis樂(lè)觀鎖介紹

redis對(duì)事務(wù)的支持比較簡(jiǎn)單。redis只能保證一個(gè)客戶端發(fā)起的事務(wù)命令可以執(zhí)行,中間不會(huì)插入其他事務(wù)。因?yàn)閞edis是單線程的,所以做到上面這點(diǎn)很容易。一般redis接受到客戶端的命令后會(huì)立即執(zhí)行,但是如果客戶端發(fā)起multi命令,redis不會(huì)立即執(zhí)行,而是讓當(dāng)前連接進(jìn)入事務(wù)上下文,把命令放到隊(duì)列中,接受到exec命令后,redis會(huì)順序執(zhí)行隊(duì)列中的命令。并把執(zhí)行結(jié)果打包到一起返回客戶端,之后就結(jié)束了事務(wù)上下文。

一、簡(jiǎn)單的事務(wù)控制

這個(gè)例子可以看到:兩個(gè)set命令發(fā)出后并沒(méi)有立即執(zhí)行而是放到隊(duì)列中,redis接受到exec命令才開(kāi)始執(zhí)行。

如果有兩個(gè)線程同時(shí)修改了一個(gè)變量的值,如何控制事務(wù)回滾?下面看樂(lè)觀鎖怎么控制的?

二、樂(lè)觀鎖控制事務(wù)

1.什么是樂(lè)觀鎖?

大多是基于數(shù)據(jù)版本的記錄機(jī)制。什么是數(shù)據(jù)版本?就是為數(shù)據(jù)增加一個(gè)版本標(biāo)識(shí),即為數(shù)據(jù)庫(kù)表添加一個(gè)version字段,當(dāng)讀取數(shù)據(jù)時(shí),把數(shù)據(jù)庫(kù)版本一同讀出,當(dāng)做了修改后,將數(shù)據(jù)庫(kù)版本+1,同修改一起提交。如果提交數(shù)據(jù)的版本號(hào) >數(shù)據(jù)庫(kù)當(dāng)前版本號(hào),提交成功。如圖:

2.樂(lè)觀鎖實(shí)例

假設(shè)數(shù)據(jù)庫(kù)中賬戶信息表中有一個(gè)version字段,當(dāng)前值為1,賬戶余額為$500

這樣避免了操作員B用舊數(shù)據(jù)修改表中記錄的的可能。

3.在redis中怎么體現(xiàn)的?

redis中用watch監(jiān)視key,如果key在提交前被修改,則提交不成功。如下:

當(dāng)session1還沒(méi)來(lái)得及對(duì)age進(jìn)行修改,session2已經(jīng)將age的值設(shè)為30,session1再執(zhí)行的時(shí)候失敗,因?yàn)閟ession1對(duì)age加了樂(lè)觀鎖的緣故。

watch命令會(huì)監(jiān)視key,當(dāng)exec時(shí)如果監(jiān)視的key從調(diào)用watch后發(fā)生過(guò)變化,則整個(gè)事務(wù)會(huì)失敗。也可以調(diào)用watch多次監(jiān)視多個(gè)key。

三、redis事務(wù)存在的問(wèn)題

redis保證事務(wù)中的命令連續(xù)執(zhí)行,但是如果其中一條命令執(zhí)行失敗,事務(wù)并不回滾。

?為age +1的命令成功,因?yàn)閍nme是string類(lèi)型的,所以不能做加操作,命令有一個(gè)失敗也不會(huì)回滾,age的值已經(jīng)被修改了

做令牌桶算法還要注意的就是后面的方案還可以接入lua,利用redis+lua的原子性操作去進(jìn)行令牌的一些初始化,比如令牌速率,令牌數(shù)量這類(lèi)的,填充的數(shù)量。

原文:https://zhuanlan.zhihu.com/p/273486885

想要獲取學(xué)習(xí)實(shí)戰(zhàn)、高并發(fā)、架構(gòu)?、筆試面試資料請(qǐng)掃碼咨詢+薇薇微信

總結(jié)

以上是生活随笔為你收集整理的c++ lua 可以做什么_Redis令牌桶算法(全网最全,后续可以接入lua做原子性操作)...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。