雜談篇-nginx(tengine)+lua動態后端實現,無間斷擴容縮容

現在很多地方都使用nginx或者tengine進行前端的負載均衡,但單純使用nginx(tengine)不能完全滿足日常需求,所以,需要進行適當的功能擴展,在以往只能是nginx c模塊開發,這樣費時費力還難維護,對于中小型公司簡直是一萬點傷害呢。幸運的是有大神做了nginx的lua模塊,使得nginx(tengine)功能擴展變得簡單和易維護。

結合我們現在正在使用的例子,分幾篇文章和大家分享一下,nginx(tengine)和lua一起搭配使用的例子。

動態后端

面對場景:

自動化擴容和縮容還有智能化運維一直是我們努力實現的目標,但在傳統的nginx后端配置中,修改upstream的server配置是需要reload或者restart。對于應用為長鏈接、流量大系統,這會影響用戶體驗。

具體做法是:

1.利用lua中 "lua_shared_dict"? 申請共享內存空間,這個內存空間能被nginx(tengine)所有子進程讀取;

2.構造API,讓其通過調用進行修改共享內存空間的值 ;

3.利用 proxy_pass 可使用變量特性及lua指令 "set_by_lua" 動態修改當前proxy的值達到動態效果。


簡單代碼例子

1.申請共享內存塊dyproxy

lua_shared_dict dyproxy 3m;

2.制造修改功能api,host為key,rip為后端ip

location ~ ^/DYPROXYSET {

default_type 'text/plain';

content_by_lua '

local dip = ngx.shared.dyproxy

local uri_args = ngx.req.get_uri_args()

if? uri_args["method"] == "add" then

if uri_args["domain"] == nil then

ngx.say("domain is null")

elseif uri_args["rip"] == nil? then

ngx.say("rip is null")

else

dip:set(uri_args["domain"],uri_args["rip"]) //設置domain:rip ,eg: xx.pcauto.com.cn:192.168.10.1|192.168.10.2

ngx.say("Set successfully : domain"..uri_args["domain"].."|rip"..uri_args["rip"])

end

elseif? uri_args["method"] == "del" then

if uri_args["domain"] == nil then

ngx.say("domain is null")

else

dip:delete(uri_args["domain"])

ngx.say("del successfully")

end

end

';

}

3.使用共享內存的值達到動態變量的效果

location ~ ^/testdyproxy {

default_type 'text/plain';

set_by_lua_file $dybackend conf/dyproxy.lua;

proxy_pass? ? ? ? http://$dybackend;

proxy_redirect? ? off;

proxy_set_header? Host? ? ? ? ? ? $host;

proxy_set_header? X-Real-IP? ? ? ? $remote_addr;

proxy_set_header? X-Forwarded-For? $proxy_add_x_forwarded_for;

proxy_connect_timeout 3;

proxy_read_timeout 10;

}

dyproxy.lua:

math.randomseed(tostring(os.time()):reverse():sub(1, 6))

local dip = ngx.shared.dyproxy

backend =? dip:get(ngx.var.host)

if backend == nil then

return "192.168.10.208:80"

end

local backendarr = loadstring("return "..ngx.unescape_uri(backend))()

local fm = 10000

nowindex = math.fmod(fm , table.getn(backendarr)) + 1

return backendarr[nowindex]? //nowindex簡單隨機拿出其中一個后端ip

經常使用nginx(tengine)的朋友可能注意到這里有兩個問題;就是在后端發生異常情況的時候,后端節點的摘除和共享內存的持久化問題。

這個兩個問題點需要存在兩個東西,服務健康監控系統和服務節點信息管理系統。

服務健康健康系統:大家業務各有不同,根據情況定造。

服務節點信息管理系統:主要是維護服務注冊信息,我們現在lua 內存共享變量是不另外做持久化處理,只是在nginx(tengine)的重啟腳本里面加入調用服務節點信息系統步驟,在nginx(tengine)重啟完后,進行自動調用服務節點信息管理系統獲取信息并寫入共享內存中。

總結:經過這樣改造,達到不需要reload restart nginx(tengine)情況下,進行服務的擴容和縮容。

-----------------------------------------


-----------------------------------------

以下內容是摘自網上同行統計其它公司或開源模塊實現的動態后端介紹:

1. nginx + upsync 模塊

這個方案在沙箱試了很久都沒有,功能上沒有任何問題。但是在線上系統并發超過50k的情況下, reload nginx后幾分鐘會出現頁面無法訪問,靜態頁面的請求都變成下載了,最后導致整個應用crash. 這個應該是upsync的bug導致http response content type類型改變了。沒有upsync開發者的幫助這個問題很難解決。

2. 右拍云的slardar

slardar 是基于nginx 1.9.5 然后集成了又拍云自己開發的一些lua module,還有春哥的balancer.lua 新模塊 并且結合consul k/v 實現了upstream動態更新

slardar可以通過動態更新consul的k/v存儲來添加upstream,添加lua script, 非常靈活。 不過這個需要對nginx的開發流程非常了解,并且熟悉nginx lua代碼

github上 https://github.com/upyun/slardar 目前還沒有人提issue和pr,估計只有又拍云自己在用了,目前來說通用性不好(雖然這個技術架構和我們的架構非常契合)

3. tengine + ngx_http_dyups_module

github 上 https://github.com/yzprofile/ngx_http_dyups_module 這個模塊已經開源很久el并且merge到了tengine里,說明這個模塊已經相當成熟了, 這個模塊都是C編寫的效率肯定是超過用lua實現的

* ngx_http_dyups_module 提供了可以操作upstream的http restful api, 管理upstream變得非常容易

* 線上系統之前使用的nginx就是tengine, 所以使用這個方案會更容易一些,只需重新編譯并加一個dyups模塊,原來的配置無需改動

* 現在線上系統的nginx是通過健康監測模塊主動check服務的端口號在不在,這個檢測時有時間間隔的,線上的配置是

check interval=2000 rise=2 fall=3 timeout=1000;

這個健康監測連續失敗3次nginx才會認為后端實例不可用,即6s后,nginx才會把一個有問題的實例屏蔽掉。通過consul service自己的健康監測不需要這么長的時間,幾乎是近實時的,dyups可以很快的拿掉有問題的實例,影響小了很多。

4. consul nginx template

*consul template定義nginx配置文件模版,從consul上讀取server信息(ip + port),然后更新nginx,并reload nginx. 這些都是consul幫助實現的,不過還是有reload操作。


更多文章,請關注微信訂閱號:輕量運維

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容