如何使用lua-nginx-module模块实现高效动态内容生成?

2026-04-01 18:471阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1979个文字,预计阅读时间需要8分钟。

如何使用lua-nginx-module模块实现高效动态内容生成?

`ngx.location.capture` 用法:plaintextlocal res=ngx.location.capture(uri, options)发起一个同步非阻塞的nginx子请求,uri为internal的。返回的res是atable,索引依次是status(值为number类型)、body(值为string类型)。

ngx.location.capture

用法:

local res = ngx.location.capture(uri, options)

发起一个同步非阻塞的nginx子请求,uri是internal的。

返回的res是个table,索引依次是status(值是number类型)、body(值是string类型)、header(值是table类型)、truncated(值是boolean类型)

context:rewrite_by_lua*, access_by_lua*, content_by_lua*

Issues a synchronous but still non-blockingNginx Subrequestusinguri.

Nginx‘s subrequests provide a powerful way to make non-blocking internal requests to other locations configured with disk file directory oranyother nginx C modules likengx_proxy,ngx_fastcgi,ngx_memc,ngx_postgres,ngx_drizzle, and even ngx_lua itself and etc etc etc.

Also note that subrequests just mimic the HTTP interface but there isnoextra HTTP/TCP trafficnorIPC involved. Everything works internally, efficiently, on the C level.

Subrequests are completely different from HTTP 301/302 redirection (viangx.redirect) and internal redirection (viangx.exec).

You should always read the request body (by either callingngx.req.read_bodyor configuringlua_need_request_bodyon) before initiating a subrequest.

This API function (as well asngx.location.capture_multi) always buffers the whole response body of the subrequest in memory. Thus, you should usecosocketsand streaming processing instead if you have to handle large subrequest responses.

Here is a basic example:

res = ngx.location.capture(uri)

Returns a Lua table with 4 slots:res.status,res.header,res.body, andres.truncated.

res.statusholds the response status code for the subrequest response.

res.headerholds all the response headers of the subrequest and it is a normal Lua table. For multi-value response headers, the value is a Lua (array) table that holds all the values in the order that they appear. For instance, if the subrequest response headers contain the following lines:

Set-Cookie: a=3 Set-Cookie: foo=bar Set-Cookie: baz=blah

Thenres.header["Set-Cookie"]will be evaluated to the table value{"a=3", "foo=bar", "baz=blah"}.

res.bodyholds the subrequest‘s response body data, which might be truncated. You always need to check theres.truncatedboolean flag to see ifres.bodycontains truncated data. The data truncation here can only be caused by those unrecoverable errors in your subrequests like the cases that the remote end aborts the connection prematurely in the middle of the response body data stream or a read timeout happens when your subrequest is receiving the response body data from the remote.

URI query strings can be concatenated to URI itself, for instance,

res = ngx.location.capture(‘/foo/bar?a=3&b=4‘)

Named locations like@fooare not allowed due to a limitation in the nginx core. Use normal locations combined with theinternaldirective to prepare internal-only locations.

An optional option table can be fed as the second argument, which supports the options:

  • methodspecify the subrequest‘s request method, which only accepts constants likengx.HTTP_POST.
  • bodyspecify the subrequest‘s request body (string value only).
  • argsspecify the subrequest‘s URI query arguments (both string value and Lua tables are accepted)
  • ctxspecify a Lua table to be thengx.ctxtable for the subrequest. It can be the current request‘sngx.ctxtable, which effectively makes the parent and its subrequest to share exactly the same context table. This option was first introduced in thev0.3.1rc25release.
  • varstake a Lua table which holds the values to set the specified Nginx variables in the subrequest as this option‘s value. This option was first introduced in thev0.3.1rc31release.
  • copy_all_varsspecify whether to copy over all the Nginx variable values of the current request to the subrequest in question. modifications of the nginx variables in the subrequest will not affect the current (parent) request. This option was first introduced in thev0.3.1rc31release.
  • share_all_varsspecify whether to share all the Nginx variables of the subrequest with the current (parent) request. modifications of the Nginx variables in the subrequest will affect the current (parent) request. Enabling this option may lead to hard-to-debug issues due to bad side-effects and is considered bad and harmful. Only enable this option when you completely know what you are doing.
  • always_forward_bodywhen set to true, the current (parent) request‘s request body will always be forwarded to the subrequest being created if thebodyoption is not specified. The request body read by eitherngx.req.read_body()orlua_need_request_body onwill be directly forwarded to the subrequest without copying the whole request body data when creating the subrequest (no matter the request body data is buffered in memory buffers or temporary files). By default, this option isfalseand when thebodyoption is not specified, the request body of the current (parent) request is only forwarded when the subrequest takes thePUTorPOSTrequest method.

Issuing a POST subrequest, for example, can be done as follows

如何使用lua-nginx-module模块实现高效动态内容生成?

res = ngx.location.capture( ‘/foo/bar‘, { method = ngx.HTTP_POST, body = ‘hello, world‘ } )

See HTTP method constants methods other than POST. Themethodoption isngx.HTTP_GETby default.

Theargsoption can specify extra URI arguments, for instance,

ngx.location.capture(‘/foo?a=1‘, { args = { b = 3, c = ‘:‘ } } )

is equivalent to

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 theargsargument is identical to the format used in thengx.encode_argsmethod.

Theargsoption can also take plain query strings:

ngx.location.capture(‘/foo?a=1‘, { args = ‘b=3&c=%3a‘ } } )

This is functionally identical to the previous examples.

Theshare_all_varsoption controls whether to share nginx variables among the current request and its subrequests. If this option is set totrue, then the current request and associated subrequests will share the same Nginx variable scope. Hence, changes to Nginx variables made by a subrequest will affect the current request.

Care should be taken in using this option as variable scope sharing can have unexpected side effects. Theargs,vars, orcopy_all_varsoptions are generally preferable instead.

This option is set tofalseby default

location /other { set $dog "$dog world"; echo "$uri dog: $dog"; } location /lua { set $dog ‘hello‘; content_by_lua_block { res = ngx.location.capture("/other", { share_all_vars = true }); ngx.print(res.body) ngx.say(ngx.var.uri, ": ", ngx.var.dog) } }

Accessing location/luagives

/other dog: hello world /lua: hello world

Thecopy_all_varsoption provides a copy of the parent request‘s Nginx variables to subrequests when such subrequests are issued. Changes made to these variables by such subrequests will not affect the parent request or any other subrequests sharing the parent request‘s variables.

location /other { set $dog "$dog world"; echo "$uri dog: $dog"; } location /lua { set $dog ‘hello‘; content_by_lua_block { res = ngx.location.capture("/other", { copy_all_vars = true }); ngx.print(res.body) ngx.say(ngx.var.uri, ": ", ngx.var.dog) } }

RequestGET /luawill give the output

/other dog: hello world /lua: hello

Note that if bothshare_all_varsandcopy_all_varsare set to true, thenshare_all_varstakes precedence.

In addition to the two settings above, it is possible to specify values for variables in the subrequest using thevarsoption. These variables are set after the sharing or copying of variables has been evaluated, and provides a more efficient method of passing specific values to a subrequest over encoding them as URL arguments and unescaping them in the Nginx config file.

location /other { content_by_lua_block { ngx.say("dog = ", ngx.var.dog) ngx.say("cat = ", ngx.var.cat) } } location /lua { set $dog ‘‘; set $cat ‘‘; content_by_lua_block { res = ngx.location.capture("/other", { vars = { dog = "hello", cat = 32 }}); ngx.print(res.body) } }

Accessing/luawill yield the output

dog = hello cat = 32

Thectxoption can be used to specify a custom Lua table to serve as thengx.ctxtable for the subrequest.

location /sub { content_by_lua_block { ngx.ctx.foo = "bar"; } } location /lua { content_by_lua_block { local ctx = {} res = ngx.location.capture("/sub", { ctx = ctx }) ngx.say(ctx.foo); ngx.say(ngx.ctx.foo); } }

Then requestGET /luagives

bar nil

It is also possible to use thisctxoption to share the samengx.ctxtable between the current (parent) request and the subrequest:

location /sub { content_by_lua_block { ngx.ctx.foo = "bar"; } } location /lua { content_by_lua_block { res = ngx.location.capture("/sub", { ctx = ngx.ctx }) ngx.say(ngx.ctx.foo); } }

RequestGET /luayields the output

bar

Note that subrequests issued byngx.location.captureinherit all the request headers of the current request by default and that this may have unexpected side effects on the subrequest responses. For example, when using the standardngx_proxymodule to serve subrequests, an "Accept-Encoding: gzip" header in the main request may result in gzipped responses that cannot be handled properly in Lua code. Original request headers should be ignored by settingproxy_pass_request_headerstooffin subrequest locations.

When thebodyoption is not specified and thealways_forward_bodyoption is false (the default value), thePOSTandPUTsubrequests will inherit the request bodies of the parent request (if any).

There is a hard-coded upper limit on the number of concurrent subrequests possible for every main request. In older versions of Nginx, the limit was50concurrent subrequests and in more recent versions, Nginx1.1.xonwards, this was increased to200concurrent subrequests. When this limit is exceeded, the following error message is added to theerror.logfile:

[error] 13983#0: *1 subrequests cycle while processing "/uri"

The limit can be manually modified if required by editing the definition of theNGX_HTTP_MAX_SUBREQUESTSmacro in thenginx/src/http/ngx_http_request.hfile in the Nginx source tree.

Please also refer to restrictions on capturing locations configured bysubrequest directives of other modules.

本文共计1979个文字,预计阅读时间需要8分钟。

如何使用lua-nginx-module模块实现高效动态内容生成?

`ngx.location.capture` 用法:plaintextlocal res=ngx.location.capture(uri, options)发起一个同步非阻塞的nginx子请求,uri为internal的。返回的res是atable,索引依次是status(值为number类型)、body(值为string类型)。

ngx.location.capture

用法:

local res = ngx.location.capture(uri, options)

发起一个同步非阻塞的nginx子请求,uri是internal的。

返回的res是个table,索引依次是status(值是number类型)、body(值是string类型)、header(值是table类型)、truncated(值是boolean类型)

context:rewrite_by_lua*, access_by_lua*, content_by_lua*

Issues a synchronous but still non-blockingNginx Subrequestusinguri.

Nginx‘s subrequests provide a powerful way to make non-blocking internal requests to other locations configured with disk file directory oranyother nginx C modules likengx_proxy,ngx_fastcgi,ngx_memc,ngx_postgres,ngx_drizzle, and even ngx_lua itself and etc etc etc.

Also note that subrequests just mimic the HTTP interface but there isnoextra HTTP/TCP trafficnorIPC involved. Everything works internally, efficiently, on the C level.

Subrequests are completely different from HTTP 301/302 redirection (viangx.redirect) and internal redirection (viangx.exec).

You should always read the request body (by either callingngx.req.read_bodyor configuringlua_need_request_bodyon) before initiating a subrequest.

This API function (as well asngx.location.capture_multi) always buffers the whole response body of the subrequest in memory. Thus, you should usecosocketsand streaming processing instead if you have to handle large subrequest responses.

Here is a basic example:

res = ngx.location.capture(uri)

Returns a Lua table with 4 slots:res.status,res.header,res.body, andres.truncated.

res.statusholds the response status code for the subrequest response.

res.headerholds all the response headers of the subrequest and it is a normal Lua table. For multi-value response headers, the value is a Lua (array) table that holds all the values in the order that they appear. For instance, if the subrequest response headers contain the following lines:

Set-Cookie: a=3 Set-Cookie: foo=bar Set-Cookie: baz=blah

Thenres.header["Set-Cookie"]will be evaluated to the table value{"a=3", "foo=bar", "baz=blah"}.

res.bodyholds the subrequest‘s response body data, which might be truncated. You always need to check theres.truncatedboolean flag to see ifres.bodycontains truncated data. The data truncation here can only be caused by those unrecoverable errors in your subrequests like the cases that the remote end aborts the connection prematurely in the middle of the response body data stream or a read timeout happens when your subrequest is receiving the response body data from the remote.

URI query strings can be concatenated to URI itself, for instance,

res = ngx.location.capture(‘/foo/bar?a=3&b=4‘)

Named locations like@fooare not allowed due to a limitation in the nginx core. Use normal locations combined with theinternaldirective to prepare internal-only locations.

An optional option table can be fed as the second argument, which supports the options:

  • methodspecify the subrequest‘s request method, which only accepts constants likengx.HTTP_POST.
  • bodyspecify the subrequest‘s request body (string value only).
  • argsspecify the subrequest‘s URI query arguments (both string value and Lua tables are accepted)
  • ctxspecify a Lua table to be thengx.ctxtable for the subrequest. It can be the current request‘sngx.ctxtable, which effectively makes the parent and its subrequest to share exactly the same context table. This option was first introduced in thev0.3.1rc25release.
  • varstake a Lua table which holds the values to set the specified Nginx variables in the subrequest as this option‘s value. This option was first introduced in thev0.3.1rc31release.
  • copy_all_varsspecify whether to copy over all the Nginx variable values of the current request to the subrequest in question. modifications of the nginx variables in the subrequest will not affect the current (parent) request. This option was first introduced in thev0.3.1rc31release.
  • share_all_varsspecify whether to share all the Nginx variables of the subrequest with the current (parent) request. modifications of the Nginx variables in the subrequest will affect the current (parent) request. Enabling this option may lead to hard-to-debug issues due to bad side-effects and is considered bad and harmful. Only enable this option when you completely know what you are doing.
  • always_forward_bodywhen set to true, the current (parent) request‘s request body will always be forwarded to the subrequest being created if thebodyoption is not specified. The request body read by eitherngx.req.read_body()orlua_need_request_body onwill be directly forwarded to the subrequest without copying the whole request body data when creating the subrequest (no matter the request body data is buffered in memory buffers or temporary files). By default, this option isfalseand when thebodyoption is not specified, the request body of the current (parent) request is only forwarded when the subrequest takes thePUTorPOSTrequest method.

Issuing a POST subrequest, for example, can be done as follows

如何使用lua-nginx-module模块实现高效动态内容生成?

res = ngx.location.capture( ‘/foo/bar‘, { method = ngx.HTTP_POST, body = ‘hello, world‘ } )

See HTTP method constants methods other than POST. Themethodoption isngx.HTTP_GETby default.

Theargsoption can specify extra URI arguments, for instance,

ngx.location.capture(‘/foo?a=1‘, { args = { b = 3, c = ‘:‘ } } )

is equivalent to

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 theargsargument is identical to the format used in thengx.encode_argsmethod.

Theargsoption can also take plain query strings:

ngx.location.capture(‘/foo?a=1‘, { args = ‘b=3&c=%3a‘ } } )

This is functionally identical to the previous examples.

Theshare_all_varsoption controls whether to share nginx variables among the current request and its subrequests. If this option is set totrue, then the current request and associated subrequests will share the same Nginx variable scope. Hence, changes to Nginx variables made by a subrequest will affect the current request.

Care should be taken in using this option as variable scope sharing can have unexpected side effects. Theargs,vars, orcopy_all_varsoptions are generally preferable instead.

This option is set tofalseby default

location /other { set $dog "$dog world"; echo "$uri dog: $dog"; } location /lua { set $dog ‘hello‘; content_by_lua_block { res = ngx.location.capture("/other", { share_all_vars = true }); ngx.print(res.body) ngx.say(ngx.var.uri, ": ", ngx.var.dog) } }

Accessing location/luagives

/other dog: hello world /lua: hello world

Thecopy_all_varsoption provides a copy of the parent request‘s Nginx variables to subrequests when such subrequests are issued. Changes made to these variables by such subrequests will not affect the parent request or any other subrequests sharing the parent request‘s variables.

location /other { set $dog "$dog world"; echo "$uri dog: $dog"; } location /lua { set $dog ‘hello‘; content_by_lua_block { res = ngx.location.capture("/other", { copy_all_vars = true }); ngx.print(res.body) ngx.say(ngx.var.uri, ": ", ngx.var.dog) } }

RequestGET /luawill give the output

/other dog: hello world /lua: hello

Note that if bothshare_all_varsandcopy_all_varsare set to true, thenshare_all_varstakes precedence.

In addition to the two settings above, it is possible to specify values for variables in the subrequest using thevarsoption. These variables are set after the sharing or copying of variables has been evaluated, and provides a more efficient method of passing specific values to a subrequest over encoding them as URL arguments and unescaping them in the Nginx config file.

location /other { content_by_lua_block { ngx.say("dog = ", ngx.var.dog) ngx.say("cat = ", ngx.var.cat) } } location /lua { set $dog ‘‘; set $cat ‘‘; content_by_lua_block { res = ngx.location.capture("/other", { vars = { dog = "hello", cat = 32 }}); ngx.print(res.body) } }

Accessing/luawill yield the output

dog = hello cat = 32

Thectxoption can be used to specify a custom Lua table to serve as thengx.ctxtable for the subrequest.

location /sub { content_by_lua_block { ngx.ctx.foo = "bar"; } } location /lua { content_by_lua_block { local ctx = {} res = ngx.location.capture("/sub", { ctx = ctx }) ngx.say(ctx.foo); ngx.say(ngx.ctx.foo); } }

Then requestGET /luagives

bar nil

It is also possible to use thisctxoption to share the samengx.ctxtable between the current (parent) request and the subrequest:

location /sub { content_by_lua_block { ngx.ctx.foo = "bar"; } } location /lua { content_by_lua_block { res = ngx.location.capture("/sub", { ctx = ngx.ctx }) ngx.say(ngx.ctx.foo); } }

RequestGET /luayields the output

bar

Note that subrequests issued byngx.location.captureinherit all the request headers of the current request by default and that this may have unexpected side effects on the subrequest responses. For example, when using the standardngx_proxymodule to serve subrequests, an "Accept-Encoding: gzip" header in the main request may result in gzipped responses that cannot be handled properly in Lua code. Original request headers should be ignored by settingproxy_pass_request_headerstooffin subrequest locations.

When thebodyoption is not specified and thealways_forward_bodyoption is false (the default value), thePOSTandPUTsubrequests will inherit the request bodies of the parent request (if any).

There is a hard-coded upper limit on the number of concurrent subrequests possible for every main request. In older versions of Nginx, the limit was50concurrent subrequests and in more recent versions, Nginx1.1.xonwards, this was increased to200concurrent subrequests. When this limit is exceeded, the following error message is added to theerror.logfile:

[error] 13983#0: *1 subrequests cycle while processing "/uri"

The limit can be manually modified if required by editing the definition of theNGX_HTTP_MAX_SUBREQUESTSmacro in thenginx/src/http/ngx_http_request.hfile in the Nginx source tree.

Please also refer to restrictions on capturing locations configured bysubrequest directives of other modules.