使用redis的incr
可以很容易的實現乙個限速器
在redis的官方文件中也有詳細的示例
function limit_api_call
(ip)
ts =
current_unix_time()
keyname = ip+
":"+ts
current =
get(keyname)
if current !=
null and current >
10 then
error "too many requests per second"
endif current ==
null then
multi
incr
(keyname,1)
expire
(keyname,1)
exec
else
incr
(keyname,1)
endperform_api_call
()
現有乙個服務,1分鐘內只能接受乙個使用者不超過10次的請求,這時我們可以將使用者的ip位址設定為key,使用者每次時程式去redis中獲取該key的值,如果大於等於10則返回錯誤,否則給key對應的value+1即可,如果value為0,那麼再將該key設定1分鐘的過期時間。
但是現在有乙個需求,我們可以在乙個指定的時間內給使用者推送一條訊息,但是要求使用者每分鐘內只能接受1條訊息,每小時內接受的訊息不超過5條,一天內接受的訊息不超過10條。
比如,我現在向這個介面提交了一條資料,要求在2019-11-11 11:11:11時向乙個使用者傳送一條資料,那麼當我再提交一條資料,要求在2019-11-11 11:11:12是向同樣的使用者傳送一條資料,那麼介面就會返回錯誤。
此時,僅使用incr
是無法滿足該需求的。
使用set
或者zset
將使用者的ip+傳送日期
作為key,傳送時間轉換為當天的秒數作為value,插入到set
或者zset
中,每次向使用者提交資訊時,可以獲取到set
中的所有傳送時間,然後再一一比對,如果不滿足條件就返回錯誤。
以下是偽**實現
function
ratelimit
(ip,sendtime)
return
false
}
以下是golang的實現
func
ratelimit
(ctx context.context, ip string
, sendtime time.time)
error
zerostr := sendtime.
format
("2006-01-02"
) key := ip + zerostr
zero,
_:= time.
parse
("2006-01-02"
, zerostr)
// 獲取傳送時間距當天時間的秒數
second :=
int(sendtime.
sub(zero)
.seconds()
) ress, err := cache.
smembers
(ctx, key)
if err !=
nil// 處理返回引數,將string轉換為int
var sends [
]int
for_
, v :=
range ress }if
len(sends)
>=
10var expire bool
iflen
(sends)==0
var hourcount int
for_
, s :=
range sends
if math.
abs(
float64
(second-s)
)<
3600
}if hourcount >
5// 非同步更新
gofunc()
t.expire
( key,
int64
(ttl))}
t.commit()
}()return
nil}
以上就可以實現乙個指定時間的限速器。 如何用兩個棧實現乙個佇列
問題 如何用兩個棧實現乙個對列的功能?思路 從棧a入佇列,從棧b出佇列。佇列的2個最重要的操作,入佇列,出佇列。入佇列 從棧a入佇列。出佇列 分兩種情況 如果棧b不為空,直接彈出。如果棧b為空,將棧a中的資料全部彈入棧b中,再從棧b彈出資料 實現如下 queuebystack.cpp 定義控制台應用...
如何用C 實現乙個Whois的查詢
什麼是whois 簡單來說,whois就是乙個用來查詢網域名稱是否已經被註冊,以及註冊網域名稱的詳細資訊的資料庫 如網域名稱所有人 網域名稱註冊商 網域名稱註冊日期和過期日期等 通過whois來實現對網域名稱資訊的查詢 什麼去查詢whois?whois的查詢其實也是蠻簡單的,就是利用socket去連...
如何用Redis實現搜尋介面
大家如果是做後端開發的,想必都實現過列表查詢的介面,當然有的查詢條件很簡單,一條 sql 就搞定了。但有的查詢條件極其複雜,再加上庫表中設計的各種不合理,導致查詢介面特別難寫,然後加班什麼的就不用說了 不知各位有沒有這種感受呢 下面以乙個例子開始,這是某購物 的搜尋條件,如果讓你實現這樣的乙個搜尋介...