OpenResty相关nginx以及lua函数

在nginx.conf中,可以执行lua代码。lua模块可以看作是将一个class单独存成文件。
所以学习OpnenResty要学习两部分语法,一方面是Nginx内置绑定变量和函数,另一方面是Lua语法和Lua针对Nginx实现的类库。
所以以下就从这两方面进行学习和总结。

Nginx语法

关于Nginx的介绍很多,觉得这个挺好:agentzh 的 Nginx 教程,但是我还没看完。
先介绍Nginx内置绑定变量。

名称说明
$arg_name请求中的name参数
$args请求中的参数
$binary_remote_addr远程地址的二进制表示
$body_bytes_sent已发送的消息体字节数
$content_lengthHTTP请求信息里的”Content-Length”
$content_type请求信息里的”Content-Type”
$document_root针对当前请求的根路径设置值
$document_uri与$uri相同; 比如 /test2/test.php
$host请求信息中的”Host”,如果请求中没有Host行,则等于设置的服务器名
$hostname机器名使用 gethostname系统调用的值
$http_cookiecookie 信息
$http_referer引用地址
$http_user_agent客户端代理信息
$http_via最后一个访问服务器的Ip地址。
$http_x_forwarded_for相当于网络访问路径
$is_args如果请求行带有参数,返回“?”,否则返回空字符串
$limit_rate对连接速率的限制
$nginx_version当前运行的nginx版本号
$pidworker进程的PID
$query_string与$args相同
$realpath_root按root指令或alias指令算出的当前请求的绝对路径。其中的符号链
$remote_addr客户端IP地址
$remote_port客户端端口号
$remote_user客户端用户名,认证用
$request用户请求
$request_body这个变量(0.7.58+)包含请求的主要信息。在使用proxy_pass或fastcgi_pass指令的location中比较有意义
$request_body_file客户端请求主体信息的临时文件名
$request_completion如果请求成功,设为”OK”;如果请求未完成或者不是一系列请求中最后一部分则设为空
$request_filename当前请求的文件路径名,比如/opt/nginx/www/test.php
$request_method请求的方法,比如”GET”、”POST”等
$request_uri请求的URI,带参数
$scheme所用的协议,比如http或者是https
$server_addr服务器地址,如果没有用listen指明服务器地址,使用这个变量将发起一次系统调用以取得地址(造成资源浪费)
$server_name请求到达的服务器名
$server_port请求到达的服务器端口号
$server_protocol请求的协议版本,”HTTP/1.0”或”HTTP/1.1”
$uri请求的URI,可能和最初的值有不同,比如经过重定向之类的

如何使用内置的绑定变量,通过一个例子来看

server {
    listen       8080;
    server_name  _;

    location /test {
        default_type "text/html";
        echo $host;
    }
}

当访问

http://localhost:8080/test  #网页会输出"localhost"

其他的绑定变量的使用也是一样的

Lua代码

下面介绍如何使用Lua代码。有两种方式。第一种,直接在server里的location下,嵌入到

    content_by_lua_block{...}

比如:

location /test{
    default_type "text/html";
    content_by_lua_block {
        local res = ngx.location.capture('/print_param',
        {
            method = ngx.HTTP_POST,
            args = {a = 1, b = '2&'},
            body = 'c=3&d=4%26'
        }
        )
        ngx.say(res.body)
    }
}

在content_by_lua_block下的就是lua语法了。
第二种方式为文件引入。这种方式更符合模块化的思想,引入方式只需要将

content_by_lua_block{}

替换为

content_by_lua_file /usr/openresty/example/test.lua

然后去看test.lua都有什么

test.lua
-------
ngx.location.capture('/print_param',
        {
            method = ngx.HTTP_POST,
            args = {a = 1, b = '2&'},
            body = 'c=3&d=4%26'
        }
        )
        ngx.say(res.body)

可以看到就是第一种方式里的lua代码部分,这说明文件方式引用,其实就是一种插入代码的形式。这跟lua的模块思想是一样的。可以原封不动的放过来,拆出来一个文件更多的是模块化思想。

我觉得Lua的语法可以去学习Lua简明教程,这里只需要介绍与Nginx相关的语法。
ngx.say和ngx.print均是向屏幕输出内容,知识ngx.say会在内容后多加一个回车符,类似与print与println的区别。
nginx中的set指令,lua中有对应的set_by_lua,可以接收lua代码的返回值。

set %name "lisi";
set_by_lua_file $name /usr/openresty/example/lua/test1.lua

Nginx API被封装ngx和ndk两个package中。
比方ngx.var.NGX_VAR_NAME能够訪问Nginx变量。这里着重介绍一下ngx.location.capture和ngx.location.capture_multi。

ngx.location.capture
语法:res= ngx.location.capture(uri, options?) 用于发出一个同步的,非堵塞的Nginxsubrequest(子请求)。

能够通过Nginx subrequest向其他location发出非堵塞的内部请求。这些location能够是配置用于读取目录的,也能够是其他的C模块,比方ngx_proxy, ngx_fastcgi, ngx_memc, ngx_postgres, ngx_drizzle甚至是ngx_lua自己。 Subrequest仅仅是模拟Http接口,并没有额外的Http或者Tcp传输开销,它在C层次上执行,很高效。Subrequest不同于Http 301/302重定向,以及内部重定向(通过ngx.redirection)。

#配置:
location =/other {
    ehco 'Hello, world!';
}

# Lua非堵塞IO
location =/lua {
    content_by_lua '
        local res = ngx.location.capture("/other")
        if res.status == 200 then
            ngx.print(res.body)
        end
    ';
}

输出:

$ curl  'http://localhost/lua'
$ Hello, world!

实际上,location能够被外部的Http请求调用,也能够被内部的子请求调用。每一个location相当于一个函数,而发送子请求就类似于函数调用。并且这样的调用是非堵塞的,这就构造了一个很强大的变成模型,后面我们会看到怎样通过location和后端的memcached、redis进行非堵塞通信。
ngx.location.capture_multi

语法:res1,res2, … = ngx.location.capture_multi({ {uri, options?}, {uri, options?}, …}) 与ngx.location.capture功能一样,能够并行的、非堵塞的发出多个子请求。这种方法在全部子请求处理完毕后返回。而且整个方法的执行时间取决于执行时间最长的子请求,并非全部子请求的执行时间之和。

配置

# 同一时候发送多个子请求(subrequest)
location =/moon {
    ehco 'moon';
}
location =/earth {
    ehco 'earth';
}

location =/lua {
    content_by_lua '
        local res1,res2 = ngx.location.capture_multi({ {"/moon"}, {"earth"} })
        if res1.status == 200 then
            ngx.print(res1.body)
        end
        ngx.print(",")
        if res2.status == 200 then
            ngx.print(res2.body)
        end
    ';
}

输出

$ curl  'http://localhost/lua'
$ moon,earth

在Lua代码中的网络IO操作仅仅能通过Nginx Lua API完毕。假设通过标准Lua API会导致Nginx的事件循环被堵塞,这样性能会急剧下降。在进行数据量相当小的磁盘IO时能够採用标准Lua io库,可是当读写大文件时这样是不行的,由于会堵塞整个NginxWorker进程。为了获得更大的性能。强烈建议将全部的网络IO和磁盘IO托付给Nginx子请求完毕(通过ngx.location.capture)。以下通过访问/html/index.html这个文件。来測试将磁盘IO托付给Nginx和通过Lua io直接访问的效率。通过ngx.location.capture托付磁盘IO:

配置

location / {
    internal;
    root html;
}

location /capture {
    content_by_lua '
        res = ngx.location.capture("/")
        echo res.body
    ';
}

通过标准lua io访问磁盘文件:

配置:

location /luaio{
    content_by_lua '
        local io = require("io")
        local chunk_SIZE = 4096
        local f = assert(io.open("html/index.html","r"))
        while true do
            local chunk = f:read(chunk)
            if not chunk then
                break
            end
            ngx.print(chunk)
            ngx.flush(true)
        end
        f:close()
    ';
}

所以,在Lua中进行各种IO时。都要通过ngx.location.capture发送子请求托付给Nginx事件模型,这样能够保证IO是非堵塞的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值