nginx_lua脚本学习

NgxLua执行过程

ngx_lua属于nginx的一部分,它的执行指令都包含在nginx的11个步骤之中了,相应的处理阶段可以做插入式处理,即可插拔式架构,不过ngx_lua并不是所有阶段都会运行的;另外指令可以在http、server、server if、location、location if几个范围进行配置:

指令 所处处理阶段 使用范围 解释
init_by_lua loading-config http nginx Master进程加载配置时执行;
init_by_lua_file 通常用于初始化全局配置/预加载Lua模块
init_worker_by_lua starting-worker http 每个Nginx Worker进程启动时调用的计时器,如果Master进程不允许则只会在init_by_lua之后调用;
init_worker_by_lua_file 通常用于定时拉取配置/数据,或者后端服务的健康检查
set_by_lua rewrite server,server if,location,location if 设置nginx变量,可以实现复杂的赋值逻辑;此处是阻塞的,Lua代码要做到非常快;
set_by_lua_file
rewrite_by_lua rewrite tail http,server,location,location if rrewrite阶段处理,可以实现复杂的转发/重定向逻辑;
rewrite_by_lua_file
access_by_lua access tail http,server,location,location if 请求访问阶段处理,用于访问控制
access_by_lua_file
content_by_lua content location,location if 内容处理器,接收请求处理并输出响应
content_by_lua_file
header_filter_by_lua output-header-filter http,server,location,location if 设置header和cookie
header_filter_by_lua_file
body_filter_by_lua output-body-filter http,server,location,location if 对响应数据进行过滤,比如截断、替换。
body_filter_by_lua_file
log_by_lua log http,server,location,location if log阶段处理,比如记录访问量/统计平均响应时间
log_by_lua_file

基本操作

    location /echo {
        default_type 'text/plain';
        echo 'hello world';
    }

    location /luatest {
        default_type 'text/plain';
        content_by_lua '
        ngx.say("request_method:",ngx.var.request_method)
        local args = nil
        if ngx.var.request_method == "POST" then
            ngx.req.read_body()
            args = ngx.req.get_post_args()
        elseif ngx.var.request_method == "GET" then
            args = ngx.req.get_uri_args()
        else
            args = "no"
        end
        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
        ';
    }

    location = /request_body {
        default_type 'text/plain';
        client_max_body_size 50k;
        client_body_buffer_size 50k;

        content_by_lua '
            ngx.req.read_body()  -- explicitly read the req body
            local data = ngx.req.get_body_data()
            if data then
                ngx.say("body data:")
                ngx.print(data)
                return
            end

            -- body may get buffered in a temp file:
            local file = ngx.req.get_body_file()
            if file then
                ngx.say("body is in file ", file)
            else
                ngx.say("no body found")
            end
        ';
    }

nginx映射到Lua的变量

ngx.arg[1]                脚本参数
ngx.var['arg_a']            取queryString的参数a #/nginx_var?a=hello,world
ngx.say(...)                依次输出参数,带换行
ngx.print(...)                格式化输出,不带换行
ngx.var.name                取nginx里的name变量,如ngx.var.remote_addr
locale res = ngx.location.capture()    请求内部location
ngx.location.capture_multi()        同时请求多个内部location
res.status                取上面请求返回的状态
    取值为常量:
    ngx.HTTP_OK = 200
    ngx.HTTP_FORBIDDEN
    ngx.HTTP_INTERNAL_SERVER_ERROR
    ... ...
res.body                去上面请求反回的页面内容
ngx.exec(uri, args?)            内部重定向
ngx.exit(ngx.HTTP_FORBIDDEN)        退出执行并返回状态常量值
ngx.redirect(uri, status?)        301/302重定向
    rewrite ^ /foo? redirect;  # nginx config
    等价于
    return ngx.redirect('/foo');  -- Lua code

    rewrite ^ /foo? permanent;  # nginx config
    等价于
    return ngx.redirect('/foo', ngx.HTTP_MOVED_PERMANENTLY)  -- Lua code
ngx.send_headers()            明确地发送response headers
ngx.headers_sent            用此变量标识response headers是否已发送
ngx.log(log_level, ...)            记录参数到error.log
ngx.flush()                把response输出到客户端
ngx.eof()                明确指定response输出流结束
ngx.sleep(seconds)            非阻塞sleep指定秒(应该是指协程短期不调度)
ngx.escape_uri(str)            url转义
ngx.unescape_uri(str)            url转义还原
ngx.encode_args()            把lua table编码为查询参数字符串
ngx.decode_args(str,max_args?)        把查询字符串转为lua table
ngx.encode_base64(str)            base64编码
ngx.decode_base64(str)            base64解码
ngx.crc32_short(str)            crc32 小于30~60字节更高效
ngx.crc32_long(str)            crc32 大于30~60字节更高效
ngx.hmac_sha1(secret_key, str)        HMAC-SHA1
ngx.md5(str)                md5
ngx.md5_bin(str)            bin 格式 md5
ngx.sha1_bin(str)            bin 格式 sha1
ngx.quote_sql_str(raw_val)        Returns a quoted SQL string literal according to the MySQL quoting rules.    ngx.today()                ngx缓存当前本地日期
ngx.time()                从nginx当前缓存时间戳经过的秒数
ngx.now()                同上,但返回浮点数,单位也是秒
ngx.update_time()            强制更新nginx当前时间缓存。系统调用勿滥用
ngx.localtime()                返回nginx当前时间缓存,本地时间,格式yyyy-mm-dd hh:mm:ss
ngx.utctime()                同上,但为utc时间
ngx.cookie_time(sec)            返回格式化后的字符串,可以用作cookie过期时间
ngx.http_time(sec)            返回格式化后的字符串,可以用作http头时间,如Last-Modified
ngx.parse_http_time(str)        上面的反操作
ngx.is_subrequest            当前req如果是nginx subrequest,则为true
captures, err = ngx.re.match(subject, regex, options?, ctx?, res_table?)         正则匹配
from, to, err = ngx.re.find(subject, regex, options?, ctx?, nth?)                同上,但是返回匹配的起始和结束位置
iterator, err = ngx.re.gmatch(subject, regex, options?)                            同上上,但返回的是lua iterator,可遍历所有匹配
newstr, n, err = ngx.re.sub(subject, regex, replace, options?)                    正则匹配并替换
newstr, n, err = ngx.re.gsub(subject, regex, replace, options?)                    同上,但是是全局替换
dict = ngx.shared.DICT                访问lua_shared_dict定义的共享内存lua字典
dict = ngx.shared[name_var]
    get                    例如value, flags = ngx.shared.DICT:get(key)
    get_stale            即时过期也返回数据
    set                    例如success, err, forcible = ngx.shared.DICT:set(key, value, exptime?, flags?)
    safe_set            内存不够也不覆盖未过期数据
    add                    同set,但只当key不存在时存
    safe_add            同add,但即使内存不够也不覆盖未过期数据
    replace                同set,但只当key存在时存储
    delete                无条件移除
    incr                按指定步长增加数字值
    flush_all            清洗所有数据,不释放空间,只标记过期
    flush_expired        清晰过期数据
    get_keys            获取指定数量的key列表
udpsock = ngx.socket.udp()
    setpeername                ok, err = udpsock:setpeername(host, port),ok, err = udpsock:setpeername("unix:/path/to/unix-domain.socket")
    send
    receive
    close
    settimeout
tcpsock = ngx.socket.tcp()
    connect
    sslhandshake
    send
    receive
    close
    settimeout
    setoption
    receiveuntil
    setkeepalive
    getreusedtimes
tcpsock, err = ngx.socket.connect(host, port)
tcpsock, err = ngx.socket.connect("unix:/path/to/unix-domain.socket")
str = ngx.get_phase()     //nginx当前处于哪个处理阶段
    init
    for the context of init_by_lua or init_by_lua_file.
    set
    for the context of set_by_lua or set_by_lua_file.
    rewrite
    for the context of rewrite_by_lua or rewrite_by_lua_file.
    access
    for the context of access_by_lua or access_by_lua_file.
    content
    for the context of content_by_lua or content_by_lua_file.
    header_filter
    for the context of header_filter_by_lua or header_filter_by_lua_file.
    body_filter
    for the context of body_filter_by_lua or body_filter_by_lua_file.
    log
    for the context of log_by_lua or log_by_lua_file.
    timer
    for the context of user callback functions for ngx.timer.*.
co = ngx.thread.spawn(func, arg1, arg2, ...)
ok, res1, res2, ... = ngx.thread.wait(thread1, thread2, ...)
ok, err = ngx.thread.kill(thread)
ok, err = ngx.on_abort(callback)                                    //注册回调函数,当客户端断开链接时执行
ok, err = ngx.timer.at(delay, callback, user_arg1, user_arg2, ...)  //定时执行(一次)
debug = ngx.config.debug                                            //标识nginx是不是debug build
prefix = ngx.config.prefix()                                        //nginx "prefix"路径
ver = ngx.config.nginx_version
str = ngx.config.nginx_configure()                                    //返回编译时./configure的参数
ver = ngx.config.ngx_lua_version
exiting = ngx.worker.exiting()                                        //boolean值表示是否已经开始退出
pid = ngx.worker.pid()                                                //当前nginx worker进程id,比ngx.var.pid更高效,可用面更广
res = ndk.set_var.DIRECTIVE_NAME                                    //通过ndk的set_var调用ngx的其他C模块,安全相关
    set_quote_sql_str
    set_quote_pgsql_str
    set_quote_json_str
    set_unescape_uri
    set_escape_uri
    set_encode_base32
    set_decode_base32
    set_encode_base64
    set_decode_base64
    set_encode_hex
    set_decode_hex
    set_sha1
    set_md5

    set_encrypt_session
    set_decrypt_session
co = coroutine.create(f)                    //创建协程
ok, ... = coroutine.resume(co, ...)            //唤醒(切换到)协程
... = coroutine.yield(...)                    //切换回调用协程
co = coroutine.wrap(f)                        //创建新协程,返回一个方法,每次调用唤醒协程,函数参数作为resume的额外参数
co = coroutine.running()                    //返回正在运行的coroutine,如果是main thread则返回nil
status = coroutine.status(co)                //返回协程co的状态
    running
    suspended            //yield
    normal                //激活但没运行,也就是它唤醒了另一个协程
    dead                //完成了它的函数体执行或已遇到错误停止

redis2_query
    set key val                赋值  成功显示 +OK
    get key                    取值  成功返回 $长度 值  如"$3 abc"
    lpush key val [val ...]    将一个或多个值 value 插入到列表 key 的表头                           LPUSH key value [value ...]
    lrange key start stop      返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。   LRANGE key start stop
redis2_pass                        指定redis服务器
redis2_literal_raw_query         不能使用ngx变量,也就是你可以自由使用$字符
redis2_raw_query                 raw查询
redis2_raw_queries                可以处理多个raw_query,如redis2_raw_queries 3 "flushall\r\nget key1\r\nget key2\r\n";
redis2_connect_timeout            设置连接redis服务器超时,单位秒
redis2_send_timeout                发送tcp请求到redis服务器的超时时间
redis2_read_timeout                从redis服务器读取tcp响应超时时间
redis2_buffer_size                redis回复缓存,但不需要像可能的最大redis回复那么大,默认值同页面大小,可能是4k或8k
redis2_next_upstream            指定哪些失败条件可以导致请求转发到另一个upstream服务器