Openresty与nginx的 执行阶段
常见的七个阶段的顺序:
set_by_lua
rewrite_by_lua
access_by_lua
content_by_lua
header_filter_by_lua
body_filter_by_lua
log_by_lua
每个阶段的作用:
set_by_lua: 流程分支处理判断变量初始化
rewrite_by_lua: 转发、重定向、缓存等功能(例如特定请求代理到外网)
access_by_lua: IP准入、接口权限等情况集中处理(例如配合iptable完成简单防火墙)
content_by_lua: 内容生成
header_filter_by_lua: 应答HTTP过滤处理(例如添加头部信息)
body_filter_by_lua: 应答BODY过滤处理(例如完成应答内容统一成大写)
log_by_lua: 会话完成后本地异步完成日志记录(日志可以记录在本地,还可以同步到其他机器)
知道这几个阶段之后,如何利用Nginx的函数变量去构建自己想要的系统,了解nginx变量和函数的作用域也是十分关键。
nginx的变量函数的阶段作用域:
ngx.arg
例子: val = ngx.arg[index]
作用域: set_by_lua*, body_filter_by_lua*
set_by_lua
'return tonumber(ngx.arg[1]) + tonumber(ngx.arg[2])';
ngx.var.VARIABLE
例子: ngx.var.VAR_NAME
作用域: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*
set $my_var '';
content_by_lua_* {
ngx.var.my_var = 123;
}
HTTP method constants
作用域:init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*
ngx.HTTP_GET
ngx.HTTP_HEAD
ngx.HTTP_PUT
ngx.HTTP_POST
ngx.HTTP_DELETE
ngx.HTTP_OPTIONS (added in the v0.5.0rc24 release)
ngx.HTTP_MKCOL (added in the v0.8.2 release)
ngx.HTTP_COPY (added in the v0.8.2 release)
ngx.HTTP_MOVE (added in the v0.8.2 release)
ngx.HTTP_PROPFIND (added in the v0.8.2 release)
ngx.HTTP_PROPPATCH (added in the v0.8.2 release)
ngx.HTTP_LOCK (added in the v0.8.2 release)
ngx.HTTP_UNLOCK (added in the v0.8.2 release)
ngx.HTTP_PATCH (added in the v0.8.2 release)
ngx.HTTP_TRACE (added in the v0.8.2 release)
HTTP status constants
作用域: init_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*
value = ngx.HTTP_CONTINUE (100) (first added in the v0.9.20 release) value = ngx.HTTP_SWITCHING_PROTOCOLS (101) (first added in the v0.9.20 release) value = ngx.HTTP_OK (200) value = ngx.HTTP_CREATED (201) value = ngx.HTTP_ACCEPTED (202) (first added in the v0.9.20 release) value = ngx.HTTP_NO_CONTENT (204) (first added in the v0.9.20 release) value = ngx.HTTP_PARTIAL_CONTENT (206) (first added in the v0.9.20 release) value = ngx.HTTP_SPECIAL_RESPONSE (300) value = ngx.HTTP_MOVED_PERMANENTLY (301) value = ngx.HTTP_MOVED_TEMPORARILY (302) value = ngx.HTTP_SEE_OTHER (303) value = ngx.HTTP_NOT_MODIFIED (304) value = ngx.HTTP_TEMPORARY_REDIRECT (307) (first added in the v0.9.20 release) value = ngx.HTTP_BAD_REQUEST (400) value = ngx.HTTP_UNAUTHORIZED (401) value = ngx.HTTP_PAYMENT_REQUIRED (402) (first added in the v0.9.20 release) value = ngx.HTTP_FORBIDDEN (403) value = ngx.HTTP_NOT_FOUND (404) value = ngx.HTTP_NOT_ALLOWED (405) value = ngx.HTTP_NOT_ACCEPTABLE (406) (first added in the v0.9.20 release) value = ngx.HTTP_REQUEST_TIMEOUT (408) (first added in the v0.9.20 release) value = ngx.HTTP_CONFLICT (409) (first added in the v0.9.20 release) value = ngx.HTTP_GONE (410) value = ngx.HTTP_UPGRADE_REQUIRED (426) (first added in the v0.9.20 release) value = ngx.HTTP_TOO_MANY_REQUESTS (429) (first added in the v0.9.20 release) value = ngx.HTTP_CLOSE (444) (first added in the v0.9.20 release) value = ngx.HTTP_ILLEGAL (451) (first added in the v0.9.20 release) value = ngx.HTTP_INTERNAL_SERVER_ERROR (500) value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501) value = ngx.HTTP_BAD_GATEWAY (502) (first added in the v0.9.20 release) value = ngx.HTTP_SERVICE_UNAVAILABLE (503) value = ngx.HTTP_GATEWAY_TIMEOUT (504) (first added in the v0.3.1rc38 release) value = ngx.HTTP_VERSION_NOT_SUPPORTED (505) (first added in the v0.9.20 release) value = ngx.HTTP_INSUFFICIENT_STORAGE (507) (first added in the v0.9.20 release)
Nginx log level constants
作用域: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*
ngx.STDERR ngx.EMERG ngx.ALERT ngx.CRIT ngx.ERR ngx.WARN ngx.NOTICE ngx.INFO ngx.DEBUG
例子: print(...)
作用域: init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*
ngx.ctx
注意和ngx.var.VARIABLE的区别,这个是一个完整请求的作用范围,一个是某个请求阶段的作用范围。
作用域: init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*
location /test { rewrite_by_lua_block { ngx.ctx.foo = 76 } access_by_lua_block { ngx.ctx.foo = ngx.ctx.foo + 3 } content_by_lua_block { ngx.say(ngx.ctx.foo) } }
不过要注意几种使用情况:有子请求的情况下,请求中执行另一个请求的情况下:作用范围不适用eg:
location /sub {
content_by_lua_block {
ngx.say("sub pre: ", ngx.ctx.blah)
ngx.ctx.blah = 32
ngx.say("sub post: ", ngx.ctx.blah)
}
}
location /main {
content_by_lua_block {
ngx.ctx.blah = 73
ngx.say("main pre: ", ngx.ctx.blah)
local res = ngx.location.capture("/sub")
ngx.print(res.body)
ngx.say("main post: ", ngx.ctx.blah)
}
}
Then GET /main will give the output
main pre: 73
sub pre: nil
sub post: 32
main post: 73
-----------------------------------------------
location /new {
content_by_lua_block {
ngx.say(ngx.ctx.foo)
}
}
location /orig {
content_by_lua_block {
ngx.ctx.foo = "hello"
ngx.exec("/new")
}
}
Then GET /orig will give
nil
ngx.location.capture
例子: res = ngx.location.capture(uri, options?)
作用域: rewrite_by_lua*, access_by_lua*, content_by_lua*
在一个location中执行子请求的时候,是非阻塞的,这个特性非常关键,你以location划分功能,再组合。
ngx.location.capture_multi(混合子请求,同上)
例子: res1, res2, ... = ngx.location.capture_multi({ {uri, options?}, {uri, options?}, ... })
作用域: rewrite_by_lua*, access_by_lua*, content_by_lua*
ngx.header.HEADER
例子: ngx.header.HEADER = VALUE
例子: value = ngx.header.HEADER
作用域: rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*
-- equivalent to ngx.header["Content-Type"] = 'text/plain' ngx.header.content_type = 'text/plain'; ngx.header["X-My-Header"] = 'blah blah';
Multi-value headers can be set this way:
ngx.header['Set-Cookie'] = {'a=32; path=/', 'b=4; path=/'}
ngx.req.get_method
例子: method_name = ngx.req.get_method()
作用域: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, balancer_by_lua*
ngx.req.set_uri
syntax: ngx.req.set_uri(uri, jump?)
context: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*
ngx.req.get_uri_args
例子: args = ngx.req.get_uri_args(max_args?)
作用域: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, balancer_by_lua*
Returns a Lua table holding all the current request URL query arguments.
location = /test { content_by_lua_block { local args = ngx.req.get_uri_args() for key, val in pairs(args) do if type(val) == "table" then ngx.say(key, ": ", table.concat(val, ", ")) else ngx.say(key, ": ", val) end end } }
ngx.req.socket
syntax: tcpsock, err = ngx.req.socket()
syntax: tcpsock, err = ngx.req.socket(raw)
context: rewrite_by_lua*, access_by_lua*, content_by_lua*
这个socket是nginx中非阻塞的socket,作用范围很明显,但只要掌握一定技巧使用还是可以越过作用范围的。
https://github.com/cloudflare/lua-resty-logger-socket 作者跳过作用域的一个作品,通过的nginx.time.at(点到为止,有兴趣的同学可以看看源码)
ngx.exec
syntax: ngx.exec(uri, args?)
context: rewrite_by_lua*, access_by_lua*, content_by_lua*
ngx.exec('/some-location'); ngx.exec('/some-location', 'a=3&b=5&c=6'); ngx.exec('/some-location?a=3&b=5', 'c=6');
ngx.print
syntax: ok, err = ngx.print(...)
context: rewrite_by_lua*, access_by_lua*, content_by_lua*
Emits arguments concatenated to the HTTP client (as response body). If response headers have not been sent, this function will send headers out first and then output body data.
local table = { "hello, ", {"world: ", true, " or ", false, {": ", nil}} } ngx.print(table)
ngx.sleep
syntax: ngx.sleep(seconds)
context: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*
ngx.escape_uri
syntax: newstr = ngx.escape_uri(str)
context: init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*
ngx.unescape_uri
syntax: newstr = ngx.unescape_uri(str)
context: init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*
ngx.say(ngx.unescape_uri("b%20r56+7"))
gives the output
b r56 7
ngx.encode_args
syntax: str = ngx.encode_args(table)
context: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*
ngx.encode_args({foo = 3, ["b r"] = "hello world"})
yields
foo=3&b%20r=hello%20world
Multi-value query args are also supported. Just use a Lua table for the argument's value, for example:
ngx.encode_args({baz = {32, "hello"}})
gives
baz=32&baz=hello
ngx.decode_args
syntax: table = ngx.decode_args(str, max_args?)
context: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*
ngx.encode_base64
syntax: newstr = ngx.encode_base64(str, no_padding?)
context: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*
ngx.decode_base64
syntax: newstr = ngx.decode_base64(str)
context: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*
ngx.md5
syntax: digest = ngx.md5(str)
context: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*
location = /md5 { content_by_lua_block { ngx.say(ngx.md5("hello")) } }
yields the output
5d41402abc4b2a76b9719d911017c592
ngx.is_subrequest
syntax: value = ngx.is_subrequest
context: set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*
Returns true
if the current request is an nginx subrequest, or false
otherwise.
ngx.re.match
syntax: captures, err = ngx.re.match(subject, regex, options?, ctx?, res_table?)
context: init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*
ngx.re.find
syntax: from, to, err = ngx.re.find(subject, regex, options?, ctx?, nth?)
context: init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*
ngx.shared.DICT
syntax: dict = ngx.shared.DICT
syntax: dict = ngx.shared[name_var]
context: init_by_lua*, init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*, ssl_session_store_by_lua*
Fetching the shm-based Lua dictionary object for the shared memory zone named DICT
defined by thelua_shared_dict directive.
Shared memory zones are always shared by all the nginx worker processes in the current nginx server instance.
The resulting object dict
has the following methods:
get,get_stale,set,safe_set,add,safe_add,replace,delete,incr,lpush,rpush,lpop,rpop
llen,flush_all,flush_expired,get_keys
Here is an example:
http { lua_shared_dict dogs 10m; server { location /set { content_by_lua_block { local dogs = ngx.shared.dogs dogs:set("Jim", 8) ngx.say("STORED") } } location /get { content_by_lua_block { local dogs = ngx.shared.dogs ngx.say(dogs:get("Jim")) } } } }
Let us test it:
$ curl localhost/set STORED $ curl localhost/get 8 $ curl localhost/get 8
ngx.socket.udp(想要二次开发很关键的函数)
syntax: udpsock = ngx.socket.udp()
context: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*
Creates and returns a UDP or datagram-oriented unix domain socket object (also known as one type of the "cosocket" objects). The following methods are supported on this object:
It is intended to be compatible with the UDP API of the LuaSocket library but is 100% nonblocking out of the box.
ngx.socket.tcp(想要二次开发很关键的函数)
syntax: tcpsock = ngx.socket.tcp()
context: rewrite_by_lua*, access_by_lua*, content_by_lua*, ngx.timer.*, ssl_certificate_by_lua*, ssl_session_fetch_by_lua*
Creates and returns a TCP or stream-oriented unix domain socket object (also known as one type of the "cosocket" objects). The following methods are supported on this object:
It is intended to be compatible with the TCP API of the LuaSocket library but is 100% nonblocking out of the box. Also, we introduce some new APIs to provide more functionalities.
The cosocket object created by this API function has exactly the same lifetime as the Lua handler creating it. So never pass the cosocket object to any other Lua handler (including ngx.timer callback functions) and never share the cosocket object between different NGINX requests.
For every cosocket object's underlying connection, if you do not explicitly close it (via close) or put it back to the connection pool (via setkeepalive), then it is automatically closed when one of the following two events happens:
- the current request handler completes, or
- the Lua cosocket object value gets collected by the Lua GC.
Fatal errors in cosocket operations always automatically close the current connection (note that, read timeout error is the only error that is not fatal), and if you call close on a closed connection, you will get the "closed" error.
Starting from the 0.9.9
release, the cosocket object here is full-duplex, that is, a reader "light thread" and a writer "light thread" can operate on a single cosocket object simultaneously (both "light threads" must belong to the same Lua handler though, see reasons above). But you cannot have two "light threads" both reading (or writing or connecting) the same cosocket, otherwise you might get an error like "socket busy reading" when calling the methods of the cosocket object.