Skip to content

Commit 6cd70c3

Browse files
committed
ops/network-service/nginx: phase, ctx/var, shared dict of lua
1 parent b907ea9 commit 6cd70c3

File tree

1 file changed

+95
-5
lines changed

1 file changed

+95
-5
lines changed

docs/ops/network-service/nginx.md

Lines changed: 95 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,7 @@ autoindex on;
871871

872872
!!! note "使用一个专门的后端程序生成文件列表页面"
873873

874-
你可能会希望使用其他的文件列表程序(例如 [h5ai](https://github.com/lrsjng/h5ai)),同时在用户访问文件时让 Nginx 直接提供,而不是让后端程序处理文件下载请求。以下是一个参考配置,视具体的文件列表程序,可能需要做一些调整:
874+
你可能会希望使用其他的文件列表程序(例如 [h5ai](https://github.com/lrsjng/h5ai),或者我们编写的用于科大镜像站的 [yadex](https://github.com/ustclug/yadex)),同时在用户访问文件时让 Nginx 直接提供,而不是让后端程序处理文件下载请求。以下是一个参考配置,视具体的文件列表程序,可能需要做一些调整:
875875

876876
```nginx
877877
root /var/www/files/;
@@ -1278,13 +1278,13 @@ print(decoded["key1"])
12781278
```lua
12791279
local _M = {}
12801280

1281-
local function some_internal_func(a)
1281+
local function double_var(a)
12821282
return a + a
12831283
end
12841284

1285-
function _M.f1(a, b)
1286-
local aa = some_internal_func(a)
1287-
local bb = some_internal_func(b)
1285+
function _M.double_plus(a, b)
1286+
local aa = double_var(a)
1287+
local bb = double_var(b)
12881288
return aa + bb
12891289
end
12901290

@@ -1314,6 +1314,96 @@ location / {
13141314

13151315
其中 [`content_by_lua_block`](https://github.com/openresty/lua-nginx-module?tab=readme-ov-file#content_by_lua_block) 控制了请求的响应内容,而 [`ngx.say`](https://github.com/openresty/lua-nginx-module?tab=readme-ov-file#ngxsay) 则会直接向响应中写入内容。
13161316

1317+
### 执行阶段 {#execution-phases}
1318+
1319+
有一张经典的图展示了 Nginx 处理请求的各个阶段中,Lua 脚本可以插入的位置:
1320+
1321+
![Order of Lua Nginx Module Directives](https://cloud.githubusercontent.com/assets/2137369/15272097/77d1c09e-1a37-11e6-97ef-d9767035fc3e.png)
1322+
1323+
/// caption
1324+
图来自 [openresty/lua-nginx-module](https://github.com/openresty/lua-nginx-module) 仓库
1325+
///
1326+
1327+
有关 Nginx 处理请求的各个阶段的介绍,可以参考 [Nginx 的 Development guide](https://nginx.org/en/docs/dev/development_guide.html#http_phases)
1328+
1329+
如果不熟悉相关的阶段的话,那么在写代码的时候可能会遇到非预期的行为。这是一个我们实际遇到的例子:
1330+
1331+
```nginx
1332+
server {
1333+
# ...
1334+
1335+
access_by_lua_file /etc/nginx/lua/access.lua;
1336+
header_filter_by_lua_file /etc/nginx/lua/header_filter.lua;
1337+
log_by_lua_file /etc/nginx/lua/log.lua;
1338+
1339+
location /lua-test0 {
1340+
return 200;
1341+
}
1342+
1343+
location /lua-test1 {
1344+
try_files $uri $uri/ =404;
1345+
}
1346+
}
1347+
```
1348+
1349+
那么在访问 `/lua-test0` 的时候,上面三个 Lua 脚本都会被执行吗?`/lua-test1` 呢?答案是:对 `/lua-test1`,这三个脚本都会被执行,但是对 `/lua-test0``access_by_lua_file` 不会被执行,其他两个会。这是因为 `return` 在 rewrite 阶段执行重定向,结束了对请求的处理,因此后面的 access 阶段的脚本不会被执行。但是 header_filter(输出过滤的一部分,未在 Phase 中列出,但是会在 content 生成后,发送数据前执行)和 log(日志记录)阶段仍然会被执行,因此就产生了和上图矛盾的现象。
1350+
1351+
### 存储与共享状态 {#storing-state}
1352+
1353+
在编写脚本时我们常见的需求有:
1354+
1355+
- 在某个阶段存储的状态需要在后续的阶段使用
1356+
- 多个 worker 进程之间需要共享状态
1357+
1358+
以下介绍 Lua 模块提供解决方案。
1359+
1360+
#### `ngx.ctx``ngx.var` {#ngx-ctx-ngx-var}
1361+
1362+
[`ngx.ctx`](https://github.com/openresty/lua-nginx-module?tab=readme-ov-file#ngxctx) 是可以存储任意 Lua 变量的表,在当前请求有效。使用方法类似如下:
1363+
1364+
```nginx
1365+
location /example {
1366+
access_by_lua_block {
1367+
ngx.ctx.start_time = ngx.now()
1368+
ngx.sleep(0.1)
1369+
}
1370+
1371+
log_by_lua_block {
1372+
local duration = ngx.now() - ngx.ctx.start_time
1373+
ngx.log(ngx.ERR, "Request took " .. duration .. " seconds")
1374+
}
1375+
}
1376+
```
1377+
1378+
可以看到,这里在 access 阶段存储了请求的开始时间,并在 log 阶段计算并输出了请求的持续时间。
1379+
1380+
不过有一个无法忽视的问题是:如果请求使用了内部跳转(internal redirect,例如 `try_files``error_page``index`),那么 `ngx.ctx` 会被清空。因此,退而求其次,需要使用 Nginx 的变量机制来存储数据。可以使用 [`ngx.var.VARIABLE`](https://github.com/openresty/lua-nginx-module?tab=readme-ov-file#ngxvarvariable) 来访问 Nginx 变量,不过 `ngx.var` 相比 `ngx.ctx` 有一些限制:
1381+
1382+
- 性能相比 `ngx.ctx` 较差。
1383+
- 每次访问 `ngx.var` 都会产生一次内存分配,因此编程时需要先获取到本地变量再使用。
1384+
- `ngx.var` 只能存储字符串或数字。
1385+
- 需要提前使用 `set` 定义变量。
1386+
1387+
对于需要使用 `ngx.var` 存储复杂 Lua 变量的场景,可以使用第三方的 [lua-resty-ctxdump](https://github.com/tokers/lua-resty-ctxdump/)。其原理是:将实际内容保存在模块内部的 `memo` 表中,而需要存储在 `ngx.var` 里面的只是 `memo` 表的 key(数字)。
1388+
1389+
#### 共享 dict {#shared-dict}
1390+
1391+
针对不同的工作进程之间需要共享状态的场景(例如统计某个 `location` 的访问状态),Lua 模块提供了 [`lua_shared_dict`](https://github.com/openresty/lua-nginx-module?tab=readme-ov-file#lua_shared_dict) 指令,可以在 `http` 块中定义一个共享 dict 区域:
1392+
1393+
```nginx
1394+
http {
1395+
lua_shared_dict my_cache 10m; # 定义一个名字为 my_cache 的共享 dict,大小 10M。
1396+
}
1397+
```
1398+
1399+
在 Lua 代码中使用 [`ngx.shared.DICT`](https://github.com/openresty/lua-nginx-module?tab=readme-ov-file#ngxshareddict) 调用。
1400+
1401+
!!! lab "获取共享 dict 的剩余空间"
1402+
1403+
对比较复杂的需求场景下,获取共享 dict 的剩余空间是有必要的——可以据此添加监控告警,防止共享 dict 被写满导致服务异常。请编写一个 Lua 脚本,当用户访问 `/shared-dict-info` 时,返回共享 dict 的总大小和剩余空间。
1404+
1405+
如果需要测试,可能还需要添加一些其他的 `location` 来向共享 dict 中写入/删除数据。
1406+
13171407
## 示例介绍 {#examples}
13181408

13191409
<!-- 以下给出一些实践中会使用的 Nginx 配置示例。

0 commit comments

Comments
 (0)