1. 程式人生 > >Nginx-動態路由升級版

Nginx-動態路由升級版

緩存 影響 應用 app err leg not serve clu

前幾篇文章我們介紹了Nginx的配置、OpenResty安裝配置、基於Redis的動態路由以及Nginx的監控。

Nginx-OpenResty安裝配置

Nginx配置詳解

Nginx技術研究系列1-通過應用場景看Nginx的反向代理

Nginx技術研究系列2-基於Redis實現動態路由

[原創]Nginx監控-Nginx+Telegraf+Influxb+Grafana

在分布式環境下,我們要考慮高可用性和性能:

1. 不能因為Redis宕機影響請求的反向代理

2. 每次請求如果都訪問Redis,性能有一定的損耗,同時Redis集群的壓力隨著流量的激增不斷增加。

因此,我們要升級一下動態路由的設計方案。 我們還是先從應用場景出發:

1.提升性能,降低Redis的訪問頻率

2.Redis宕機不影響Nginx反向代理

實現上述兩個應用場景,我們采用本地緩存+Redis緩存的雙緩存配合機制。

  • 第一次請求時,從Redis中獲取路由地址,然後放到本地緩存中,同時設置本地緩存項的有效時間
  • 後續請求時,從本地緩存直接獲取路由地址,如果本地緩存已經失效,則再次從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

Nginx-動態路由升級版