Nginx 使用Lua腳本


安裝 Nginx Lua支持

  1. 安裝系統依賴庫
yum install readline-devel pcre-devel openssl-devel gcc
  1. 安裝LuaJIT
$ wget http://luajit.org/download/LuaJIT-2.0.5.tar.gz
$ tar zxvf LuaJIT-2.0.5.tar.gz
$ cd LuaJIT-2.0.5
$ make install
# 安裝成功
==== Successfully installed LuaJIT 2.0.5 to /usr/local ====
  1. 設置LuaJIT環境變量
$ export LUAJIT_LIB=/usr/local/lib
$ export LUAJIT_INC=/usr/local/include/luajit-2.0
$ echo "/usr/local/lib" > /etc/ld.so.conf.d/usr_local_lib.conf
$ ldconfig
  1. 下載相關模塊

ngx_devel_kit

$ wget https://github.com/simpl/ngx_devel_kit/archive/v0.3.0.tar.gz
$ tar zxvf v0.3.0.tar.gz
# 解壓縮后目錄名
ngx_devel_kit-0.3.0

lua-nginx-module

$ wget https://github.com/openresty/lua-nginx-module/archive/v0.10.10.tar.gz
$ tar zxvf v0.10.10.tar.gz
# 解壓縮后目錄名
lua-nginx-module-0.10.10
  1. 重新編譯Nginx模塊
# 最好先查看下之前編譯的
./sbin/nginx -V

./configure --prefix=/home/ifan/software/nginx \
    --with-http_ssl_module \
    --with-http_v2_module \
    --with-http_stub_status_module \
    --with-http_realip_module \
    --with-threads \
    --with-http_realip_module \
    --add-module=/home/ifan/lua-nginx-module-0.10.10 \
    --add-module=/home/ifan/ngx_devel_kit-0.3.0

make && make install
  1. 配置Nginx環境

6.1 添加 Http lua 支持

http {
    # 第三方庫(cjson)地址luajit-2.0/lib
    lua_package_path './lua/?.lua;;';
    lua_package_cpath '/usr/local/include/luajit-2.0/lib/?.so;;';
}

6.2 添加一個Lua服務

server {
    location /ip {
        # 正式環境需要去掉,現在加上方便調試lua腳本
		lua_code_cache off;
		default_type 'text/plain';

        # nginx下的相對路徑
        content_by_lua_file lua/get_ip.lua;

		# content_by_lua 'ngx.say("hello, lua")';

		# content_by_lua_block {
		#     ngx.say("hello lua")
		# }
    }
}

6.3 添加nginx/lua/get_ip.lua

local headers = ngx.req.get_headers()

for k, v in pairs(headers) do
	ngx.say(k,":", v)
end

ngx.say("X-REAL-IP", ":", headers["X-REAL-IP"])
ngx.say("X_FORWARDED_FOR", ":", headers["X_FORWARDED_FOR"])
ngx.say("remote_addr", ":", ngx.var.remote_addr)

一些配置信息

lua常用系統變量

方法 類型 說明
ngx.var 請求 如果要賦值如 ngx.var.b = 2,此變量必須提前聲明;另外對於 nginx location 中使用 正則捕獲的捕獲組可以使用 ngx.var [捕獲組數字]獲取;
ngx.req.get_headers 請求 獲取請求頭,默認只獲取前100,如果想要獲取所以可以調用ngx.req.get_header s(0);獲取帶中划線的請求頭時請使用如 headers.user_agent 這種方式;如果一個請求頭有多個值,則返回的 是 table;
ngx.req.get_uri_args 請求 獲取 url 請求參數,其用法和 get_headers 類似;
ngx.req.get_post_args 請求 獲取 post 請求內容體,其用法和 get_headers 類似,但是必須提前調用 ngx.req.r ead_body() 來讀取 body 體(也可以選擇在 nginx 配置文件使用lua_need_request_body on;開啟讀取 bod y 體,但是官方不推薦);
ngx.req.raw_header 請求 未解析的請求頭字符串;
ngx.req.get_body_data 請求 為解析的請求 body 體內容字符串。
ngx.req.get_method 請求 獲取請求的大寫字母形式的請求方式
ngx.header 響應 通過ngx.header.header_name的形式獲取或設置響應頭信息。
ngx.exit 響應 以某個狀態碼返回響應內容
ngx.redirect 響應 重定向當前請求到新的 url
ngx.log 其他 輸出到log日志
ngx.re.match 其他 正則匹配
ngx.md5 其他 md5編碼
ngx.encode_base64 其他 base64解碼
ngx.decode_base64 其他 base64編碼

模塊指令

每個模塊都有*_lua(指令)、*_lua_block(代碼塊)、*_lua_file(腳本文件)

指令 所在階段 使用范圍 說明
init_by_lua 加載配置文件 http 可以用於初始化全局配置
set_by_lua rewrite server location location if 復雜邏輯的變量賦值,注意是阻塞的
rewrite_by_lua rewrite http server location location if 實現復雜邏輯的轉發或重定向
content_by_lua content location location if 處理請求並輸出響應
header_filter_by_lua 響應頭信息過濾 http server location location if 設置響應頭信息
body_filter_by_lua 輸出過濾 http server location location if 對輸出進行過濾或修改

案例

訪問權限控制

location / {
    set $allowed '115.171.226.212';
    access_by_lua_block {
        if ngx.re.match(ngx.req.get_method(), "PUT|POST|DELETE") and not ngx.re.match(ngx.var.request_uri, "_search") then
			start, _ = string.find(ngx.var.allowed, ngx.var.remote_addr)
			if not start then
				ngx.exit(403)
			end
		end
    }
	
    proxy_pass http://127.0.0.1:8000$request_uri;
}

訪問頻率控制

在 Nginx 配置文件的 location 部分配置 Lua 腳本基本參數,並配置 Lua 模塊指令:

default_type "text/html";
set rate_per 300
access_by_lua_file lua/access.lua;

Lua 腳本實現頻率控制邏輯,使用 Redis 對單位時間內的訪問次數做緩存,key 為訪問 uri 拼接 token 后的 md5 值。具體內容如下:

local redis = require "resty.redis"
local red = redis:new()

local limit = tonumber(ngx.var.rate_per) or 200
local expire_time = tonumber(ngx.var.rate_expire) or 1000
local key = "rate.limit:string:"

red:set_timeout(500)
local ok, err = red:connect("www.fanhaobai.com", 6379)
if not ok then
    ngx.log(ngx.ERR, "failed to connect redis: " .. err)
    return
end

key = key .. ngx.md5(ngx.var.request_uri .. (ngx.req.get_uri_args()['token'] or ngx.req.get_post_args()['token']))
local times, err = red:incr(key)
if not times then
    ngx.log(ngx.ERR, "failed to exec incr: " .. err)
    return
elseif times == 1 then
    ok, err = red:expire(key, expire_time)
    if not ok then
        ngx.log(ngx.ERR, "failed to exec expire: " .. err)
        return
    end
end

if times > limit then
    return ngx.exit(403)
end

return


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM