什么是降级, 为什么降级,降级的场景。
降级的最终目的是保证核心服务的高可用。过程就是丢卒保帅,有些服务是无法降级的,比如支付。 当我们的服务器压力剧增为了保证核心功能的可用性 ,而选择性的降低一些功能的可用性,或者直接关闭该功能。 这就是典型的丢车保帅了。 就比如贴吧类型的网站,当服务器吃不消的时候,可以选择把发帖功能关闭,注册功能 关闭,改密码,改头像这些都关了,为了确保登录和浏览帖子这种核心的功能。
降级的原理:就是降低次要功能的可用性实用性,增加核心功能的高可用性。 降级的实现原理是多样多样的。
例如:利用一个降级开关,以这个开关为判断依据,切换数据的获取方式,比如当mysql 负载高的时候,可以从mysql切换到redis,比如从redis切换到静态文件,比如从错误频发的新版本切换到老版本等 等。 这个开关是根据现状来配置的,比如当新版本错误频发的时候,我们可以配置这个开关为从老版本获取数据。
降级的种类
1.根据降级的开关位置:分为服务代码降级和开关前置降级。根据降级的开关位置:分为服务代码降级和开关前置降级。
2.根据降级的开关位置:分为服务代码降级和开关前置降级。根据降级的开关位置:分为服务代码降级和开关前置降级。
3.根据降级的性质:分为返回内容降级,限流降级,限速降级。根据降级的性质:分为返回内容降级,限流降级,限速降级。
限流限速:nginx 自带限流限速没空看,比如ngx_http_limit_req_module和ngx_http_limit_conn_module 。 但是这类模块只是提供了在nginx配置文件中进行简单的参数配置。 如果想更加灵活和功能更多,同学们可以编写自己的lua代码进行控制,而不是用现成的模块进行配置文件修 改。
根据降级的维护特点:分为手动降级和自动降级。
手动降级,是人为看到系统负载异常后,手动调整降级 自动降级,是系统监测到异常后,自动降级,自动降级虽然更加智能,但有时候自动脚本可能会干一些超乎预 料的事情
举例例如我们采用nginx+lua+redis对广告推荐模块实现前置降级,读降级,内容降级,手动降级(附自动降级)
nginx配置文件/etc/nginx/nginx.conf
user nobody; worker_processes 1; events { worker_connections 1024; } http { lua_package_path "/usr/share/lua/5.1/lua-resty-redis/lib/?.lua;;/usr/share/lua/5.1/lua-resty-redis-cluster/lib/resty‘7/?.lua;;"; lua_package_cpath "/usr/share/lua/5.1/lua-resty-redis-cluster/lib/libredis_slot.so;;"; include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name 127.0.0.1; server_name 192.168.232.100; #获取广告推荐数据 location /goods_list_advert { default_type 'application/x-javascript;charset=utf-8'; content_by_lua_file /etc/nginx/lua/goods_list_advert.lua; } #从服务层+mysql获取数据 location /goods_list_advert_from_data { #allow 127.0.0.1; #deny all; default_type 'application/x-javascript;charset=utf-8'; #rewrite https//www.baidu.com/ break; proxy_pass http://192.168.232.101:18306/goods/getList; #content_by_lua ' # ngx.say("从服务层+mysql获取数据") #'; } } }
lua降级代码:/etc/nginx/lua/goods_list_advert.lua
--获取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 --close_redis(redis_instance) --ngx.exit(ngx.OK)
测试我们访问/goods_list_advert地址根据设置不同redis的level_goods_list_advert值来体现不同的效果
自动降级
采用nginx+lua+redis对错误返回进行统计,当统计到的错误次数达到一定数值时,lua会判断这个数值, 然后 会 在/etc/nginx/lua/goods_list_advert.lua中添加如下代码:
--判断错误的响应,并进行计数, 后续便可以参考这个数值进行降级 if tonumber(ngx.var.status) == 200 then ngx.say(ngx.var.status) ngx.log(ngx.ERR,"upstream reponse status is " .. ngx.var.status .. ",please notice it") local error_count, err = redis_instance:get("error_count_goods_list_advert") --得到的数据为空处理 if error_count == ngx.null then error_count = 0 end error_count = error_count + 1 --统计错误次数到error_count_goods_list_advert local resp,err = redis_instance:set("error_count_goods_list_advert",error_count) if not resp then ngx.say("set msg error : ",err) return close_redis(redis_instance) end end close_redis(redis_instance)
当我们有意关掉一些服务让状态码不为200时, 可以看到redis中error_count_goods_list_advert的值在加1
接下来,你就可以在lua中添加判断, 比如设置一个阈值为100, 当error_count_goods_list_advert的值达到100时 (也就是错误返回达到100时),你就可以降级为请求另外一个版本(老版本程序)。
本文由:xiasohu168.com 作者:xiaoshu发表,转载请注明来源!