【轉載請註明出處】:http://www.javashuo.com/article/p-rfggpffw-bn.htmlnode
動態Nginx負載均衡的配置,能夠經過Consul+Consul-Template方式,可是這種方案有個缺點:每次發現配置變動都須要reload Nginx,而reload是有必定損耗的。並且,若是你須要長鏈接支持的話,那麼當reload時Nginx長鏈接所在worker進程會進行優雅退出,並當該worker進程上的全部鏈接都釋放時,進程才真正退出(表現爲worker進程處於worker process is shutting down)。所以,若是能作到不reload就能動態更改upstream,那麼就完美了。nginx
目前的開源解決方法有3種:
一、Tengine的Dyups模塊
二、微博的Upsync模塊+Consul
三、使用OpenResty的balancer_by_lua。git
這裏使用的是Consul+OpenResty 來實現動態負載均衡。Consul的安裝這裏將再也不介紹。github
OpenResty 是一個基於 Nginx 與 Lua 的高性能 Web 平臺,其內部集成了大量精良的 Lua 庫、第三方模塊以及大多數的依賴項。用於方便地搭建可以處理超高併發、擴展性極高的動態 Web 應用、Web 服務和動態網關。算法
Lua是一個簡潔、輕量、可擴展的程序設計語言,其設計目的是爲了嵌入應用程序中,從而爲應用程序提供靈活的擴展和定製功能。Lua由標準C編寫而成,代碼簡潔優美,幾乎在全部操做系統和平臺上均可以編譯,運行。編程
正常使用Nginx做爲API網關, 須要以下配置, 當加後端實例時, reload一下Nginx, 使其生效json
upstream backend { server 192.168.0.1; server 192.168.0.2; }
那麼只要讓upstream變成動態可編程就OK了, 當新增後端實例時, 無需reload, 自動生效。
在OpenResty經過長輪訓和版本號及時獲取Consul的kv store變化。Consul提供了time_wait和修改版本號概念,若是Consul發現該kv沒有變化就會hang住這個請求5分鐘,在這5分鐘內若是有任何變化都會及時返回結果。經過比較版本號咱們就知道是超時了仍是kv的確被修改了。
Consul的node和service也支持阻塞查詢,相對來講用service更好一點,畢竟支持服務的健康檢查。阻塞api和kv同樣,加一個index就行了。segmentfault
筆者使用的是MAC,下面重點介紹MAC系統上的操做。後端
brew tap openresty/brew brew install openresty
sudo yum install yum-utils sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo sudo yum install openresty #命令行工具 resty sudo yum install openresty-resty
Mac OS系統上安裝完以後Nginx的配置文件在目錄/usr/local/etc/openresty
,啓動命令在目錄/usr/local/opt/openresty/nginx/sbin
。centos
openresty
在瀏覽器輸入localhost
openresty -h
到這裏OpenResty就已經安裝好了。
重啓
openresty -s reload
LuaRocks是Lua模塊的包管理器,安裝LuaRocks以後能夠經過LuaRocks來快速安裝Lua的模塊,官網地址是https://luarocks.org/。
進入OpenResty的安裝目錄/usr/local/opt/openresty
能夠看到已經安裝了luajit,也是OpenResty默認使用的lua環境。
下載LuaRocks安裝包http://luarocks.github.io/lua...,解壓以後進入LuaRocks源碼目錄編譯安裝到OpenResty的安裝目錄。
./configure --prefix=/usr/local/opt/openresty/luajit --with-lua=/usr/local/opt/openresty/luajit --lua-suffix=luajit --with-lua-include=/usr/local/opt/openresty/luajit/include/luajit-2.1 make build make install
安裝完LuaRocks以後,能夠看到LuaRocks的執行命令在目錄/usr/local/opt/openresty/luajit
安裝完LuaRocks以後就能夠安裝搭建環境須要的依賴包了。
./luarocks install luasocket
在Consul註冊好服務以後經過Consul的http://127.0.0.1:8500/v1/catalog/service/api_tomcat2
接口就能夠獲取到服務的列表。這裏再也不講述Consul的服務註冊。
在默認配置文件目錄/usr/local/etc/openresty
建立一個servers
文件夾來放新的配置文件,建立lualib
文件夾來放lua腳本,修改配置文件nginx.conf
,添加include servers/*.conf;
。
在lualib
文件夾下建立腳本upstreams.lua
local http = require "socket.http" local ltn12 = require "ltn12" local cjson = require "cjson" local _M = {} _M._VERSION="0.1" function _M:update_upstreams() local resp = {} http.request{ url = "http://127.0.0.1:8500/v1/catalog/service/api_tomcat2", sink = ltn12.sink.table(resp) } local resp = table.concat(resp); local resp = cjson.decode(resp); local upstreams = {} for i, v in ipairs(resp) do upstreams[i] = {ip=v.Address, port=v.ServicePort} end ngx.shared.upstream_list:set("api_tomcat2", cjson.encode(upstreams)) end function _M:get_upstreams() local upstreams_str = ngx.shared.upstream_list:get("api_tomcat2"); local tmp_upstreams = cjson.decode(upstreams_str); return tmp_upstreams; end return _M
過luasockets查詢Consul來發現服務,update_upstreams用於更新upstream列表,get_upstreams用於返回upstream列表,此處能夠考慮worker進程級別的緩存,減小由於json的反序列化形成的性能開銷。
還要注意使用的luasocket是阻塞API,這可能會阻塞咱們的服務,使用時要慎重。
建立文件test_openresty.conf
lua_package_path "/usr/local/etc/openresty/lualib/?.lua;;"; lua_package_cpath "/usr/local/etc/openresty/lualib/?.so;;"; lua_shared_dict upstream_list 10m; # 第一次初始化 init_by_lua_block { local upstreams = require "upstreams"; upstreams.update_upstreams(); } # 定時拉取配置 init_worker_by_lua_block { local upstreams = require "upstreams"; local handle = nil; handle = function () --TODO:控制每次只有一個worker執行 upstreams.update_upstreams(); ngx.timer.at(5, handle); end ngx.timer.at(5, handle); } upstream api_server { server 0.0.0.1 down; #佔位server balancer_by_lua_block { local balancer = require "ngx.balancer"; local upstreams = require "upstreams"; local tmp_upstreams = upstreams.get_upstreams(); local ip_port = tmp_upstreams[math.random(1, table.getn(tmp_upstreams))]; balancer.set_current_peer(ip_port.ip, ip_port.port); } } server { listen 8000; server_name localhost; charset utf-8; location / { proxy_pass http://api_server; access_log /usr/local/etc/openresty/logs/api.log main; } }
init_worker_by_lua是每一個Nginx Worker進程都會執行的代碼,因此實際實現時可考慮使用鎖機制,保證一次只有一我的處理配置拉取。另外ngx.timer.at是定時輪詢,不是走的長輪詢,有必定的時延。有個解決方案,是在Nginx上暴露HTTP API,經過主動推送的方式解決。
Agent能夠長輪詢拉取,而後調用HTTPAPI推送到Nginx上,Agent能夠部署在Nginx本機或者遠程。
對於拉取的配置,除了放在內存裏,請考慮在本地文件系統中存儲一份,在網絡出問題時做爲託底。
獲取upstream列表,實現本身的負載均衡算法,經過ngx.balancer API進行動態設置本次upstream server。經過balancer_by_lua除能夠實現動態負載均衡外,還能夠實現個性化負載均衡算法。
可使用lua-resty-upstream-healthcheck模塊進行健康檢查,後面會單獨介紹。
到這裏,其實還有一個問題沒有解決掉,雖然upstream中的佔位server是下線的,可是nginx在檢測upstream列表中server的健康狀態的時候是會去檢測這個佔位server的,最好的方式仍是在啓動以後把它完全從upstream列表中給移除掉。
【轉載請註明出處】:http://www.javashuo.com/article/p-rfggpffw-bn.html