编辑nginx.conf配置文件
vim /usr/local/nginx/conf/nginx.conf
在http部分添加配置
#lua模块路径,多个之间用";"分隔,其中";;"表示默认搜索路径,默认到 /usr/local/nginx 下找包路径
lua_package_path "/usr/local/lualib/?.lua;;"; #lua模块
lua_package_cpath "/usr/local/lualib/?.so;;"; # c模块
include lua.conf; #在http部分添加lua配置文件
编辑lua.conf文件
在 /usr/local/nginx/conf 目录下创建一个lua.conf
vim /usr/local/nginx/conf/lua.conf
#lua.conf
server{
listen 80;
server_name _;
}
# 将代码写在配置文件里面的简易版,这有一个问题,代码多了之后,不容易维护
location /lua{ # 添加访问接口,接口名字是 /lua 返回helloword
# http://127.0.0.1/lua 返回 hello world
default_type "text/html";
content_by_lua "ngx.say('hello world')";
}
# 升级版 location
location /lua{
default_type "text/html";
content_by_lua_file conf/lua/test.lua; # 相对于nginx 安装目录
# 这里也可以使用绝对路径 /usr/local/nginx/conf/lua/test.lua
lua_code_cache off;
# lua_code_cache 默认是开启的,即缓存lua代码,即每次lua代码变更必须reload nginx才生效
# 如果在开发阶段可以通过lua_code_cache off; 关闭缓存,这样调试时每次修改lua代码不需要reload nginx
# 但是正式环境一定记得开启缓存
}
# 然后编辑test.lua 文件
vim /usr/local/nginx/conf/lua/test.lua
# 添加以下内容
ngx.say("hello world");
测试nginx是否正常
/usr/local/nginx/sbin/nginx -t
显示如下内容说明配置成功
nginx: the configuration file /usr/servers/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/servers/nginx/conf/nginx.conf test is successful
重启nginx
/usr/local/nginx/sbin/nginx -s reload
nginx|lua API
ngx.req.discard_body()
忽略请求体,如果不需要post传送的参数,就在lua文件首行加这句话,如果需要body内容就要不能加这句话,
要加ngx.req.read_body(),下面有获取post参数的nginx|lua API
ngx.req.get_headers([max_headers]) 获取请求头
获取请求的请求头信息。返回结果是一个table。
local headers, err = ngx.req.get_headers()
ngx.say("host: ", headers["Host"])
当遇到一个key对应多个值的时候,比如
Foo: foo
Foo: bar
Foo: baz
会返回一个table
ngx.req.get_headers()["Foo"] 会得到 {"foo", "bar", "baz"}
ngx.req.get_headers() 有一个可选参数max_headers来控制能够最大处理一个key对应的多少个值
比如:
ngx.req.get_headers(10) 上面返回的table中最多有10个元素,多余的参数会被默认丢弃
ngx.req.get_headers(0) 移除最大限制。
在获取headers里面的内容时,会默认全部转换为小写取请求参数
ngx.req.get_uri_args([max_args]) 获取get请求的参数
获取uri里面的参数,返回结果是一个table
不一样的uri获取到的参数类型不一样,比如以下这些请求。
GET /test?foo=bar&bar=baz&bar=blah 一键多值类型
结果 foo : bar (字符串) bar : {"baz","blah"} (table类型)
GET /test?a%20b=1%61+2 非转义字符类型
结果 a b: 1a 2 非转义的字符会自动转义过来
GET /test?foo&bar
结果 foo:true bar:true 布尔类型
GET /test?foo=&bar=
结果 foo: bar: 空字符串类型
GET /test?=hello&=world 空的key类型,这种参数将自动丢弃
返回一个空的输出 return an empty output for instances
ngx.req.read_body() 获取POST请求的参数
ngx.req.read_body()
local args = ngx.req.get_post_args()
注:
如果使用 ngx.req.get_post_args() 得到的将是一个lua table
如果使用 ngx.req.get_body_data() 得到的将是一个lua string, rather then a lua table
If the request body is already read previously by turning on lua_need_request_body or by using other modules, then this function does not run and returns immediately.
如果请求体已经被lua_need_request_body或者其他模块抢先解析了,
则ngx.req.read_body()不会执行,并且立即返回
If the request body has already been explicitly discarded, either by the ngx.req.discard_body function or other modules, this function does not run and returns immediately.
如果请求体通过ngx.req.discard_body()明确放弃解析,那么ngx.req.read_body()
也不会执行,并且立即返回
ngx.req.http_version() 请求http协议版本
Returns the HTTP version number for the current request as a Lua number
ngx.req.get_method() 获取请求的请求方法
Retrieves the current request's request method name. Strings like `"GET"` and `"POST"` are returned instead of numerical method constants.
If the current request is an Nginx subrequest, then the subrequest's method name will be returned.
ngx.req.raw_header([true]) 获取原始的请求头内容
Returns the original raw HTTP protocol header received by the Nginx server.
ngx.print(ngx.req.raw_header())
GET /t HTTP/1.1
Host: localhost
Connection: close
Foo: bar
You can specify the optional no_request_line argument as a true value to exclude the request line from the result. For example,
ngx.print(ngx.req.raw_header(true))
Host: localhost
Connection: close
Foo: bar
输出响应
ngx.header 输出响应头
ngx.print 输出相应内容体
ngx.say 同ngx.print 但是最后输出一个换行符
ngx.exit 指定状态码退出
example.conf 配置文件
location /lua_response_1{
default_type "text/html";
content_by_lua_file /usr/example/lua/test_response_1.lua
}
test_response_1.lua文件
--写响应头
ngx.header.a = "1"
--多个响应头可以使用table
ngx.header.b = {"2", "3"}
--输出响应
ngx.say("a","b","<br/>")
ngx.print("c","d","<br/>")
--200状态码推出
return ngx.exit(200)
ngx.redirect 重定向
ngx.redirect("http://jd.com",302)
ngx.status=状态码,设置响应的状态码 ngx.resp.get_headers()或者设置的响应状态码
ngx.send_headers()发送响应状态码,当调用ngx.say/ngx.print时自动发送响应状态码;
可以通过ngx.headers_sent=true 判断是否发送了响应状态码
nginx其他API
ngx.escape_uri/ngx.unescape_uri : uri 编码解码
ngx.encode_args/ngx.decode_args : 参数编码解码
ngx.encode_base64/ngx.decode_base64 : BASE64编码解码
ngx.re.match :nginx正则表达式匹配
比如
--未经解码的请求uri
local request_uri = ngx.var.request_uri;
ngx.say("request_uri : ", request_uri, "<br/>");
--解码
ngx.say("decode request_uri : ", ngx.unescape_uri(request_uri), "<br/>");
--MD5
ngx.say("ngx.md5 : ", ngx.md5("123"), "<br/>")
--http time
ngx.say("ngx.http_time : ", ngx.http_time(ngx.time()), "<br/>")
nginx全局内存
nginx是一个master进程多个worker进程的工作方式,因此我们可能需要在多个worker进程中共享数据。
那么此时就使用ngx.shared.DICT来实现全局内存共享
1.首先在nginx.conf的http部分分配内存大小
#共享全局变量,在所有worker间共享
lua_shared_dict shared_data 1m;
2.example.conf配置文件
location /lua_shared_dict{
default_type "text/html";
content_by_lua_file /usr/example/lua/test_lua_shared_dict.lua;
}
3.test_lua_shared_dict.lua
-- 1.获取全局共享内存变量
local shared_data = ngx.shared.shared_data
-- 2.获取字典值
local i = shared_data:get("i")
if not i then
i = 1
-- 3.惰性赋值
shared_data:set("i",i)
ngx.say("lazy set i", i, "<br/>")
end
-- 递增
i = shared_data:incr("i",1)
ngx.say("i=",i,"<br/>")
ngxin lua 模块指令
init_by_lua 和 init_by_lua_file
所处的处理阶段 loading-config
使用的范围 http
每次nginx重新加载配置时执行,可以用它来完成一些耗时模块的加载,或者初始化一些全局配置;
在Master进程创建worker进程时,此指令中加载的全局变量会进行copy-onwrite,即会复制到所有全局变量到worker进程中
- nginx.conf配置文件中的http部分添加
# 共享全局变量,在所有worker间共享
lua_shared_dict shared_data 1m;
init_by_lua_file /usr/example/lua/init.lua
- init.lua
-- 初始化耗时的模块
local redis = require 'resty.redis'
local cjson = require 'cjson'
-- 全局变量,不推荐
count = 1
-- 共享全局内存
local shared_data = ngx.shared.shared_data
shared_data : set("count",1)
- test.lua
count = count + 1
ngx.say("global variable:",count)
local shared_data = ngx.shared.shared_data
ngx.say(", shared memory:", shared_data:get("count"))
shared_data:incr("count",1)
ngx.say("hello,world")
4.访问http://localhost/lua 会发现全局变量一直不变,而共享内存一直递增
global variable : 2, shared memory : 8 hello world
注意:一定在生产环境开启lua_code_cache,否则每个请求都会创建lua VM实例
init_worker_by_lua 和init_worker_by_lua_file
所处的处理阶段 starting-worker
使用的范围 http
通常用于定时拉取配置/数据,或者后端服务的健康检查
用于启动一些定时任务,比如心跳检查,定时拉取服务器配置
此处的任务是跟worker进程数量有关系的,比如有2个worker进程
那么就会启动两个完全一样的定时任务
- nginx.conf配置文件中的http部分添加
init_worker_by_lua_file /usr/example/lua/init_worker.lua;
2.init_worker.lua
local count = 0
local delayInSeccounds = 3
local heartbeatCheck = nil
heartbeatCheck = function(args)
count = count + 1
ngx.log(ngx.ERR, "do check", count)
local ok , err = ngx.timer.at(delayInSeconds,heartbeatCheck)
if not ok then
ngx.log(ngx.ERR,"failed to startup heartbeart worker ...", err)
end
end
heartbeatCheck()
另外根据实际情况设置如下指令
lua_max_pending_timers 1024; # 最大等待任务数
lua_max_running_timers 256; # 最大同时运行任务数
set_by_lua 或 set_by_lua_file
设置nginx变量
语法 set_by_lua 变量名 'lua_code';
set_by_lua_file 变量名 file_path;
local uri_args =ngx.req.get_uri_args() -- 获取url 参数
rewrite_by_lua 或 rewrite_by_lua_file
语法
rewrite_by_lua "lua_code"
rewrite_by_lua_file file_path
可能会用到的语法
ngx.redirect("url", status) 重定向到url ,状态码status
ngx.req.set_uri(uri,false) 内部重写uri,但是false参数决定不重新发起请求
ngx.req.set_uri(uri,true) 内部重写uri,true参数决定会重新发起请求
ngx.req.set_uri_args({a=1,b=2}) 重写请求的uri参数
access_by_lua 和 access_by_lua_file 用于访问控制
比如我们只允许内网ip访问,可以使用以下形式
allow 127.0.0.1;
allow 192.168.0.0/16;
deny all;
有可能用到的其他技术
ngx.exit(status) -- 立即中断当前http请求,后续lua代码将不会再执行,status代表状态码 。不影响使用了proxy_pass的子请求。
content_by_lua 和 content_by_lua_file
用于location中, 给每个请求执行一段lua代码
ngx.sub(str,start,stop)
截取字符串,范围是从start到 stop