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

上篇博文咱們寫了個引子:html

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

發現了新大陸,OpenRestyweb

OpenResty 是一個基於 Nginx 與 Lua 的高性能 Web 平臺,其內部集成了大量精良的 Lua 庫、第三方模塊以及大多數的依賴項。用於方便地搭建可以處理超高併發、擴展性極高的動態 Web 應用、Web 服務和動態網關。redis

OpenResty 經過匯聚各類設計精良的 Nginx 模塊(主要由 OpenResty 團隊自主開發),從而將 Nginx 有效地變成一個強大的通用 Web 應用平臺。這樣,Web 開發人員和系統工程師可使用 Lua 腳本語言調動 Nginx 支持的各類 C 以及 Lua 模塊,快速構造出足以勝任 10K 乃至 1000K 以上單機併發鏈接的高性能 Web 應用系統。後端

OpenResty的目標是讓你的Web服務直接跑在 Nginx 服務內部,充分利用 Nginx 的非阻塞 I/O 模型,不單單對 HTTP 客戶端請求,甚至於對遠程後端諸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都進行一致的高性能響應。api

回到咱們的原始需求:數組

http://api.***.com/action    => http://192.168.0.11/api/action 服務器

Header: ***                            Header: ***併發

Body:   ***                             Body: ***                              app

經過Actiton獲取對應的後端服務器地址

Action和服務器的對應關係(路由表)存儲在Redis中.(實時更新實時獲取)

根據請求的Action動態解析對應的內網服務器地址,再實現服務的轉發。

Nginx原生的路由配置沒法實現上述動態路由配置,由於咱們要訪問Redis,獲取到反向代理的路由信息,再實現服務的轉發。詳細的實現步驟:

1. 解析URL中的Action

2.訪問Redis,Key=Action Value=內網服務器地址

3.Http請求轉發到內網服務器

明確了具體的實現方案後,咱們須要先詳細的研究一下OpenResty和Lua

http://openresty.org/cn/

https://www.tutorialspoint.com/lua/

大體瞭解OpenResty是什麼,能作什麼,同時能簡單寫一些Lua腳本。

而後,咱們重點看OpenResty提供的樣例:

Dynamic Routing Based On Redis

經過這個樣例,咱們就能夠模仿着寫一個咱們本身的配置和腳本,實現上面的動態路由的需求。

Come On。

1、解析URL中的Action

首先,要解析並拆分URL字符串,各類百度和Google,須要寫一段Lua代碼,實現字符串按"/"拆分

其實就是定義了一個split_path函數。

而後咱們將上面的Split.lua文件,配置到Nginx的配置文件中

/usr/local/openresty/nginx/conf/nginx.conf

注:split.lua文件咱們放在了

/usr/local/openresty/nginx/lua

編輯/usr/local/openresty/nginx/conf/nginx.conf文件

http {
    include       mime.types;
    default_type  application/octet-stream;    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;   
    init_worker_by_lua_file /usr/local/openresty/nginx/lua/split.lua;
    server {
        listen 80;
        location = /redis {
            internal;
            set_unescape_uri $key $arg_key;
            redis2_query get $key;
            redis2_pass RedisServer:6379;
        }

        location / {
            set $target '';            
            access_by_lua '
        local parameters = split_path(ngx.var.uri)
        local action = parameters[1]

location中的access_by_lua '這一段負責執行Lua腳本和方法
local action = parameters[1]

這樣,咱們便解析到了Action,注:Lua中數組的下標從1開始

二.訪問Redis,Key=Action Value=內網服務器地址

繼續編輯Nginx配置文件

 

 location / {
            set $target '';            
            access_by_lua '
        local parameters = split_path(ngx.var.uri)
        local action = parameters[1]
        if(#parameters == 0) then
                   ngx.exit(ngx.HTTP_FORBIDDEN)
        end
        
local key
= action 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.com" end

三.Http請求轉發到內網服務器

繼續編輯Nginx.Conf文件

 init_worker_by_lua_file /usr/local/openresty/nginx/lua/split.lua;
    server {
        listen 80;
        location = /redis {
            internal;
            set_unescape_uri $key $arg_key;
            redis2_query get $key;
            redis2_pass RedisServer:6379;
        }

        location / {
            set $target '';            
            access_by_lua '
        local parameters = split_path(ngx.var.uri)
        local action = parameters[1]
        if(#parameters == 0) then
                   ngx.exit(ngx.HTTP_FORBIDDEN)
        end
        
        local key = action
        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.com"
        end
        
server
= server .. "/api/" .. action if ngx.var.QUERY_STRING ~= nil and ngx.var.QUERY_STRING ~= "" then server = server .."&"..ngx.var.QUERY_STRING end ngx.var.target = server '; resolver 8.8.8.8; proxy_pass http://$target; } }

至此,咱們完成了Nginx的配置文件。

啓動Nginx測試:

sudo /usr/local/openresty/nginx/sbin/nginx -c /usr/local/openresty/nginx/conf/Nginx.conf

PostMan發起一次請求:

查看Nginx日誌

 

以上就是使用Nginx+lua+redis實現動態路由。分享給你們

 

 

周國慶

2017/10/01

相關文章
相關標籤/搜索