當前位置:
首頁 > 最新 > nginx+lua 入門

nginx+lua 入門

我們的目的是做一個靜態文件服務,實現的功能是根據用戶的來源 IP 將用戶重定向到不同的頁面,如果用戶來自中國,則重定向到中文首頁,如果用戶來自非中國地區,則顯示英文首頁。

文中使用了 nginx 和 lua 的技術,此篇文章為入門級,高手們請繞走。

回顧上篇文章:MySQL「七年之癢」

nginx+lua 編譯安裝

1.nginx 版本的選擇

我們選擇最新 Stable 版本的 nginx-1.14.0:

2. 編譯 nginx 需要解決前置的一些依賴,因為我們使用的是 centos 系統,有一些依賴可以直接使用 yum 安裝,但是有一些我們希望使用特定的版本,不想使用系統自帶的版本,比如 OpenSSL。

yum 安裝需要的依賴

下載需要的 OpenSSL,現在 OpenSSL 最新的穩定版是 1.1.0 系列,1.0.2 是長期支持版本並且支持到 2019 年 12 月 31 號。0.9.8、1.0.0 和 1.0.1 版本現在已經不再維護,官方建議不要使用這些版本。

下載 Nginx Development Kit (NDK) 模塊,lua-nginx-module 依賴該模塊

下載 headers-more-nginx-module 模塊

下載 ngx_cache_purge 模塊

下載 lua 支持模塊 lua-nginx-module,該模塊的依賴詳細介紹請見github

3.編譯安裝 Lua-JIT,現在 Lua-JIT 的最新版本為 2.1.0-beta3,於 2017 年 5 月 1 號發布,我們就使用這個版本:

4.編譯安裝 nginx,由於我們使用的是 24core 伺服器,所以在編譯 nginx 的時候我們使用了並行編譯,速度會快一些:

編譯安裝完成後可以看到在 /home/work/app/nginx 已經有了我們的 nginx 相關的文件:

安裝需要的 lua 模塊

由於這裡我們的 nginx 只是一個靜態文件伺服器,沒有後端 server 的處理邏輯,為了實現判斷用戶的歸屬,我們需要通過 nginx 來實現。考慮到使用 GEOIP 庫後續升級維護可能比較麻煩,我們使用的是能通過 API 來獲取用戶歸屬的服務。

首先,我們手頭有這樣的服務,通過訪問可以返回以下示例:

從上面可以看出我們可以獲取我們需要的國家信息了,其餘信息可以先不管,畢竟我們要做的是國家粒度。正常的返回結果是一個 json 串,那麼我們選擇使用 lua-cjson 來解析該 json 串,要在 nginx 中發起 http 請求,我們選擇使用 luasocket 來實現。

1. 安裝編譯 lua 模塊需要的依賴

2. 安裝 luasocket

3. 安裝 cjson

4. 驗證安裝

能正常發起請求表示 luasocket 安裝成功

能正常解析 json 串,表示 cjson 安裝成功

nginx lua 處理原理介紹

基於 nginx 使用的多模塊設計思想,nginx 對 http 請求的處理劃分為 11 個階段。這樣可以使一個 http 請求的處理過程由很多模塊參與處理,每個模塊只專註於一個獨立而簡單的功能處理,可以擁有更好的擴展。11 個階段如下所示:

這 11 個階段的意義分別如下:

NGX_HTTP_POST_READ_PHASE:接收完請求頭之後的處理階段,它位於 uri 重寫之前,一般很少有模塊註冊在該階段;

NGX_HTTP_SERVER_REWRITE_PHASE:server 級別的 URL 重寫,在 location 塊之前;

NGX_HTTP_FIND_CONFIG_PHASE:根據請求的 URI 尋找匹配的 location 表達式,這個階段只能由ngx_http_core_module模塊實現;

NGX_HTTP_REWRITE_PHASE: location 級別的 uri 重寫階段,該階段執行 location 基本的重寫指令,可能會被執行多次;

NGX_HTTP_POST_REWRITE_PHASE:重寫 URL 後,防止錯誤的 nginx.conf 配置導致死循環(遞歸修改 URI),並根據結果跳到合適的階段;

NGX_HTTP_PREACCESS_PHASE:訪問許可權控制的前一階段,該階段在許可權控制階段之前,一般也用於訪問控制,比如限制訪問頻率,鏈接數等;

NGX_HTTP_ACCESS_PHASE:訪問許可權控制階段,比如基於 ip 黑白名單的許可權控制,基於用戶名密碼的許可權控制等;

NGX_HTTP_POST_ACCESS_PHASE:訪問許可權控制的後一階段,用於上一階段的收尾工作;

NGX_HTTP_TRY_FILES_PHASE:該階段是為 try_files 設置的;

NGX_HTTP_CONTENT_PHASE:內容生成階段,該階段產生響應,並發送到客戶端;

NGX_HTTP_LOG_PHASE:日誌寫入階段。ngx_http_log_module 實際上也是在這裡註冊了一個 handler 實現日誌寫入的。

Lua 可使用的主要階段:

編寫 nginx lua 模塊

終於到了該模塊啦,在該模塊中我們將實現通過用戶 IP 的歸屬將用戶重定向到中 / 英文首頁。

1. 首先構造獲取用戶的真實 IP:

因為 x-forwarded-for 獲取的用戶 IP 要麼是 nil,要麼是 192.168.1.100,要麼是 192.168.1.100,192.168.100.10 這種格式,所以要進行處理,獲取到用戶最真實的 IP,如果該欄位沒有內容則取 remote_addr 作為用戶 IP。因為 lua 中的字元串操作沒有 split 函數,所以這裡我們自己實現了 splite 操作:

2.構造獲取用戶 IP 歸屬的 http 請求。我們在上一步中已經獲取到了用戶的真實 IP,現在進一步獲取用戶所屬的國家。

現在我們已經獲取到用戶 IP 的歸屬信息,但是由於 cjson 處理的 json 串只能在一行中,所以我們要深入處理下獲取到的數據: * 去掉多於的換行 * 去掉多於的空格

接下來可以對處理後的 newstr 進行 cjson decode 操作了:

在處理上我們為了魯棒性考慮,當 cjson decode 失敗時 tb 的值會是 nil,則後面所取的國家屬性也是 nil,如果這種情況發生則返回英文首頁,還有一個問題,如果我們根本就沒有獲取到客戶的 IP,是不是可以省略掉內部發起 http 訪問的環節?直接返回英文首頁,嗯,下面在發起 http 請求的代碼前面添加如下代碼:

還有一個問題,如果我們向後端發起 http 請求後,請求被 block 了,等了 1 分鐘才返回怎麼辦?這樣的話我們的頁面豈不是也會有問題?等一分鐘再返回給用戶?這個太不優雅了,我們這裡可以給請求設置超時時間,比如超過 1s 中就強制結束,所以我們構造 http 請求的代碼變成了這樣:

最後一個需求,怎麼看我們寫的腳本有沒有執行成功呢?怎麼判斷我們通過用戶 IP 獲取到的國家是否正確呢?我們需要加列印一些日誌信息,我們在前面添加了如下代碼:

這樣我們就能知道獲取到的用戶是來自於哪裡了。 中國用戶訪問我們的測試域名 xxxx.srv 會被重定向到 xxxx.srv/zh 頁面,國外 IP 訪問會被重定向到 xxxx.srv/en 頁面,日誌示例如下:

最後 nginx 配置如下:

總結

通過這個小功能我們大體上了解了怎樣編寫 nginx+lua 模塊,做一些小的功能。更廣闊的應用等待的大家去發掘 ^_^

小米運維

和我們一起成為更酷的運維工程師


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 小米運維 的精彩文章:

TAG:小米運維 |