场景示例:
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