服務網關---基於Nginx+lua+Redis的服務降級設計(二)

一:服務限流功能點
    1:根據請求入參中的服務標識判斷nginx後端服務是否處於流量限制中。如果是,則全部限制訪問,否則,轉發請求到後端服務。
    2:容錯機制,如果Redis宕機等異常,限流模塊失效,所有客戶端請求放行。
    3:是否開啓限流,及限流類型(AF:全部請求限制訪問,PF:設置閾值,每秒限制請求多少次)可熱加載。

二:設計思路
    1:在Reids中設置服務鍵值標識,Y標識限速,其它表示不限速。
    2:在Nginx中獲取請求體,判斷服務標識是否流量控制中,如果是,即攔截請求返回403,否則通過。
    3:如果在連接Redis和從Redis中讀取數據時發生異常,則跳出限流模塊,轉發請求到服務端。

三:PF(服務降級)

     由於上一章節以完成AF(服務下線)的設計說明:https://blog.csdn.net/qq_35723073/article/details/87930011

     這次主要說明PF(服務降級)的設計說明。

     申請Nginx共享內存,存儲限流類型標識limit_flag(NF:不限流、AF:服務下線、PF:服務降級)。

默認爲NF。當limit_flag爲空或NF時即正常處理業務請求。

四:邏輯流程

五:核心設計說明

      在Redis中根據服務名設置一個鍵(incr),並設置失效時間(expire)。

      例如: incr  SRV_NAME          EXPIRE SRV_NAME  60

      再設置一個鍵值表示最大調用量。 例如 set  count  10

      這樣,當客戶端每次調用此服務時,incr將自動加1,當在60s內大於10筆時,將會被限流。

      備註:Redis中支持lua代碼執行,需要把自增鍵和計數的邏輯在Redis中執行,才能保證事務的唯一性。因爲Redis是單進程的。可以使用eval()方法。

六:代碼實現

     由於此文章只爲大家分享一個設計思路,且博主代碼涉及公司的業務。只能貼出博主設計初期的部分代碼。

   local my_eval_str = [==[
local prestr = "LIMIT_RATE"
local srv_name = ARGV[1]
local flag = ARGV[2]
local retmsg = "0:OK"
local call_counts = 0
local mykey = ''
local key_time = ''
local key_counts = ''
local sf = string.format
if 'F1'==flag then
    mykey  =    sf("%s.%s.%s",   prestr,flag,srv_name)
    key_time  = sf("%s.%s.%s.%s",prestr,flag,srv_name,"TIME")
    key_counts= sf("%s.%s.%s.%s",prestr,flag,srv_name,"COUNTS")
else
    return retmsg
end

local r = redis.pcall('GET', key_time)
if not r then
    retmsg = sf("-1:%s not set value",key_time)
    return retmsg
end
local key_time_value = tonumber(r)
r = redis.pcall('GET', key_counts)
if not r then
    retmsg = sf("-1:%s not set value",key_counts)
    return retmsg
end
local key_counts_value = tonumber(r)
if 0 == key_counts_value then
    return sf("1:%s is 0",key_counts)
end
r = redis.pcall('INCR', mykey)
if type(r) == "number" then
    if r == 1 then
        r = redis.pcall('EXPIRE', mykey, key_time_value)
        call_counts = 1
    else
        call_counts = r
    end
    if call_counts>key_counts_value then
        retmsg = sf("1:%s must be forbidden[%s>%s]",mykey,call_counts,key_counts_value)
    else
        retmsg = sf("0:%s you can access[%s<%s]",mykey,call_counts,key_counts_value)
    end
else
    retmsg = sf("-1:%s incr err",mykey)
end
return retmsg
]==]


res,err = red:eval(my_eval_str,0,srv_name,flag)

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章