Skip to content

Commit a488ea8

Browse files
committed
add 015-nx_lua_log.md, but the others not yet
1 parent ddf6938 commit a488ea8

File tree

4 files changed

+669
-26
lines changed

4 files changed

+669
-26
lines changed

chapters/003-ngx_lua_rewrite_by_lua.md

Lines changed: 205 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
> 以下代码均出自 lua-nginx-module v0.10.7 版本
2-
3-
> 这篇文章主要要介绍 `rewrite_by_lua` 这个指令 <br>
4-
> rewrite 是 Nginx HTTP 框架划分的 11 个阶段的其中之一,通常在这个阶段我们可以实现 uri 的修改(进行内部重定向)或者 301、302 的 HTTP 重定向;另外,下面的讲解以 `rewrite_by_lua` 这个指令来展开,`rewrite_by_lua_block``rewrite_by_lua _file` 类似,不再赘述
2+
>
3+
> 这篇文章主要要介绍 `rewrite_by_lua` 这个指令以及由这个指令延伸开来的相关函数。 <br>
4+
> rewrite 是 Nginx HTTP 框架划分的 11 个阶段的其中之一,通常在这个阶段我们可以实现 uri 的修改下面的讲解以 `rewrite_by_lua` 这个指令来展开,`rewrite_by_lua_block``rewrite_by_lua _file` 类似,不展开讨论。
55
>
66
77

88
### rewrite_by_lua
99

10-
`lua-nginx-module` 通过这个指令来介入到一个请求的 rewrite 阶段。先来看下其配置项结构
10+
`lua-nginx-module` 通过这个指令来介入到一个请求的 rewrite 阶段。先来看下其配置项结构
1111

1212
```c
1313
{ ngx_string("rewrite_by_lua"),
@@ -18,13 +18,13 @@
1818
(void *) ngx_http_lua_rewrite_handler_inline },
1919
```
2020
21-
从这里可以了解到
21+
从这里可以了解到
2222
23-
- 这个指令可以出现在 main 配置块下、server 配置块下、location 配置块下和 location 下的 if 块
24-
- 必须携带一个参数
25-
- 解析函数是 `ngx_http_lua_rewrite_by_lua`
23+
- 这个指令可以出现在 main 配置块下、server 配置块下、location 配置块下和 location 下的 if 块
24+
- 必须携带一个参数
25+
- 解析函数是 `ngx_http_lua_rewrite_by_lua`
2626
27-
同样地,我们来看下 `ngx_http_lua_rewrite_by_lua ` 这个函数
27+
同样地,我们来看下 `ngx_http_lua_rewrite_by_lua ` 这个函数
2828
2929
```c
3030
/* parse `rewrite_by_lua` */
@@ -126,17 +126,17 @@ ngx_http_lua_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
126126

127127
<img src="../images/003-ngx_http_lua_rewrite_by_lua-001.png" alt="chunkname">
128128

129-
接下来根据配置项配置的 post 指针指向的函数不同,记录下 Lua code 或者 Lua code path;最后为 Lua code cache 这个功能计算了一个 cache key,通过 gdb 我们来看下计算出来的 key 是什么样的(这边实例走的是 `rewrite_by_lua_file` 指令)
129+
接下来根据配置项配置的 post 指针指向的函数不同,记录下 Lua code 或者 Lua code path;最后为 Lua code cache 这个功能计算了一个 cache key,通过 gdb 我们来看下计算出来的 key 是什么样的(这边实例走的是 `rewrite_by_lua_file` 指令)
130130

131131
<img src="../images/003-ngx_http_lua_rewrite_by_lua-002.png" alt="rewrite_src_key">
132132

133133
后面就是用这个 key 来检索缓存 Lua code,当然前提是 `lua_code_cache` 这条指令打开,生产环境中一般都需要对 Lua code 进行缓存以提高效率;开发环境中为了调试方便则可以关闭缓存。
134134

135-
恩,指令解析到这里就结束了,那么,`rewrite_by_lua` 究竟是如何让 Lua code 介入到 HTTP 请求里的呢?下面就来一探究竟
135+
恩,指令解析到这里就结束了,那么,`rewrite_by_lua` 究竟是如何让 Lua code 介入到 HTTP 请求里的呢?下面就来一探究竟
136136

137137
### rewrite_by_lua_handler
138138

139-
这是真正被插入到 rewrite 阶段的回调方法(见 [ngx_lua_init_by_lua](001-ngx_lua_init_by_lua.md) 关于`ngx_http_lua_init` 的介绍)
139+
这是真正被插入到 rewrite 阶段的回调方法(见 [ngx_lua_init_by_lua](001-ngx_lua_init_by_lua.md) 关于`ngx_http_lua_init` 的介绍)
140140

141141
```c
142142
ngx_int_t
@@ -289,9 +289,9 @@ ngx_http_lua_rewrite_handler(ngx_http_request_t *r)
289289
}
290290
```
291291
292-
让我们来解剖下这个函数 <br>
293-
首先可以看到,如果 uri 被改变了,该函数直接返回 <br>
294-
再来回顾下 [ngx_lua_init_by_lua](001-ngx_lua_init_by_lua.md) 这一节,里面有介绍 `ngx_http_lua_init` 这个函数,该函数的一小段代码则是把 `lmcf->postponed_to_rewrite_phase_end ` 置 0,这个变量用来标记 `rewrite_by_lua` 的回调方法是否被放置 rewrite 阶段最后,因此这个函数需要检测下这个变量,当它发现 Lua 的回调方法没有放到最后一位时(第一个被 Lua 介入到的请求),需要手动修改回调方法的位置,我们知道 HTTP 框架通过 `phase_engine.handlers` 这个动态数组来连续存放每个 HTTP 模块插入到所有阶段的回调,因此修改顺序实际只需要修改这个数组就行了,值得注意的是,我们要对 `r->phase_handler` 减一,因为交换顺序完毕后,该函数返回 `NGX_DECLINED`,代表希望 HTTP 框架按顺序执行下一个模块的回调,而在 `ngx_http_core_rewrite_phase` 这个 checker 中则会对 `r->phase_handler` 加一,为了某个模块的回调不被漏掉,这里才对这个值减去了 1
292+
让我们来解剖下这个函数<br>
293+
首先可以看到,如果 uri 被改变了,该函数直接返回 <br>
294+
再来回顾下 [ngx_lua_init_by_lua](001-ngx_lua_init_by_lua.md) 这一节,里面有介绍 `ngx_http_lua_init` 这个函数,该函数的一小段代码则是把 `lmcf->postponed_to_rewrite_phase_end ` 置 0,这个变量用来标记 `rewrite_by_lua` 的回调方法是否被放置 rewrite 阶段最后,因此这个函数需要检测下这个变量,当它发现 Lua 的回调方法没有放到最后一位时(第一个被 Lua 介入到的请求),需要手动修改回调方法的位置,我们知道 HTTP 框架通过 `phase_engine.handlers` 这个动态数组来连续存放每个 HTTP 模块插入到所有阶段的回调,因此修改顺序实际只需要修改这个数组就行了,值得注意的是,我们要对 `r->phase_handler` 减一,因为交换顺序完毕后,该函数返回 `NGX_DECLINED`,代表希望 HTTP 框架按顺序执行下一个模块的回调,而在 `ngx_http_core_rewrite_phase` 这个 checker 中则会对 `r->phase_handler` 加一,为了某个模块的回调不被漏掉,这里才对这个值减去了 1
295295
296296
继续看这个函数,我们会发现原来 `lua-nginx-module` 的模块上下文也是在某请求里被创建的,当然只会创建一次;另外,对于重入到 `rewrite_by_lua` 的情况,这个函数也做了处理;最后,当它发现需要需要读取请求体的时候,它还会调用 `ngx_http_read_client_request_body ` 来读请求体,如果一次读不完,把 `ctx->waiting_more_body` 设置为 1,然后返回 `NGX_DONE`,这个特殊的返回值会让 `ngx_http_core_rewrite_phase ` 这个 checker 会让 HTTP 框架的控制权返回到事件模块,调度其他的请求,而这个请求则会在未来某个时刻被重新调度到,届时 `rewrite_by_lua ` 的回调方法会被重入,当然,该读的读完,一切就绪之后,就轮到我们的 Lua 代码被执行了,也就是 `ngx_http_lua_rewrite_handler_inline ` 会被调用。
297297
@@ -326,11 +326,11 @@ ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r)
326326
```
327327

328328
这个函数通过函数 `ngx_http_lua_get_lua_vm` 获取到 Lua VM,然后进行 rewrite 阶段的 Lua code 的“载入”(从缓存里取或者调用 `lua_loadbuffer` 载入)
329-
最后调用 `ngx_http_lua_rewrite_by_chunk ` 运行 Lua chunk。下面就来分析下这些过程
329+
最后调用 `ngx_http_lua_rewrite_by_chunk ` 运行 Lua chunk。下面就来分析下这些过程
330330

331331
### ngx_http_lua_get_lua_vm
332332

333-
首先,Lua VM 是怎么取到的呢?请看 `ngx_http_lua_get_lua_vm`
333+
首先,Lua VM 是怎么取到的呢?请看 `ngx_http_lua_get_lua_vm`
334334

335335
```c
336336
static ngx_inline lua_State *
@@ -352,12 +352,12 @@ ngx_http_lua_get_lua_vm(ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx)
352352
}
353353
```
354354
355-
代码非常简单,首先是尝试从 `lua-nginx-module` 模块上下文里取 Lua VM,但是有可能此时 ctx 还没有被创建出来,所以在 ctx 没有被创建出来的情况下,就从 `lua-nginx-module` 的 main 配置结构体里取出 Lua VM
355+
代码非常简单,首先是尝试从 `lua-nginx-module` 模块上下文里取 Lua VM,但是有可能此时 ctx 还没有被创建出来,所以在 ctx 没有被创建出来的情况下,就从 `lua-nginx-module` 的 main 配置结构体里取出 Lua VM
356356
357357
### ngx_http_lua_cache_loadbuffer
358358
359359
接下来我们看下 得到 Lua chunk 的过程,也就是 `ngx_http_lua_cache_loadbuffer`
360-
这个函数
360+
这个函数
361361
362362
```c
363363
ngx_int_t
@@ -429,16 +429,195 @@ error:
429429
}
430430
```
431431

432-
还记得之前所说的 `chunkname``rewrite_src_key`?没错,那两个字符串的用武之地就是在这里!我们来分析下这个函数,这里有个缓存状态机,步骤如下
432+
还记得之前所说的 `chunkname``rewrite_src_key`?没错,那两个字符串的用武之地就是在这里!我们来分析下这个函数,这里有个缓存状态机,步骤如下:
433+
434+
1. 调用 `ngx_http_lua_cache_load_code`,判断当前的 Lua chunk 有没有缓存,得到返回码,如果返回码为 NGX_OK,跳到第二步;如果返回码是 `NGX_ERROR`,跳到第三步;否则跳到第四步。
435+
2. 从缓存中拿到 Lua chunk 且被压入到栈,返回 NGX_OK。
436+
3. 出错,返回 NGX_ERROR。
437+
4. 缓存 Miss,从原生的 Lua 代码加载,然后压栈,如果出错,记录错误日志然后返回。`NGX_ERROR`;否则返回 `NGX_OK`
438+
439+
这里对 `ngx_http_lua_cache_load_code ``ngx_http_lua_clfactory_loadbuffer ``ngx_http_lua_cache_store_code ` 这三个具体的函数不展开分析,详细讨论见 [ngx_http_lua_cache](012-ngx_lua_cache.md)
440+
441+
最后 Lua chunk 已经被拿到而且已经在栈顶,之后就是调用`ngx_http_lua_rewrite_by_chunk` 运行通过指令传入的 Lua 代码了。
442+
443+
### ngx_http_lua_rewrite_by_chunk
444+
445+
```c
446+
static ngx_int_t
447+
ngx_http_lua_rewrite_by_chunk(lua_State *L, ngx_http_request_t *r)
448+
{
449+
int co_ref;
450+
lua_State *co;
451+
ngx_int_t rc;
452+
ngx_event_t *rev;
453+
ngx_connection_t *c;
454+
ngx_http_lua_ctx_t *ctx;
455+
ngx_http_cleanup_t *cln;
456+
457+
ngx_http_lua_loc_conf_t *llcf;
458+
459+
/* {{{ new coroutine to handle request */
460+
co = ngx_http_lua_new_thread(r, L, &co_ref);
461+
462+
if (co == NULL) {
463+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
464+
"lua: failed to create new coroutine to handle request");
465+
466+
return NGX_HTTP_INTERNAL_SERVER_ERROR;
467+
}
468+
469+
/* move code closure to new coroutine */
470+
lua_xmove(L, co, 1);
471+
472+
/* set closure's env table to new coroutine's globals table */
473+
ngx_http_lua_get_globals_table(co);
474+
lua_setfenv(co, -2);
475+
476+
/* save nginx request in coroutine globals table */
477+
ngx_http_lua_set_req(co, r);
478+
479+
/* {{{ initialize request context */
480+
ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module);
481+
482+
dd("ctx = %p", ctx);
483+
484+
if (ctx == NULL) {
485+
return NGX_ERROR;
486+
}
433487

434-
1. 调用 `ngx_http_lua_cache_load_code`,判断当前的 Lua chunk 有没有缓存,得到返回码,如果返回码为 NGX_OK,跳到第二步;如果返回码是 `NGX_ERROR`,跳到第三步;否则跳到第四步
435-
2. 从缓存中拿到 Lua chunk 且被压入到栈,返回 NGX_OK
436-
3. 出错,返回 NGX_ERROR
437-
4. 缓存 Miss,从原生的 Lua 代码加载,然后压栈,如果出错,记录错误日志然后返回 `NGX_ERROR`;否则返回 `NGX_OK`
488+
ngx_http_lua_reset_ctx(r, L, ctx);
438489

439-
这里对 `ngx_http_lua_cache_load_code ``ngx_http_lua_clfactory_loadbuffer ``ngx_http_lua_cache_store_code ` 这三个具体的函数不展开分析,有兴趣可以自行阅读
490+
ctx->entered_rewrite_phase = 1;
491+
492+
ctx->cur_co_ctx = &ctx->entry_co_ctx;
493+
ctx->cur_co_ctx->co = co;
494+
ctx->cur_co_ctx->co_ref = co_ref;
495+
#ifdef NGX_LUA_USE_ASSERT
496+
ctx->cur_co_ctx->co_top = 1;
497+
#endif
440498

441-
最后 Lua chunk 已经被拿到而且已经在栈顶,之后调用 `ngx_http_lua_rewrite_by_chunk` 运行起来就行了
499+
/* }}} */
500+
501+
/* {{{ register request cleanup hooks */
502+
if (ctx->cleanup == NULL) {
503+
cln = ngx_http_cleanup_add(r, 0);
504+
if (cln == NULL) {
505+
return NGX_HTTP_INTERNAL_SERVER_ERROR;
506+
}
507+
508+
cln->handler = ngx_http_lua_request_cleanup_handler;
509+
cln->data = ctx;
510+
ctx->cleanup = &cln->handler;
511+
}
512+
/* }}} */
513+
514+
ctx->context = NGX_HTTP_LUA_CONTEXT_REWRITE;
515+
516+
llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);
517+
518+
if (llcf->check_client_abort) {
519+
r->read_event_handler = ngx_http_lua_rd_check_broken_connection;
520+
521+
#if (NGX_HTTP_V2)
522+
if (!r->stream) {
523+
#endif
524+
525+
rev = r->connection->read;
526+
527+
if (!rev->active) {
528+
if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {
529+
return NGX_ERROR;
530+
}
531+
}
532+
533+
#if (NGX_HTTP_V2)
534+
}
535+
#endif
536+
537+
} else {
538+
r->read_event_handler = ngx_http_block_reading;
539+
}
540+
541+
rc = ngx_http_lua_run_thread(L, r, ctx, 0);
542+
543+
if (rc == NGX_ERROR || rc > NGX_OK) {
544+
return rc;
545+
}
546+
547+
c = r->connection;
548+
549+
if (rc == NGX_AGAIN) {
550+
rc = ngx_http_lua_run_posted_threads(c, L, r, ctx);
551+
552+
} else if (rc == NGX_DONE) {
553+
ngx_http_lua_finalize_request(r, NGX_DONE);
554+
rc = ngx_http_lua_run_posted_threads(c, L, r, ctx);
555+
}
556+
557+
if (rc == NGX_OK || rc == NGX_DECLINED) {
558+
if (r->header_sent) {
559+
dd("header already sent");
560+
561+
/* response header was already generated in access_by_lua*,
562+
* so it is no longer safe to proceed to later phases
563+
* which may generate responses again */
564+
565+
if (!ctx->eof) {
566+
dd("eof not yet sent");
567+
568+
rc = ngx_http_lua_send_chain_link(r, ctx, NULL
569+
/* indicate last_buf */);
570+
if (rc == NGX_ERROR || rc > NGX_OK) {
571+
return rc;
572+
}
573+
}
574+
575+
return NGX_HTTP_OK;
576+
}
577+
578+
return NGX_DECLINED;
579+
}
580+
581+
return rc;
582+
}
583+
```
584+
585+
586+
587+
> 附录:ngx_http_lua_rewrite_by_chunk 函数 Lua 栈变化图
588+
589+
```c
590+
/* the new coroutine
591+
592+
|----------|
593+
|stack peak|
594+
|----------|
595+
|Lua chunk |
596+
|----------|
597+
||
598+
|| ngx_http_lua_get_globals_table
599+
\/
600+
|----------------|
601+
| stack peak |
602+
|----------------|
603+
| Lua chunk |
604+
|----------------|
605+
|Lua global table|
606+
|----------------|
607+
||
608+
|| lua_setfenv, ngx_http_lua_set_req
609+
\/
610+
|----------|
611+
|stack peak|
612+
|----------|
613+
|Lua chunk |
614+
|----------|
615+
||
616+
||
617+
\/
618+
619+
*/
620+
```
442621

443622
### 总结
444623

0 commit comments

Comments
 (0)