openresty学习笔记

  1. lua_package_path “conf/lua/?.lua”; 表示多个字符串的通配符为?,不是 *

  2. lua中涉及到路径,如果没有指定绝对路径,那前缀为 安装 openresty 的 路径前缀。比如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    lua_package_path "lua/?.lua" :路径为/usr/local/openresty/lua/?.lua
    access_by_lua_file lua/access_check.lua : 路径为/usr/local/openresty/lua/access_check.lua
    ```
    3. ngx.var.arg_a : 获取url中参数a的值;
    ngx.var.内部变量名:获取内部变量值,如ngx.var.remote_addr 获取访问用户地址
    Setting ngx.var.Foo to a nil value will unset the $Foo Nginx variable.
    ```lua
    ngx.var.args = nil
    ```
    4. ngx.var.limit_rate = 1000 限速,注意:并不是每个nginx内置变量都可以改变。
    5. 如果需要在Lua中处理错误,必须使用函数pcall(protected call)来包装需要执行的代码。 pcall接收一个函数和要传递给后者的参数,并执行,执行结果:有错误、无错误;返回值true或者或false, errorinfo。pcall以一种"保护模式"来调用第一个参数,因此pcall可以捕获函数执行中的任何错误。
    ```lua
    pcall(function (str) json_value = json.decode(str) end, str)
  1. debug

    1
    lua_code_cache off;

    修改完代码后,不用reload nginx就可以生效了。在生产环境下记得打开这个选项。

  2. 读取post数据:curl -XPOST ‘xxxxx/test’ -d ‘a=xxx&b=yyy’

    1
    2
    ngx.req.read_body() -- explicitly read the req body
    local data = ngx.req.get_body_data() => a=xxx&b=yyy
  1. local file = ngx.req.get_body_file() 获取文件内容

    1
    curl -XPOST 'xxxxx/test' -d '@/etc/passwd' file的值为本地文件/etc/passwd的内容。
  2. ngx.location.capture

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    res = ngx.location.capture("/some_other_location")
    ```
    res结果是 http://xxxxx/some_other_location 这个链接的输出。
    Issuing a POST subrequest, for example, can be done as follows
    ```lua
    res = ngx.location.capture(
    '/foo/bar',
    { method = ngx.HTTP_POST, body = 'hello, world' }
    )
res包括: 
res.status, res.header, res.body, and res.truncated

The args option can specify extra URI arguments, for instance,
1
2
3
ngx.location.capture('/foo?a=1',
{ args = { b = 3, c = ':' } }
)
is equivalent to
1
ngx.location.capture('/foo?a=1&b=3&c=%3a')
that is, this method will escape argument keys and values according to URI rules and concatenate them together into a complete query string. The format for the Lua table passed as the args argument is identical to the format used in thengx.encode_args method.

The args option can also take plain query strings:
1
2
3
ngx.location.capture('/foo?a=1',
{ args = 'b=3&c=%3a' } }
)
ngx.location.capture_multi :同时捕获多个链接输出

The ngx.location.capture and ngx.location.capture_multi directives cannot capture locations that include the add_before_body, add_after_body, auth_request, echo_location, echo_location_async, echo_subrequest, or echo_subrequest_async directives.
1
2
3
4
5
6
7
8
9
10
11
12
13
res1, res2, res3 = ngx.location.capture_multi{
{ "/foo", { args = "a=3&b=4" } },
{ "/bar" },
{ "/baz", { method = ngx.HTTP_POST, body = "hello" } },
}
if res1.status == ngx.HTTP_OK then
...
end
if res2.body == "BLAH" then
...
end
Lua tables can be used for both requests and responses when the number of subrequests to be issued is not known in advance:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
-- construct the requests table
local reqs = {}
table.insert(reqs, { "/mysql" })
table.insert(reqs, { "/postgres" })
table.insert(reqs, { "/redis" })
table.insert(reqs, { "/memcached" })
-- issue all the requests at once and wait until they all return
local resps = { ngx.location.capture_multi(reqs) }
-- loop over the responses table
for i, resp in ipairs(resps) do
-- process the response table "resp"
end
  1. Capture

    1
    2
    3
    4
    location ~ ^/app/([-_a-zA-Z0-9/]+) {
    set $path $1; $1为location中匹配到的部分
    content_by_lua_file /path/to/lua/app/root/$path.lua;
    }
  1. 重定向

    1
    ngx.redirect("/terms_of_use.html") :重定向
  1. 伪异步

    1
    nginx.eof():
与客户端断开连接,但是继续向下执行以后的代码,此时nginx进程还没有释放,类似php的fastcgi_request_finish.
  1. ngx.say && ngx.print

    1
    ngx.say、ngx.print:功能一样,say会额外输出一个换行
  1. set

    1
    2
    3
    set $foo 32;
    set_by_lua $bar 'return tonumber(ngx.var.foo) + 1';
    set $baz "bar: $bar"; # $baz == "bar: 33"
  1. header_filter_by_lua:增加response header

    1
    2
    3
    4
    location / {
    proxy_pass http://mybackend;
    header_filter_by_lua 'ngx.header.Foo = "blah"';
    }
  1. ngx.arg

    When this is used in the context of the set_by_lua or set_by_lua_file directives, this table is read-only and holds the input arguments to the config directives:

    1
    value = ngx.arg[n]
Here is an example

1
2
3
4
5
6
7
8
location /foo {
set $a 32;
set $b 56;
set_by_lua $sum
'return tonumber(ngx.arg[1]) + tonumber(ngx.arg[2])' $a $b;
echo $sum;
}
that writes out 88, the sum of 32 and 56.
  1. HTTP方法常量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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)
  1. HTTP status常量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    value = ngx.HTTP_OK (200)
    value = ngx.HTTP_CREATED (201)
    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_BAD_REQUEST (400)
    value = ngx.HTTP_UNAUTHORIZED (401)
    value = ngx.HTTP_FORBIDDEN (403)
    value = ngx.HTTP_NOT_FOUND (404)
    value = ngx.HTTP_NOT_ALLOWED (405)
    value = ngx.HTTP_GONE (410)
    value = ngx.HTTP_INTERNAL_SERVER_ERROR (500)
    value = ngx.HTTP_METHOD_NOT_IMPLEMENTED (501)
    value = ngx.HTTP_SERVICE_UNAVAILABLE (503)
    value = ngx.HTTP_GATEWAY_TIMEOUT (504) (first added in the v0.3.1rc38 release)
  1. Nginx log level constants

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ngx.STDERR
    ngx.EMERG
    ngx.ALERT
    ngx.CRIT
    ngx.ERR
    ngx.WARN
    ngx.NOTICE
    ngx.INFO
    ngx.DEBUG
  1. ngx.ctx:在一个请求中传递变量
    This table can be used to store per-request Lua context data and has a life time identical to the current request (as with the Nginx variables).
    Consider the following example,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    location /test {
    rewrite_by_lua '
    ngx.ctx.foo = 76
    ';
    access_by_lua '
    ngx.ctx.foo = ngx.ctx.foo + 3
    ';
    content_by_lua '
    ngx.say(ngx.ctx.foo)
    ';
    }
    ```
    Then GET /test will yield the output
    ```lua
    79
That is, the ngx.ctx.foo entry persists across the rewrite, access, and content phases of a request.
Every request, including subrequests, has its own copy of the table. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
location /sub {
content_by_lua '
ngx.say("sub pre: ", ngx.ctx.blah)
ngx.ctx.blah = 32
ngx.say("sub post: ", ngx.ctx.blah)
';
}
location /main {
content_by_lua '
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
1
2
3
4
main pre: 73
sub pre: nil
sub post: 32
main post: 73
Here, modification of the ngx.ctx.blah entry in the subrequest does not affect the one in the parent request. This is because they have two separate versions of ngx.ctx.blah. Internal redirection will destroy the original request ngx.ctx data (if any) and the new request will have an empty ngx.ctxtable. For instance,
1
2
3
4
5
6
7
8
9
10
11
12
location /new {
content_by_lua '
ngx.say(ngx.ctx.foo)
';
}
location /orig {
content_by_lua '
ngx.ctx.foo = "hello"
ngx.exec("/new")
';
}
Then GET /orig will give
1
nil
  1. ngx.status:设置或获取nginx status

    1
    2
    ngx.status = ngx.HTTP_CREATED
    status = ngx.status

    Setting ngx.status after the response header is sent out has no effect,but leaving an error message in your nginx’s error log file:
    attempt to set ngx.status after sending out response headers

  2. ngx.header.HEADER

    syntax: ngx.header.HEADER = VALUE
    syntax: value = ngx.header.HEADER
    header name中的下划线默认会被替换成中划线,lua_transform_underscores_in_response_headers可以关闭。
    The header names are matched

    1
    case-insensitively:大小写不敏感.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    -- 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=/'}
    will yield
    Set-Cookie: a=32; path=/
    Set-Cookie: b=4; path=/

    in the response headers.

    Setting a slot to nil effectively removes it from the response headers:

    1
    ngx.header["X-My-Header"] = nil;
The same applies to assigning an empty table:

1
ngx.header["X-My-Header"] = {};
This is particularly useful in the context of header_filter_by_lua and header_filter_by_lua_file, for example,
1
2
3
4
5
6
7
8
9
10
11
12
13
location /test {
set $footer '';
proxy_pass http://some-backend;
header_filter_by_lua '
if ngx.header["X-My-Header"] == "blah" then
ngx.var.footer = "some value"
end
';
echo_after_body $footer;
}
For multi-value headers, all of the values of header will be collected in order and returned as a Lua table. For example, response headers Foo: bar Foo: baz will result in
1
{"bar", "baz"}
to be returned when reading ngx.header.Foo.
  1. ngx.resp.get_headers/ngx.req.get_headers:获取header

    syntax: headers = ngx.resp.get_headers(max_headers?, raw?)
    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 a Lua table holding all the current response headers for the current request.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    local h = ngx.req.get_headers()
    for k, v in pairs(h) do
    ngx.say("Req Header ---> " .. k .. "==> ' .. v)
    end
    local h = ngx.resp.get_headers()
    for k, v in pairs(h) do
    ngx.say("Resp Header <---- " .. k .. "==> ' .. v)
    end
    header_filter_by_lua_block {
    ngx.header.X-Foo-bar = "Hit"
    }
  2. ngx.req.start_time:返回当前请求被创建的时间

    1
    local request_time = ngx.now() - ngx.req.start_time()
  1. ngx.req.http_version获取 http 版本

  2. ngx.req.raw_header(bool no_request_line):获取原始header字符串

    参数no_request_line决定是否返回 ‘GET /t HTTP/1.1’ 类似的请求信息

  3. ngx.req.get_method()、ngx.req.set_method:获取、设置method

  4. ngx.req.set_uri

    syntax: ngx.req.set_uri(uri, jump?)
    For instance, Nginx config

    1
    rewrite ^ /foo?a=3? last;
can be coded as

1
2
ngx.req.set_uri_args("a=3")
ngx.req.set_uri("/foo", true)
or
1
2
ngx.req.set_uri_args({a = 3})
ngx.req.set_uri("/foo", true)
  1. ngx.req.set_uri_args

    1
    2
    3
    ngx.req.set_uri_args("a=3&b=hello%20world")
    ngx.req.set_uri_args({ a = 3, b = "hello world" }) 效果相同
    ngx.req.set_uri_args({ a = 3, b = {5, 6} }) --> a=3&b=5&b=6.
  1. ngx.req.get_uri_args:获取url参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    location = /test {
    content_by_lua '
    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
    ';
    }

    Then GET /test?foo=bar&bar=baz&bar=blah will yield the response body

    1
    2
    foo: bar
    bar: baz, blah
Arguments without the = parts are treated as boolean arguments. GET /test?foo&bar will yield:

1
2
foo: true
bar: true
GET /test?foo=&bar= will give something like
1
2
foo:
bar:
Updating query arguments via the nginx variable $args (or ngx.var.args in Lua) at runtime is also supported:
1
2
ngx.var.args = "a=3&b=42"
local args = ngx.req.get_uri_args()
Here the args table will always look like
1
{a = 3, b = 42}
  1. 在一个nignx worker内共享数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    -- mydata.lua
    local _M = {}
    local data = {
    dog = 3,
    cat = 4,
    pig = 5,
    }
    function _M.get_age(name)
    return data[name]
    end
    function _M.set_age(name,value)
    data[name] = value
    end
    return _M
    location /lua {
    content_by_lua '
    local mydata = require "mydata"
    ngx.say(mydata.get_age("dog"))
    mydata.set_age("dog",1111)
    ';
    }
    ```
    连续访问两次,第一次输出3 ,第二次输出1111
    注意:需要以下设置才能生效:
    ```lua
    worker_processes 1; //多个进程时,可能需要刷新多次才能出现
    lua_code_cache on;

最后:

  • 尽量在声明变量时使用local
  • 使用ngx.var,ngx.print等的时候也尽量设定为本地变量
  • 错误处理需要使用pcall 包装要执行的代码
  • 使用require加载模块
  • 请求返回可以继续执行任务(fastcgi_finish, ngx.eof()) 尾调用
  • 连接池使用(set_keep_alive, redis/mysql要用)
  • 数组下标1 #aa 可以获取数组大小,但是千万不要使用
  • ffi的使用

    参考: phper