nginx+lua+redis實(shí)現(xiàn)降級的示例代碼
前言
商城或web站點(diǎn)的用戶訪問量出乎意料地增加了很多,超出了系統(tǒng)的負(fù)載能力, 系統(tǒng)有些扛不住,繼而導(dǎo)致注
冊,下單,支付什么的全部在繞圈卡住,繼而導(dǎo)致公司業(yè)務(wù)損失了不少用戶和訂單。。
一、引子
面對一大波訪問量出乎意料地涌入,超出了系統(tǒng)正常的負(fù)載范圍,我們可以采用降級來應(yīng)對,何謂降級?就是將不重要的服務(wù)和功能采用屏蔽,或降低實(shí)時性,或延遲處理,等等方式,最終目的是保證核心服務(wù)可用。
二、什么是降級, 為什么降級,降級的場景?
降級的最終目的是保證核心服務(wù)的高可用。過程就是丟卒保帥,有些服務(wù)是無法降級的,比如支付。
當(dāng)我們的服務(wù)器壓力劇增為了保證核心功能的可用性 ,而選擇性的降低一些功能的可用性,或者直接關(guān)閉該功能。
這就是典型的丟車保帥了。 就比如貼吧類型的網(wǎng)站,當(dāng)服務(wù)器吃不消的時候,可以選擇把發(fā)帖功能關(guān)閉,注冊功能關(guān)閉,改密碼,改頭像這些都關(guān)了,為了確保登錄和瀏覽帖子這種核心的功能。
降級的原理:就是降低次要功能的可用性實(shí)用性,增加核心功能的高可用性。
降級的實(shí)現(xiàn)原理是多樣多樣的。利用一個降級開關(guān),以這個開關(guān)為判斷依據(jù),切換數(shù)據(jù)的獲取方式,比如當(dāng)mysql負(fù)載高的時候,可以從mysql切換到redis,比如從redis切換到靜態(tài)文件,比如從錯誤頻發(fā)的新版本切換到老版本等等。 這個開關(guān)是根據(jù)現(xiàn)狀來配置的,比如當(dāng)新版本錯誤頻發(fā)的時候,我們可以配置這個開關(guān)為從老版本獲取數(shù)據(jù)。
三、降級的種類
- 根據(jù)降級的開關(guān)位置:分為服務(wù)代碼降級和開關(guān)前置降級代碼降級就是利用代碼控制,這種方式比前置降級要low并不推薦
前置降級是把降級開關(guān)放到http請求鏈路層的上游,降低鏈路層消耗。比如提升到nginx,甚至可以提升到前
端,當(dāng)提升到前端,后端訪問壓力接近于0
拓展:【如何提升到前端】
可以通過一個從服務(wù)器獲取的js腳本進(jìn)行控制。
- 根據(jù)讀寫:分為讀降級和寫降級。
讀降級,比如,讀取動態(tài)數(shù)據(jù),降級為讀取靜態(tài)數(shù)據(jù)。 寫降級,
比如,寫入mysql,降級為寫入消息隊(duì)列, 等高峰期過后,在從隊(duì)列寫入mys
- 根據(jù)降級的性質(zhì):分為返回內(nèi)容降級,限流降級,限速降級。
返回內(nèi)容降級,比如,返回實(shí)時數(shù)據(jù),降級為返回兜底數(shù)據(jù) 限流降級,
比如,1000個請求,我只接受500個。 這么做也是無奈之下選擇,要不然系統(tǒng)崩了 誰都訪問不了 限速降級,
比如,對于那些訪問過于頻繁的ip進(jìn)行限速
拓展:【限流限速】
nginx自帶限流限速,比如ngx_http_limit_req_module和ngx_http_limit_conn_module 。但是這類模塊只是提供了在nginx配置文件中進(jìn)行簡單的參數(shù)配置。 如果想更加靈活和功能更多,可以編寫自己的lua代碼進(jìn)行控制,而不是用現(xiàn)成的模塊進(jìn)行配置文件修 改。
4.根據(jù)降級的維護(hù)特點(diǎn):分為手動降級和自動降級手動降級,是人為看到系統(tǒng)負(fù)載異常后,手動調(diào)整降級
自動降級,是系統(tǒng)監(jiān)測到異常后,自動降級,自動降級雖然更加智能,但有時候自動腳本可能會干一些超乎預(yù) 料的事情。
四、 業(yè)務(wù)分析
大家知道,廣告推薦模塊的特點(diǎn):
1 .是要經(jīng)過對數(shù)據(jù)模型進(jìn)行大量分析,并結(jié)合用戶剛剛的瀏覽記錄,計算出用戶喜歡喜歡什么商品,然后給他打什么
廣告,運(yùn)算量相當(dāng)大。
2 .是廣告推薦模塊不是商城的核心模塊,沒有了這個模塊, 買家照樣可以完成商品的購買。
總結(jié)上面兩點(diǎn), 我們可以在商城負(fù)載過高時,對廣告推薦模塊進(jìn)行降級,讓它只從緩存或靜態(tài)文件中讀取數(shù)據(jù),或
者干脆nginx不返回任何數(shù)據(jù)給它。
五、 設(shè)計分析
第一種:從數(shù)據(jù)
第二種:從微服務(wù)
第三種:從緩存
第四種:從文件
第五種:從前端返回
第六種:直接返回空
前5種獲取廣告推薦數(shù)據(jù)的方式,從上而下,對系統(tǒng)帶來的性能的損耗逐漸降低,最底下的第5種,幾乎對系統(tǒng)沒有損耗,但是數(shù)據(jù)實(shí)時性也是自上而下逐漸降低的,從需求上講,我們更喜歡實(shí)時從數(shù)據(jù)庫讀取,但為了降低性能損耗,在系統(tǒng)負(fù)載重的時候, 我們可能不得不降級為從文件或者從緩存中讀取。這個降級是通過一個開關(guān)可以控制的。
六、 降級的線路圖
1 降級配置中心, 用于統(tǒng)一管理所有的微服務(wù)的降級開關(guān), 該中心是單獨(dú)的服務(wù)器,和微服務(wù)服務(wù)器集群是分 開的 2
每個微服務(wù)都有一個降級開關(guān)。
這個降級開關(guān)是個什么東西,結(jié)構(gòu)如何呢?實(shí)際上是一條條的redis數(shù)據(jù), 每條數(shù)據(jù)就是一個開關(guān)。
這條開關(guān)數(shù)據(jù)的結(jié)構(gòu)是這么設(shè)計的:
key:url鏈接中的請求地址。
value:記錄這個請求從哪里讀取數(shù)據(jù),也就是配置從哪里查詢數(shù)據(jù)。
七、實(shí)現(xiàn)降級
1. 配置中心
配置中心是一個后臺,這個配置中心大家根據(jù)各自需求自己去實(shí)現(xiàn),這里我們采用手工操作redis的方式,實(shí)際上是
通過后臺操作的redis。
2. nginx+lua+redis實(shí)現(xiàn)降級
nginx配置文件/etc/nginx/nginx.conf
user nobody; worker_processes 1; events { lua降級代碼:/etc/nginx/lua/goods_list_advert.lua 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; #獲取廣告推薦數(shù)據(jù) location /goods_list_advert { default_type 'application/x-javascript;charset=utf-8'; content_by_lua_file /etc/nginx/lua/goods_list_advert.lua; } #從服務(wù)層+mysql獲取數(shù)據(jù) location /goods_list_advert_from_data { default_type 'application/x-javascript;charset=utf-8'; content_by_lua ' ngx.say("從服務(wù)層+mysql獲取數(shù)據(jù)") '; } } }
lua降級代碼:/etc/nginx/lua/goods_list_advert.lua
--獲取get或post參數(shù)-------------------- local request_method = ngx.var.request_method local args = nil local param = nil --獲取參數(shù)的值 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"] --關(guān)閉redis的函數(shù)-------------------- 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" -- 創(chuàng)建一個redis對象實(shí)例。在失敗,返回nil和描述錯誤的字符串的情況下 local redis_instance = redis:new(); --設(shè)置后續(xù)操作的超時(以毫秒為單位)保護(hù),包括connect方法 redis_instance:set_timeout(1000) --建立連接 local ip = '127.0.0.1' local port = 6379 --嘗試連接到redis服務(wù)器正在偵聽的遠(yuǎn)程主機(jī)和端口 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里面讀取開關(guān)-------------------- 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 --得到的開關(guān)為空處理-------------------- if switch == ngx.null then switch = "FROM_DATA" --比如默認(rèn)值 end --當(dāng)開關(guān)是要從服務(wù)中獲取數(shù)據(jù)時-------------------- if "FROM_DATA" == switch then ngx.exec('/goods_list_advert_from_data'); --當(dāng)開關(guān)是要從緩存中獲取數(shù)據(jù)時-------------------- elseif "FROM_CACHE" == switch then local resp, err = redis_instance:get("nihao") ngx.say(resp) --當(dāng)開關(guān)是要從靜態(tài)資源中獲取數(shù)據(jù)時-------------------- 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) --當(dāng)開關(guān)是要停掉數(shù)據(jù)獲取時-------------------- elseif "SHUT_DOWN" == switch then ngx.say('no data') end
八、驗(yàn)證降級
驗(yàn)證1 設(shè)置為從服務(wù)和數(shù)據(jù)庫讀取
[root@101 redis-5.0.8]# redis-cli
127.0.0.1:6379> set level_goods_list_advert FROM_DATA
發(fā)送請求,發(fā)現(xiàn)廣告推薦請求獲取到了微服務(wù)提供的數(shù)據(jù)
用postman:http://127.0.0.1:6379/get_goods_List
返回數(shù)據(jù):[{“name”:[“bingwoo”]}]
驗(yàn)證2 設(shè)置為從緩存讀取
127.0.0.1:6379> set level_goods_list_advert FROM_CACHE
發(fā)送請求,發(fā)現(xiàn)廣告推薦請求獲取到了微服務(wù)提供的數(shù)據(jù)
用postman:http://127.0.0.1:6379/get_goods_List
返回數(shù)據(jù):redis_data
驗(yàn)證3 設(shè)置為從靜態(tài)文件讀取
127.0.0.1:6379> set level_goods_list_advert FROM_STATIC
發(fā)送請求,發(fā)現(xiàn)廣告推薦請求獲取到了微服務(wù)提供的數(shù)據(jù)
用postman:http://127.0.0.1:6379/get_goods_List
返回數(shù)據(jù):
[ { “goods_id”:“1”, “goods_name”:“測試1”, }, { “goods_id”:“2”, “goods_name”:“測試2”, } ]
驗(yàn)證4 設(shè)置為從緩存讀取
127.0.0.1:6379> set level_goods_list_advert FROM_CACHE
發(fā)送請求,發(fā)現(xiàn)廣告推薦請求獲取到了微服務(wù)提供的數(shù)據(jù)
用postman:http://127.0.0.1:6379/get_goods_List
返回數(shù)據(jù):null
九、自動降級
原理:
采用nginx+lua+redis對錯誤返回進(jìn)行統(tǒng)計,當(dāng)統(tǒng)計到的錯誤次數(shù)達(dá)到一定數(shù)值時,lua會判斷這個數(shù)值, 然后會在/etc/nginx/lua/goods_list_advert.lua中添加如下代碼:
--判斷錯誤的響應(yīng),并進(jìn)行計數(shù), 后續(xù)便可以參考這個數(shù)值進(jìn)行降級 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") --得到的數(shù)據(jù)為空處理 if error_count == ngx.null then error_count = 0 end error_count = error_count + 1 --統(tǒng)計錯誤次數(shù)到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
當(dāng)我們有意關(guān)掉一些服務(wù)讓狀態(tài)碼不為200時,
可以看到redis中error_count_goods_list_advert的值在加1:
127.0.0.1:6379> get error_count_goods_list_advert “1”
接下來,你就可以在lua中添加判斷, 比如設(shè)置一個閾值為100, 當(dāng)error_count_goods_list_advert的值達(dá)到100時(也就是錯誤返回達(dá)到100時),你就可以降級為請求另外一個版本(老版本程序)
到此這篇關(guān)于nginx+lua+redis實(shí)現(xiàn)降級的示例代碼的文章就介紹到這了,更多相關(guān)nginx+lua+redis降級內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Nginx負(fù)載均衡中的Memcached緩存模塊
- Nginx 負(fù)載均衡實(shí)現(xiàn)上游服務(wù)健康檢查功能
- Nginx實(shí)現(xiàn)負(fù)載均衡的配置步驟
- nginx tcp負(fù)載均衡的具體實(shí)現(xiàn)
- Nginx七層負(fù)載均衡的實(shí)現(xiàn)示例
- Nginx四層負(fù)載均衡的實(shí)現(xiàn)示例
- Nginx配置多臺機(jī)器實(shí)現(xiàn)負(fù)載均衡的教程詳解
- Nginx 反向代理與負(fù)載均衡運(yùn)行小結(jié)
- Nginx實(shí)現(xiàn)負(fù)載均衡和反向代理的方法
- nacos集群搭建Nginx負(fù)載均衡的操作詳解
相關(guān)文章
Nginx配置文件中l(wèi)ocation配置的多種場景
location主要做定位功能,根據(jù)uri來進(jìn)行不同的定位,下面這篇文章主要給大家介紹了關(guān)于Nginx配置文件中l(wèi)ocation配置的多種場景,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09nginx啟動、關(guān)閉及重啟等簡單命令小結(jié)
這篇文章主要介紹了使用命令行重啟Nginx的方法,包括修改配置文件后重啟以使更改生效,查看端口占用情況,以及如何關(guān)閉Nginx,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-03-03Nginx訪問FTP服務(wù)器文件的時效性/安全校驗(yàn)的方法
nginx的實(shí)現(xiàn)方式在校驗(yàn)失敗的時候頁面返回error image,跳轉(zhuǎn)的是420 error_page,成功的時候會訪問FTP文件服務(wù)器的路徑,反正圖片到頁面展示,這篇文章主要介紹了Nginx訪問FTP服務(wù)器文件的時效性/安全校驗(yàn),需要的朋友可以參考下2023-12-12Nginx結(jié)合keepalived實(shí)現(xiàn)集群
本文主要介紹了Nginx結(jié)合keepalived實(shí)現(xiàn)集群,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05