天天看點

Redis令牌桶限流

一 、場景描述

在開發接口伺服器的過程中,為了防止用戶端對于接口的濫用,保護伺服器的資源, 通常來說我們會對于伺服器上的各種接口進行調用次數的限制。比如對于某個 使用者,他在一個時間段(interval)内,比如 1 分鐘,調用伺服器接口的次數不能夠 大于一個上限(limit),比如說 100 次。如果使用者調用接口的次數超過上限的話,就直接拒絕使用者的請求,傳回錯誤資訊。

服務接口的流量控制政策:分流、降級、限流等。本文讨論下限流政策,雖然降低了服務接口的通路頻率和并發量,卻換取服務接口和業務應用系統的高可用。

二、常用的限流算法

1、漏桶算法

漏桶(Leaky Bucket)算法思路很簡單,水(請求)先進入到漏桶裡,漏桶以一定的速度出水(接口有響應速率),當水流入速度過大會直接溢出(通路頻率超過接口響應速率),然後就拒絕請求,可以看出漏桶算法能強行限制資料的傳輸速率.示意圖如下:

   

可見這裡有兩個變量,一個是桶的大小,支援流量突發增多時可以存多少的水(burst),另一個是水桶漏洞的大小(rate)。

因為漏桶的漏出速率是固定的參數,是以,即使網絡中不存在資源沖突(沒有發生擁塞),漏桶算法也不能使流突發(burst)到端口速率.是以,漏桶算法對于存在突發特性的流量來說缺乏效率.

2、令牌桶算法

令牌桶算法(Token Bucket)和 Leaky Bucket 效果一樣但方向相反的算法,更加容易了解.随着時間流逝,系統會按恒定1/QPS時間間隔(如果QPS=100,則間隔是10ms)往桶裡加入Token(想象和漏洞漏水相反,有個水龍頭在不斷的加水),如果桶已經滿了就不再加了.新請求來臨時,會各自拿走一個Token,如果沒有Token可拿了就阻塞或者拒絕服務.

令牌桶的另外一個好處是可以友善的改變速度. 一旦需要提高速率,則按需提高放入桶中的令牌的速率. 一般會定時(比如100毫秒)往桶中增加一定數量的令牌, 有些變種算法則實時的計算應該增加的令牌的數量.

三、基于PHP+Redis實作的令牌桶算法

<?php
namespace Api\Lib;

/**
 * 限流控制
 */
class RateLimit
{
    private $minNum = 60; //單個使用者每分通路數
    private $dayNum = 10000; //單個使用者每天總的通路量

    public function minLimit($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']);
        }
    }

    public function getRedis($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            

繼續閱讀