1
1
> 以下代码均出自 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 ` 类似,不展开讨论。
5
5
>
6
6
7
7
8
8
### rewrite_by_lua
9
9
10
- ` lua-nginx-module ` 通过这个指令来介入到一个请求的 rewrite 阶段。先来看下其配置项结构
10
+ ` lua-nginx-module ` 通过这个指令来介入到一个请求的 rewrite 阶段。先来看下其配置项结构。
11
11
12
12
``` c
13
13
{ ngx_string ("rewrite_by_lua"),
18
18
(void * ) ngx_http_lua_rewrite_handler_inline },
19
19
```
20
20
21
- 从这里可以了解到
21
+ 从这里可以了解到:
22
22
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`。
26
26
27
- 同样地,我们来看下 `ngx_http_lua_rewrite_by_lua ` 这个函数
27
+ 同样地,我们来看下 `ngx_http_lua_rewrite_by_lua ` 这个函数:
28
28
29
29
```c
30
30
/* parse `rewrite_by_lua` */
@@ -126,17 +126,17 @@ ngx_http_lua_rewrite_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
126
126
127
127
<img src =" ../images/003-ngx_http_lua_rewrite_by_lua-001.png " alt =" chunkname " >
128
128
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 ` 指令)。
130
130
131
131
<img src =" ../images/003-ngx_http_lua_rewrite_by_lua-002.png " alt =" rewrite_src_key " >
132
132
133
133
后面就是用这个 key 来检索缓存 Lua code,当然前提是 ` lua_code_cache ` 这条指令打开,生产环境中一般都需要对 Lua code 进行缓存以提高效率;开发环境中为了调试方便则可以关闭缓存。
134
134
135
- 恩,指令解析到这里就结束了,那么,` rewrite_by_lua ` 究竟是如何让 Lua code 介入到 HTTP 请求里的呢?下面就来一探究竟
135
+ 恩,指令解析到这里就结束了,那么,` rewrite_by_lua ` 究竟是如何让 Lua code 介入到 HTTP 请求里的呢?下面就来一探究竟。
136
136
137
137
### rewrite_by_lua_handler
138
138
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 ` 的介绍)。
140
140
141
141
``` c
142
142
ngx_int_t
@@ -289,9 +289,9 @@ ngx_http_lua_rewrite_handler(ngx_http_request_t *r)
289
289
}
290
290
```
291
291
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。
295
295
296
296
继续看这个函数,我们会发现原来 `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 ` 会被调用。
297
297
@@ -326,11 +326,11 @@ ngx_http_lua_rewrite_handler_inline(ngx_http_request_t *r)
326
326
```
327
327
328
328
这个函数通过函数 ` 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。下面就来分析下这些过程。
330
330
331
331
### ngx_http_lua_get_lua_vm
332
332
333
- 首先,Lua VM 是怎么取到的呢?请看 ` ngx_http_lua_get_lua_vm `
333
+ 首先,Lua VM 是怎么取到的呢?请看 ` ngx_http_lua_get_lua_vm ` :
334
334
335
335
``` c
336
336
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)
352
352
}
353
353
```
354
354
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。
356
356
357
357
### ngx_http_lua_cache_loadbuffer
358
358
359
359
接下来我们看下 得到 Lua chunk 的过程,也就是 `ngx_http_lua_cache_loadbuffer`
360
- 这个函数
360
+ 这个函数:
361
361
362
362
```c
363
363
ngx_int_t
@@ -429,16 +429,195 @@ error:
429
429
}
430
430
```
431
431
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
+ }
433
487
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);
438
489
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
440
498
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
+ ```
442
621
443
622
### 总结
444
623
0 commit comments