nginx-lua-module

Description 簡述

Almost all the Nginx modules can be used with this ngx_lua module by means of ngx.location.capture or ngx.location.capture_multi but it is recommended to use those lua-resty-* libraries instead of creating subrequests to access the Nginx upstream modules because the former is usually much more flexible and memory-efficient.
幾乎所有的nginx模塊都能通過ngx.location.capture或ngx.location.catpure_multi使用但是推薦使用lua-resty-*庫而不是創(chuàng)建一個子請求去訪問nginx上游服務,因為這種形式更加靈活和內存高效。

The Lua interpreter or LuaJIT instance is shared across all the requests in a single nginx worker process but request contexts are segregated using lightweight Lua coroutines
在單個nginx work進程中,所有的request共享Lua或者LuaJIT實例。但是請求上下文通過輕量級的Lua協(xié)程隔離

Loaded Lua modules persist in the nginx worker process level resulting in a small memory footprint in Lua even when under heavy loads.
載入的lua模塊在nginx進程級別持續(xù)存在,即使在負載較重的情況下,lua也占用很少內存。

This module is plugged into NGINX's "http" subsystem so it can only speaks downstream communication protocols in the HTTP family (HTTP 0.9/1.0/1.1/2.0, WebSockets, and etc). If you want to do generic TCP communications with the downstream clients, then you should use the ngx_stream_lua module instead which has a compatible Lua API
這些模塊在nginx http子系統(tǒng)中以插件形式提供。所以你可以使用HTTP協(xié)議族調用下游服務。如果你想使用通用TCP協(xié)議和下游溝通,需要使用ngx-stream-lua模塊中提供的兼容Lua API。

Typical Uses 典型用途

Just to name a few:

Mashup'ing and processing outputs of various nginx upstream outputs (proxy, drizzle, postgres, redis, memcached, and etc) in Lua,
在Lua中混合和處理各種nginx上游輸出

doing arbitrarily complex access control and security checks in Lua before requests actually reach the upstream backends
在請求實際到達上游后端之前,在Lua中進行任意復雜的訪問控制和安全檢查

manipulating response headers in an arbitrary way (by Lua)
以任意方式操縱響應頭

fetching backend information from external storage backends (like redis, memcached, mysql, postgresql) and use that information to choose which upstream backend to access on-the-fly,
從外部后端存儲獲取信息并使用該信息選擇哪個后端上游服務

coding up arbitrarily complex web applications in a content handler using synchronous but still non-blocking access to the database backends and other storage,
同步非阻塞的訪問數(shù)據(jù)庫后端或其他存儲,在內容處理中編碼實現(xiàn)任意復雜的應用。

doing very complex URL dispatch in Lua at rewrite phase,
using Lua to implement advanced caching mechanism for Nginx's subrequests and arbitrary locations.
在Lua rewrite階段實現(xiàn)非常復雜的URL 分發(fā),為Nginx子請求使用Lua實現(xiàn)高級緩存

The possibilities are unlimited as the module allows bringing together various elements within Nginx as well as exposing the power of the Lua language to the user. The module provides the full flexibility of scripting while offering performance levels comparable with native C language programs both in terms of CPU time as well as memory footprint. This is particularly the case when LuaJIT 2.x is enabled.
可能性是無限的,因為該模塊允許將nginx的各種模塊集合在一起并給將Lua語言的強大暴露給用戶。

Other scripting language implementations typically struggle to match this performance level
其它腳本語言通常難以匹配這種性能級別。

The Lua state (Lua VM instance) is shared across all the requests handled by a single nginx worker process to minimize memory use.
Lua 虛擬機實例在每個工作進程的所有請求之間共享以減少內存占用。

INSTALLATION 安裝。。。

Lua/LuaJIT bytecode support Lua字節(jié)碼支持

As from the v0.5.0rc32 release, all *_by_lua_file configure directives (such as content_by_lua_file) support loading Lua 5.1 and LuaJIT 2.0/2.1 raw bytecode files directly.
v0.5.0rc32 版本開始, 所有 *_by_lua_file配置支持直接載入Lua或者LuaJIT字節(jié)碼。

Please note that the bytecode format used by LuaJIT 2.0/2.1 is not compatible with that used by the standard Lua 5.1 interpreter. So if you are using LuaJIT 2.0/2.1 with ngx_lua, LuaJIT compatible bytecode files must be generated as shown
請注意,LuaJIT 2.0 / 2.1使用的字節(jié)碼格式與標準Lua 5.1解釋器使用的格式不兼容。因此,如果您使用LuaJIT 2.0 / 2.1和ngx_lua,必須生成LuaJIT兼容的字節(jié)碼文件。示例如下:

/path/to/luajit/bin/luajit -b /path/to/input_file.lua /path/to/output_file.ljbc

Statically Linking Pure Lua Modules 靜態(tài)鏈接純Lua模塊

When LuaJIT 2.x is used, it is possible to statically link the bytecode of pure Lua modules into the Nginx executable.
當使用LuaJIT2.x時,可以在nginx執(zhí)行文件中直接靜態(tài)連接純Lua字節(jié)碼模塊。

Basically you use the luajit executable to compile .lua Lua module files to .o object files containing the exported bytecode data, and then link the .o files directly in your Nginx build.
基本上,您使用luajit可執(zhí)行文件將.lua Lua模塊文件編譯為包含導出的字節(jié)碼數(shù)據(jù)的.o對象文件,然后直接在Nginx構建中鏈接.o文件。

Below is a trivial example to demonstrate this. Consider that we have the following .lua file named foo.lua:
下面是一個簡單的例子來證明這一點。考慮到我們有以下名為foo.lua的.lua文件

-- foo.lua
 local _M = {}

 function _M.go()
     print("Hello from foo")
 end

 return _M

And then we compile this .lua file to foo.o file
當我們編譯該.lua文件為foo.o文件。
/path/to/luajit/bin/luajit -bg foo.lua foo.o

What matters here is the name of the .lua file, which determines how you use this module later on the Lua land. The file name foo.o does not matter at all except the .o file extension (which tells luajit what output format is used). If you want to strip the Lua debug information from the resulting bytecode, you can just specify the -b option above instead of -bg.
這里重要的是.lua文件的名稱,它決定了你以后如何在Lua上使用這個模塊。除了.o文件擴展名(告訴luajit使用了什么輸出格式)之外,文件名foo.o完全沒有關系。如果要從生成的字節(jié)碼中去除Lua調試信
息,可以指定上面的-b選項而不是-bg。

Then when building Nginx or OpenResty, pass the --with-ld-opt="foo.o" option to the ./configure script:
當編譯nginx的時候,通過在configure中添加 --with-ld-opt=''foo.o''選項來編譯:
./configure --with-ld-opt="/path/to/foo.o" ...

Finally, you can just do the following in any Lua code run by ngx_lua:
最終,你可以在任意Lua代碼中以下面的形式使用:

 local foo = require "foo"
 foo.go()

And this piece of code no longer depends on the external foo.lua file any more because it has already been compiled into the nginx executable.
這段代碼不再依賴外部foo.lua文件,因為他已經(jīng)被編譯僅nginx可執(zhí)行文件中了。

If you want to use dot in the Lua module name when calling require, as in
如果要在調用require時在Lua模塊名稱中使用dot,請參閱
local foo = reuire "resty.foo"
then you need to rename the foo.lua file to resty_foo.lua before compiling it down to a .o file with the luajit command-line utility.
然后你需要將foo.lua文件重命名為resty_foo.lua,然后使用luajit命令行實用程序將其編譯為.o文件

It is important to use exactly the same version of LuaJIT when compiling .lua files to .o files as building nginx + ngx_lua. This is because the LuaJIT bytecode format may be incompatible between different LuaJIT versions. When the bytecode format is incompatible, you will see a Lua runtime error saying that the Lua module is not found.
在將.lua文件編譯為.o文件時,使用完全相同版本的LuaJIT非常重要,因為它構建了nginx + ngx_lua。這是因為LuaJIT字節(jié)碼格式在不同的LuaJIT版本之間可能不兼容。當字節(jié)碼格式不兼容時,您將看到Lua運行時錯誤,指出未找到Lua模塊。

When you have multiple .lua files to compile and link, then just specify their .o files at the same time in the value of the --with-ld-opt option. For instance,
如果要編譯和鏈接多個.lua文件,則只需在--with-ld-opt選項的值中同時指定其.o文件。例如,
./configure --with-ld-opt="/path/to/foo.o /path/to/bar.o" ...

If you have just too many .o files, then it might not be feasible to name them all in a single command. In this case, you can build a static library (or archive) for your .o files, as in
如果你有太多.o文件,那么在一個命令中將它們全部命名可能是不可行的。在這種情況下,您可以為.o文件構建靜態(tài)庫(或存檔),如
ar rcus libmyluafiles.a *.o
then you can link the myluafiles archive as a whole to your nginx executable:
./configure \ --with-ld-opt="-L/path/to/lib -Wl,--whole-archive -lmyluafiles -Wl,--no-whole-archive"
然后你可以將myluafiles存檔作為一個整體鏈接到你的nginx可執(zhí)行文件。

where /path/to/lib is the path of the directory containing the libmyluafiles.a file. It should be noted that the linker option --whole-archive is required here because otherwise our archive will be skipped because no symbols in our archive are mentioned in the main parts of the nginx executable.
其中/ path / to / lib是包含libmyluafiles.a文件的目錄的路徑。應該注意的是,這里需要鏈接器選項--whole-archive,否則我們的存檔將被跳過,因為在nginx可執(zhí)行文件的主要部分中沒有提到我們的存檔中的符號。

Data Sharing within an Nginx Worker nginx進程間的數(shù)據(jù)共享

To globally share data among all the requests handled by the same nginx worker process, encapsulate the shared data into a Lua module, use the Lua require builtin to import the module, and then manipulate the shared data in Lua. This works because required Lua modules are loaded only once and all coroutines will share the same copy of the module (both its code and data). Note however that Lua global variables (note, not module-level variables) WILL NOT persist between requests because of the one-coroutine-per-request isolation design.
要在同一個nginx工作進程處理的所有請求之間全局共享數(shù)據(jù),將共享數(shù)據(jù)封裝到Lua模塊中,使用Lua require builtin導入模塊,然后在Lua中操作共享數(shù)據(jù)。這是有效的,因為所需的Lua模塊只加載一次,并且所有協(xié)同程序將共享模塊的相同副本(包括其代碼和數(shù)據(jù))。但請注意,由于每個請求的單協(xié)程隔離設計,Lua全局變量(注意,而不是模塊級變量)不會在請求之間保持不變。

Here is a complete small example:
這里是一個完整的小例子。

 -- mydata.lua
 local _M = {}

 local data = {
     dog = 3,
     cat = 4,
     pig = 5,
 }

 function _M.get_age(name)
     return data[name]
 end

 return _M

The mydata module in this example will only be loaded and run on the first request to the location /lua, and all subsequent requests to the same nginx worker process will use the reloaded instance of the module as well as the same copy of the data in it, until a HUP signal is sent to the Nginx master process to force a reload. This data sharing technique is essential for high performance Lua applications based on this module.
此示例中的mydata模塊僅在第一次請求時加載并運行到位置/ lua,對同一nginx工作進程的所有后續(xù)請求將重新加載使用模塊的實例以及其中的數(shù)據(jù),直到'HUP`信號被發(fā)送到Nginx主進程以強制重新加載。這種數(shù)據(jù)共享技術對于基于該模塊的高性能Lua應用程序至關重要

Note that this data sharing is on a per-worker basis and not on a per-server basis. That is, when there are multiple nginx worker processes under an Nginx master, data sharing cannot cross the process boundary between these workers.
請注意,此數(shù)據(jù)共享基于每個worker而不是基于每個服務器。也就是說,當Nginx主機下有多個nginx工作進程時,數(shù)據(jù)共享不能越過這些工作進程之間的進程邊界。

It is usually recommended to share read-only data this way. You can also share changeable data among all the concurrent requests of each nginx worker process as long as there is no nonblocking I/O operations in the middle of your calculations. As long as you do not give the control back to the nginx event loop and ngx_lua's light thread scheduler (even implicitly), there can never be any race conditions in between. For this reason, always be very careful when you want to share changeable data on the worker level. Buggy optimizations can easily lead to hard-to-debug race conditions under load.
通常建議以這種方式共享只讀數(shù)據(jù)。只要在計算過程中存在nonblocking I / O操作,您還可以在每個nginx工作進程的所有并發(fā)請求之間共享可更改數(shù)據(jù)。只要你不將控制權交還給nginx事件循環(huán)和ngx_lua的輕線程調度程序(甚至隱式),就不會有任何競爭條件。因此,當您想要在worker級別共享可更改數(shù)據(jù)時,請務必非常小心。bug優(yōu)化很容易導致在負載下難以調試的競爭條件

If server-wide data sharing is required, then use one or more of the following approaches:
如果需要服務器范圍的數(shù)據(jù)共享,請使用以下一種或多種方法

  1. Use the [ngx.shared.DICT API provided by this module.
    使用ngx.shared.DICT API
  2. Use only a single nginx worker and a single server (this is however not recommended when there is a multi core CPU or multiple CPUs in a single machine).
    使用單進程nginx和一個單服務器
  3. Use data storage mechanisms such as memcached, redis, MySQL or PostgreSQLassociated with this module comes with a set of companion Nginx modules and Lua libraries that provide interfaces with these data storage mechanisms.
    使用 數(shù)據(jù)存儲服務。

Known Issues 已知問題

TCP socket connect operation issues
Lua Coroutine Yielding/Resuming
Lua Variable Scope Lua變量作用域

Care must be taken when importing modules and this form should be used:
當導入模塊時務必使用下面的形式

 local xxx = require('xxx')

而不是老的形式:

 require('xxx')

Here is the reason: by design, the global environment has exactly the same lifetime as the Nginx request handler associated with it. Each request handler has its own set of Lua global variables and that is the idea of request isolation. The Lua module is actually loaded by the first Nginx request handler and is cached by the require() built-in in the package.loaded table for later reference, and the module() builtin used by some Lua modules has the side effect of setting a global variable to the loaded module table. But this global variable will be cleared at the end of the request handler, and every subsequent request handler all has its own (clean) global environment. So one will get Lua exception for accessing the nil value.
原因如下:通過設計,全局環(huán)境與與之關聯(lián)的Nginx請求處理程序具有完全相同的生命周期。每個請求處理程序都有自己的一組Lua全局變量,這就是請求隔離的想法。 Lua模塊實際上由第一個Nginx請求處理程序加載,并由package()內置在package.loaded表中進行緩存以供以后引用,以及module()內置由一些Lua模塊使用具有將全局變量設置為已加載模塊表的副作用。但是這個全局變量將在請求處理程序結束時被清除,并且每個后續(xù)請求處理程序都有自己的(干凈的)全局環(huán)境。因此,訪問nil值會得到Lua異常

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容