并发处理 - Lua实现功能降级

分类: 源代码 > Lua

场景示例:

    1. 商城或web站点的用户访问量出乎意料地增加了很多,超出了系统的负载能力, 系统有些扛不住,继而导致注 册,下单,支付什么的全部在绕圈卡住,继而导致公司业务损失了不少用户和订单。

    2. 网站只能承受1000个请求/s,突然来了10000个,nginx限流,把另外的9000个拒之门外,不转发给系统去处理

降级的目的

    降级的最终目的是保证核心服务的高可用。过程就是丢卒保车,有些服务是无法降级的,比如支付。

    当我们的服务器压力剧增为了保证核心功能的可用性 ,而选择性的降低一些功能的可用性,实时性,或者把这 些功能从不实时的缓存中获取,或者直接关闭该功能。这就是典型的丢车保帅了。 就比如贴吧类型的网站,当 服务器吃不消的时候,可以选择把发帖功能关闭,注册功能关闭,改密码,改头像这些都关了,为了确保登录 和浏览帖子这种核心的功能。

    降级的原理:就是降低次要功能的可用性实用性,增加核心功能的高可用性

    降级的方式:

        1. 配置中心手动降级

        2. 基于nginx的漏桶自动降级

 1. 配置中心手动降级,基于nginx接入层的手动降级代码示例:

--获取get或post参数--------------------
local request_method = ngx.var.request_method
local args = nil
local param = nil
--获取参数的值
if "GET" == request_method then
    args = ngx.req.get_uri_args()
elseif "POST" == request_method then
    ngx.req.read_body()
    args = ngx.req.get_post_args()
end
sku_id = args["sku_id"]
--关闭redis的函数--------------------
local function close_redis(redis_instance)
    if not redis_instance then
        return
    end
    local ok,err = redis_instance:close();
    if not ok then
        ngx.say("close redis error : ",err);
    end
end
--连接redis--------------------
local redis = require("resty.redis");
--local redis = require "redis"
-- 创建一个redis对象实例。在失败,返回nil和描述错误的字符串的情况下
local redis_instance = redis:new();
--设置后续操作的超时(以毫秒为单位)保护,包括connect方法
redis_instance:set_timeout(1000)
--建立连接
local ip = '127.0.0.1'
local port = 6379
--尝试连接到redis服务器正在侦听的远程主机和端口
local ok,err = redis_instance:connect(ip,port)
if not ok then
    ngx.say("connect redis error : ",err)
    return close_redis(redis_instance);
end
--从redis里面读取开关--------------------
local key = "level_goods_list_advert"
local switch, err = redis_instance:get(key)
if not switch then
    ngx.say("get msg error : ", err)
    return close_redis(redis_instance)
end
--得到的开关为空处理--------------------
if switch == ngx.null then
    switch = "FROM_DATA" --比如默认值
end
--当开关是要从服务中获取数据时--------------------
if "FROM_DATA" == switch then
    ngx.exec('/goods_list_advert_from_data');
--当开关是要从缓存中获取数据时--------------------
elseif "FROM_CACHE" == switch then
    local resp, err = redis_instance:get("nihao")
    ngx.say(resp)
--当开关是要从静态资源中获取数据时--------------------
elseif "FROM_STATIC" == switch then
    ngx.header.content_type="application/x-javascript;charset=utf-8"
    local file = "/etc/nginx/html/goods_list_advert.json"
    local f = io.open(file, "rb")
    local content = f:read("*all")
    f:close()
    ngx.print(content)
--当开关是要停掉数据获取时--------------------
elseif "SHUT_DOWN" == switch then
    ngx.say('no data')
end
基于nginx的漏桶自动降级代码示例: 
-- 加载nginx—lua限流模块
local limit_req = require "resty.limit.req"
-- 这里设置rate=50个请求/每秒,漏桶桶容量设置为1000个请求
-- 因为模块中控制粒度为毫秒级别,所以可以做到毫秒级别的平滑处理
local lim, err = limit_req.new("my_limit_req_store", 50, 1000)
if not lim then
    ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
    return ngx.exit(501)
end
local key = ngx.var.binary_remote_addr
local delay, err = lim:incoming(key, true)
ngx.say(delay)
if ( delay <0 or delay==nil ) then
    return ngx.exit(502)
end
-- delay值就是当前这个请求的等待时长,这个时长是通过resty.limit.req模块计算出来的
-- 1000以外的就溢出
if not delay then
    if err == "rejected" then
        return ngx.say("1000以外的就溢出")
        -- return ngx.exit(502)
    end
    ngx.log(ngx.ERR, "failed to limit req: ", err)
    return ngx.exit(502)
end
-- 50-100的等待从微服务+mysql获取实时数据;(100-50)/50 =1
if ( delay >0 and delay <=1 ) then
    ngx.sleep(delay)
    -- 100-400的直接从redis获取实时性略差的数据;(400-50)/50 =7
elseif ( delay >1 and delay <=7 ) then
    local resp, err = redis_instance:get("redis_goods_list_advert")
    ngx.say(resp)
    return
    -- 400-1000的从静态文件获取实时性非常低的数据(1000-50)/50 =19
elseif ( delay >7) then
    ngx.header.content_type="application/x-javascript;charset=utf-8"
    local file = "/etc/nginx/html/goods_list_advert.json"
    local f = io.open(file, "rb")
    local content = f:read("*all")
    f:close()
    ngx.print(content)
    return
end
ngx.say("进入查询微服务+mysql")
来源:原创 发布时间:2022-06-20 11:54:44