服务网关---基于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时即正常处理业务请求。

四:逻辑流程

服务网关---基于Nginx+lua+Redis的服务降级设计(二)_第1张图片

五:核心设计说明

      在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)

 

 

你可能感兴趣的:(Nginx+Lua,openresty,nginx,lua,redis)