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
原文地址:https://www.cnblogs.com/iFanLiwei/p/13574968.html