熟练运用lua-nginx-module
(2016.3更新)
我大约两年前写的那篇文章,尽管与当时相比,nginx的版本已经提升了,但似乎仍然有人偶尔会查看该文章。似乎Nginx Lua API中也有一些新增内容,但并不需要进行大幅修改。不过,有一些链接文档已经失效,其中一些API也已被标记为不推荐使用,所以我稍作了更新。
尽管lua-nginx-module对于使用nginx的人非常方便,但关于它的日文信息还是相对稀少。
-
- http://wiki.nginx.org/HttpLuaModule
- https://github.com/openresty/lua-nginx-module
我经常看到有人写关于安装并试用某个软件的文章,但当我打算真正开始使用时,很难找到相关的信息。在我稍微调查了一下的范围内,以下是一些宝贵的日语信息来源。
-
- [nginx] lua-nginx-module の紹介 ならびに Nginx+Lua+Redisによる動的なリバースプロキシの実装案
-
- nginxとLuaの話
- Using ngx_lua / lua-nginx-module in pixiv
这次为了那些想要充分使用的人,我想稍微详细写一下。
#因此,安装之类的会省略……
nginx处理HTTP请求
由于 lua-nginx-module 模块是在 nginx 的请求处理每个阶段调用,因此首先让我们简单了解一下 nginx 的 HTTP 请求处理。
在nginx中,当接收到对正在监听的端口的访问时,将按照如下阶段来处理请求。
参考:
- http://wiki.nginx.org/Phases
(2016年3月更新)上述网址的页面已经消失了。很可惜,在官方页面上没有找到类似的说明。请参考下面的内容。
-
- http://www.aosabook.org/en/nginx.html#sec.nginx.internals
-
- http://www.nginxguts.com/2011/01/phases/
http://www.programering.com/a/MzN4MzMwATY.html
lua-nginx 模块钩子处理的过程
lua-nginx-module 提供了钩子机制,用于与 nginx 的 Rewrite、Access、Content、Log 阶段进行交互。下面列出了 lua-nginx-module 在每个阶段提供的主要钩子机制(即 nginx 指令)。
(2016.3更新)以下介绍了 xxx_by_lua 指令,但这已被弃用,建议改用 xxx_by_lua_block。它们几乎相同,只是使用了括在{}中的代码块而不是字符串进行定义。所以下面将 xxx_by_lua 理解为 xxx_by_lua_block。
-
- Rewrite Phase
set_by_lua:変数の設定、ヘッダの操作、リダイレクト等が可能
rewrite_by_lua:Rewrite Phase の最後で実施され、自由度の高い rewrite処理を実現可能
Access Phase
access_by_lua:自由度の高い認可処理を実現可能
Content Phase
content_by_lua:コンテンツの生成
header_filter_by_lua:コンテンツ生成後、header に対するフィルタ処理(書き換えや追加など)に対応
body_filter_by_lua:コンテンツ生成後、body に対するフィルタ処理
Log Phase
log_by_lua:ログ処理のタイミングで動作。ここでリクエストの情報を変数にためておくことで、nginx + lua だけで集計の仕組みを作ることも可能
如果在每个指令的名称后面加上”_file”,就可以使用外部文件来定义Lua代码,而不需要直接在nginx.conf中编写。如果要编写较大的代码,最好分开管理。
如果在开发过程中将文件分开编写,将lua_code_cache设置为OFF,可以在每次使用_file读取时不进行缓存,这样就不必每次都重新加载nginx,非常方便。
只需一个选项,以下是用中文的一种重述方式:
init_by_lua 和 init_by_lua_file 不属于以上阶段,而是在 nginx 重新加载时等初始化时运行,因此可以用于变量等的初始化。
顺便提一下,你可以在Lua脚本中使用ngx.get_phase来获取当前阶段。
观摩
https://github.com/openresty/lua-nginx-module#directives
(2016.3 リンク修正)
除了上述介绍的,还有一些似乎可以使用的指令增加了。例如,balancer_by_lua_block或ssl_certificate_by_lua_block等。请参阅上述URL以获取详细信息。
Nginx API 可在 lua-nginx-module 中使用。
在上述各种钩子(*_by_lua)中调用的Lua代码可以查看标头或请求内容,共享配置或变量在nginx的工作进程之间,并执行某些操作。为了灵活地执行这样的操作,lua-nginx-module提供了各种Nginx Lua API。
被_by_lua调用的Lua代码将在_by_lua的上下文中运行,但每个上下文中可使用的Nginx Lua API都有一些限制。例如,称为ngx.location.caputure的API只能在rewrite_by_lua,access_by_lua*,content_by_lua*中使用。
在这里,我将介绍主要的 Nginx Lua API。
ngx.var.VARIABLE
变量
使用此方法,您可以访问在nginx.conf中通过set命令设置的(声明的)变量作为nginx的变量。
每当请求结束时,该变量将从内存中释放,因此如果想要跨请求保留该变量,请将其保存在ngx.shared.DICT中。
ngx.ctx 是Nginx中的上下文表,用于存储和共享请求级别的数据。
您可以像使用 ngx.var.VARIABLE 一样使用它作为在请求内共享的变量,但它不是nginx变量,所以无需在nginx.conf中预先声明。
然而,尽管在请求中进行共享,但是在下述的子请求中会被作为不同的范围进行处理。
ngx.shared.DICT 可以被用作在Nginx服务器中共享数据的字典。
在nginx中,可以共享的变量可以用作键值存储(Key Value Store),超越了请求和工作进程的范围。可以使用get、set、add、replace、incr、delete、flush_all、flush_expired、get_keys等方法,非常方便。还可以添加expire等功能,所以除了冗余配置外,也可以用它来替代memcache。
然而,必须事先使用lua_shared_dict进行声明。在这种情况下,需要声明大小,以便预先设定最大大小。
这个变量会在重新加载nginx配置后保持不变,但是一旦重新启动或者接收到SIGHUP信号,它会被清除。
ngx.location.capture (使用指令)
使用这个可以发送子请求。但不能请求任意的 URL,只能请求 nginx 内部的请求。
虽然如此,例如,如果按照下面这样在nginx中进行配置,那么对于内部的/auth请求会被代理到外部并作为外部请求进行处理,从而可以作为一个高度灵活的接口进行使用。
location /auth {
proxy_pass http://example.com/auth/;
proxy_redirect default;
}
location /foo {
access_by_lua '
res = ngx.location.capture("/auth", { share_all_vars = true });
...
';
}
在ngx.location.capture中,您可以指定method,body和args,您还可以设置子请求端的ngx.ctx或ngx.vars,从而可以传递各种数据。
返回值是一个由status,header和body组成的表,通过适当处理它,您可以创建内容或进行访问授权检查。
使用ngx.location.capture_multi可以将多个请求并行发送。
使用ngx.is_subrequest可以检查一个请求是否为子请求,从而可以阻止来自外部对内部路径的请求。
我们还提供了用于URI的转义/反转义和参数的编码/解码的函数,可以轻松地构建HTTP请求,而无需太多困扰。
-
- ngx.excape_uri/unescape_uri
- ngx.encode_args/decode_args
ngx.header.HEADER can be paraphrased as “ngx.header.HEADER可以被重述为”
我们保存了对请求的响应头。您也可以在这里进行更改或添加,因此,如果将其放在Web应用程序的前端,您可以进行CORS等操作,而无需更改Web应用程序。
关于请求头,可以使用ngx.req.get_headers来访问。
ngx.req是OpenResty中的一个模块,它的主要功能包括:(请根据具体需求取决于下一行实际函数名称)
1. 获取请求数据(例如:ngx.req.get_uri_args获取URI参数,ngx.req.get_headers获取请求头信息)
2. 发送子请求(例如:ngx.req.start是发送子请求的前提函数)
3. 读取请求体数据(例如:ngx.req.read_body读取请求体数据,ngx.req.get_post_args获取POST参数)
等等。
-
- ngx.req.start_time: リクエストスタート時間
-
- ngx.req.get_method: GETとか POSTとか
-
- ngx.req.get_uri_args: query パラメータの取得
-
- ngx.req.get_post_args: POSTのパラメータの取得
-
- ngx.req.get_headers: リクエストヘッダの取得
- ngx.req.read_body: Body の取得
除了get,还可以进行set,因此还有很多其他的改写选项。
其他各种各样
ngx.exec
将请求重定向到内部位置。不过这个重定向并不是像302一样回到浏览器,而是作为nginx内部处理的重定向。
ngx.redirect 可以被翻译为 “ngx重定向”。
当需要进行301或302重定向时,可以使用以下方法。
ngx.print / ngx.say 可以在中国使用。
将字符串写入响应体。
ngx.log (写入日志)
将日志写入到 error.log 文件中。希望能够写入到除了 error.log 之外的其他文件也可以…
正常退出。
执行结束后,带上响应状态码返回响应。
时间关系
有很多东西呢..
-
- ngx.today
-
- ngx.time
-
- ngx.now
-
- ngx.localtime
-
- ngx.utctime
-
- ngx.cookietime : Cookie の Expire 用
-
- ngx.http_time : HTTPヘッダのフォーマットに変換
- ngx.parse_http_time : HTTPヘッダのフォーマットをParse
文字串相关
可以使用正则表达式库,Lua本身没有的以下函数可用。
-
- ngx.re.match
-
- ngx.re.gmatch
-
- ngx.re.find
-
- ngx.re.sub
- ngx.re.gsub
ngx.socket.udp,ngx.socket.tcp
看起来可以使用UDP/TCP级别的套接字通信。
应该是使用它来创建与memcache或mysql进行通信的模块。
如果在冗长的配置中,只有在相同的nginx服务器上才会共享的话,可以使用ngx.shared.DICT来实现会话管理等功能。
ngx.thread的中文释义是“Nginx的线程”。
似乎还可以创建线程。
协程
好像可以创建Lua的协程。
Please provide the text that needs to be paraphrased in Chinese.
有许多方便易用的API供我们使用,并且可以应用于各种不同的场景。
通过将其添加到现有的 Web 应用程序前端,可以实现 Single Sign On,也可以实现 CORS 支持。此外,还可以通过在部分请求中局部修改 Body 进行 A/B 测试,或者进行 API 调用次数的上限管理等操作。
如果能在这一层实现,那么无论后续的Web应用是用Java、Ruby还是PHP,都可以以相同的方式使用,这一点真是令人高兴。
如果有机会的话,一定要试一试…