前幾篇文章咱們介紹了Nginx的配置、OpenResty安裝配置、基於Redis的動態路由以及Nginx的監控。html
Nginx-OpenResty安裝配置nginx
Nginx配置詳解git
Nginx技術研究系列1-經過應用場景看Nginx的反向代理github
[原創]Nginx監控-Nginx+Telegraf+Influxb+Grafanaredis
在分佈式環境下,咱們要考慮高可用性和性能:算法
1. 不能由於Redis宕機影響請求的反向代理緩存
2. 每次請求若是都訪問Redis,性能有必定的損耗,同時Redis集羣的壓力隨着流量的激增不斷增長。app
所以,咱們要升級一下動態路由的設計方案。 咱們仍是先從應用場景出發:分佈式
1.提高性能,下降Redis的訪問頻率
2.Redis宕機不影響Nginx反向代理
實現上述兩個應用場景,咱們採用本地緩存+Redis緩存的雙緩存配合機制。
肯定了上述實現方案後,咱們回顧一下咱們已有的Redis動態路由實現:
upstream redis_cluster { server 192.0.1.*:6379; server 192.0.1.*:6379; server 192.0.1.*:6379; } location = /redis { internal; set_unescape_uri $key $arg_key; redis2_query get $key; redis2_pass redis_cluster; } location / { set $target ''; access_by_lua ' local query_string = ngx.req.get_uri_args() local sid = query_string["RequestID"] if sid == nil then ngx.exit(ngx.HTTP_FORBIDDEN) end local key = sid local res = ngx.location.capture( "/redis", { args = { key = key } } ) if res.status ~= 200 then ngx.log(ngx.ERR, "redis server returned bad status: ", res.status) ngx.exit(res.status) end if not res.body then ngx.log(ngx.ERR, "redis returned empty body") ngx.exit(500) end local parser = require "redis.parser" local server, typ = parser.parse_reply(res.body) if typ ~= parser.BULK_REPLY or not server then ngx.log(ngx.ERR, "bad redis response: ", res.body) ngx.exit(500) end if server == "" then server = "default" end server = server .. ngx.var.request_uri ngx.var.target = server '; resolver 255.255.255.0; proxy_pass http://$target; }
咱們要在上述代碼中增長一層本地緩存實現,所以,咱們須要找一個本地緩存的實現Lib。
咱們在OpenResty的官網上找了一遍已提供的組件,發現了:lua-resty-lrucache - Lua-land LRU Cache based on LuaJIT FFI
Git Hub的地址:https://github.com/openresty/lua-resty-lrucache
嘗試寫了一下,發現這個cache實現是worker進程級別的,咱們Nginx是Auto的進程數配置,通常有4~8個,這個緩存每一個進程都New一個,貌似不符合咱們的要求,同時,這個cache是預分配好緩存的大小和數量,啓動的時候若是數量太多,分配內存很慢。
測試了一下,果然是這樣,所以,咱們繼續查找新的Cache實現。
ngx.shared.DICT,https://github.com/openresty/lua-nginx-module#ngxshareddict
這個 cache 是 nginx 全部 worker 之間共享的,內部使用的 LRU 算法(最近常常使用)來判斷緩存是否在內存佔滿時被清除。同時提供了以下方法:
而後,咱們基於這個組件實現了咱們升級版的Redis動態路由,直接上代碼Show:
upstream redis_cluster { server 192.0.1.*:6379; server 192.0.1.*:6379; server 192.0.1.*:6379; } lua_shared_dict localcache 10m;—— location = /redis { internal; set_unescape_uri $key $arg_key; redis2_query get $key; redis2_pass redis_cluster; } location / { set $target ''; access_by_lua ' local query_string = ngx.req.get_uri_args() local sid = query_string["RequestID"] if sid == nil then ngx.exit(ngx.HTTP_FORBIDDEN) end local key = sid local cache = ngx.shared.localcache local server = cache:get(key) if server == nil then local res = ngx.location.capture( "/redis", { args = { key = key } } ) if res.status ~= 200 then ngx.log(ngx.ERR, "redis server returned bad status: ", res.status) ngx.exit(res.status) end if not res.body then ngx.log(ngx.ERR, "redis returned empty body") ngx.exit(500) end local parser = require "redis.parser" local serveradr, typ = parser.parse_reply(res.body) if typ ~= parser.BULK_REPLY or not serveradr then ngx.log(ngx.ERR, "bad redis response: ", res.body) ngx.exit(500) end server = serveradr cache:set(key, server,3600) end if server == "" then server = "default" end server = server .. ngx.var.request_uri ngx.var.target = server '; resolver 255.255.255.0; proxy_pass http://$target; } }
重點說一下上面的代碼:
首先,定義了一個本地緩存:
lua_shared_dict localcache 10m;
而後,從本地緩存中獲取路由地址
local cache = ngx.shared.localcache local server = cache:get(key)
若是本地緩存中沒有,從Redis中獲取,從Redis中獲取到以後,加入到本地緩存中,緩存有效時間3600s:
server = serveradr cache:set(key, server, 3600)
升級後的Redis+本地緩存的動態路由方案,壓測性能提高1.4倍,同時解決了Redis宕機的問題。
以上這個方案和實現都分享給你們。
周國慶
2017/11/13