lua代码的执行,lua代码执行
lua代码的执行,lua代码执行
##代码的执行
在init_by_lua等阶段 openresty是在主协程中通过lua_pcall直接执行lua代码
而在access_by_lua content_by_lua等阶段中 openresty创建一个新的协程,通过lua_resume执行lua代码
二者的区别在于能否执行ngx.slepp. ngx.thread ngx.socket 这些有让出操作的函数
我们依旧以content_by_**阶段为例进行讲解
#content_by_**阶段
content_by_**阶段 对应的请求来临时 执行流程为 ngx_http_lua_content_handler -> ngx_http_lua_content_handler_file-> ngx_http_lua_content_by_chunk
ngx_http_lua_content_handler 和 ngx_http_lua_content_handler_file 完成了请求上下文初始化,代码加载等操作
ngx_http_lua_content_by_chunk进行代码的执行工作
#ngx_http_lua_content_by_chunk1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
24
ngx_int_t
25
ngx_http_lua_content_by_chunk(lua_State *L, ngx_http_request_t *r)
26
{
27
...
50
ctx->entered_content_phase = 1; //标示当前进入了content_phase
51
52 /*
{{{ new coroutine to handle request */
53
co = ngx_http_lua_new_thread(r, L, &co_ref); //创建了一个新的lua协程
54
61
...
62 /*
move code closure to new coroutine */
63
lua_xmove(L, co, 1); //主协程的栈顶
是需要执行的lua函数,通过lua_xmove将栈顶函数交换到新lua协程中
64
65 /*
set closure's env table to new coroutine's globals table */
66
ngx_http_lua_get_globals_table(co);
67
lua_setfenv(co, -2);
68
69 /*
save nginx request in coroutine globals table */
70
ngx_http_lua_set_req(co, r); //把当前请求r赋值给新协程的全局变量中
71
...
103
rc = ngx_http_lua_run_thread(L, r, ctx, 0); //运行新协程
104
...
109 if (rc
== NGX_AGAIN) {
110 return ngx_http_lua_content_run_posted_threads(L,
r, ctx, 0); //执行需要延后执行的协程,0表示上面传来的状态是NGX_AGAIN
111
}
112
113 if (rc
== NGX_DONE) {
114 return ngx_http_lua_content_run_posted_threads(L,
r, ctx, 1); //执行需要延后执行的协程,1表示上面传来的状态是NGX_DONE
115
}
116
117 return NGX_OK;
118
}
|
27-50行有一步是重新设置请求的上下文,将用于标示当前进入了那个阶段的变量重置为0
1 2 3 |
855
ctx->entered_rewrite_phase = 0;
856
ctx->entered_access_phase = 0;
857
ctx->entered_content_phase = 0;
|
这几个字段的用处在ngx_http_lua_content_handler确认之前是进入过对应阶段
1 2 3 4 5 6 7 8 9 10 11 12 |
135
ngx_int_t
136
ngx_http_lua_content_handler(ngx_http_request_t *r)
137
{
138
...
170 if (ctx->entered_content_phase)
{
171
dd( "calling
wev handler" );
172
rc = ctx->resume_handler(r);
173
dd( "wev
handler returns %d" ,
( int )
rc);
174 return rc;
175
}
176
...
206
}
|
1 |
|
53行,创建了一个新的lua协程
63行,加载代码的时候 我们把需要执行的lua函数放到了主协程的栈顶,所以这里我们需要通过lua_xmove将函数移到新协程中
70行,把当前请求r赋值给新协程的全局变量中,从而可以让lua执行获取和请求相关的一些函数,比如ngx.req.get_method()和ngx.set_method,ngx.req.stat_time()等
103行 ,运行新创建的lua协程
109-114行,ngx.thread.spawn中创建子协程后,会调用ngx_http_lua_post_thread。ngx_http_lua_post_thread函数将父协程放在了ctx->posted_threads指向的链表中,这里的ngx_http_lua_content_run_posted_threads运行延后执行的主协程
#ngx_http_lua_new_thread创建协程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
303
lua_State *
304
ngx_http_lua_new_thread(ngx_http_request_t *r, lua_State *L, int *ref)
305
{
306
...
312
base = lua_gettop(L);
313
314
lua_pushlightuserdata(L, &ngx_http_lua_coroutines_key); //获取全局变量中储存协程的table
315
lua_rawget(L, LUA_REGISTRYINDEX);
316
317
co = lua_newthread(L); //创建新协程
319
...
334
*ref = luaL_ref(L, -2); //将创建的新协程保存对应的全局变量中
335
336 if (*ref
== LUA_NOREF) {
337
lua_settop(L, base); /*
restore main thread stack */
338 return NULL;
339
}
340
341
lua_settop(L, base); //恢复主协程的栈空间大小
342 return co;
343
}
|
312行,获得了主协程栈中有多少元素
314-315行,获得全局变量中储存协程的table LUA_REGISTRYINDEX[‘ngx_http_lua_code_coroutines_key’]
因为lua中协程也是GC的对象,会被lua系统进行垃圾回收,为了保证挂起的协程不会被GC掉,openresty使用了 LUA_REGISTRYINDEX[‘ngx_http_lua_code_coroutines_key’]来保存新创建的协程,在协程执行完毕后将协程从table
中删除,使的GC可以将这个协程垃圾回收掉
317行,创建了一个lua_newthread并把其压倒主协程的栈顶
334行,将创建的协程保存到LUA_REGISTRYINDEX[‘ngx_http_lua_code_coroutines_key’]
341行,将栈中元素个数设置为之前的个数
343行,返回新创建的协程
#ngx_http_lua_run_thread运行协程
ngx_http_lua_run_thread函数的代码行数比较多,有500多行,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
951
ngx_http_lua_run_thread(lua_State *L, ngx_http_request_t *r,
952
ngx_http_lua_ctx_t *ctx, volatile int nrets)
953
{
954
...
973
NGX_LUA_EXCEPTION_TRY {
974
...
982 for (
;; ) {
983
...
997
orig_coctx = ctx->cur_co_ctx;
998
...
1015
rv = lua_resume(orig_coctx->co, nrets); //通过lua_resume执行协程中的函数
1016
...
1032 switch (rv)
{ //处理lua_resume的返回值
1033 case LUA_YIELD:
1034
..
1047 if (r->uri_changed)
{
1048 return ngx_http_lua_handle_rewrite_jump(L,
r, ctx);
1049
}
1050 if (ctx->exited)
{
1051 return ngx_http_lua_handle_exit(L,
r, ctx);
1052
}
1053 if (ctx->exec_uri.len)
{
1054 return ngx_http_lua_handle_exec(L,
r, ctx);
1055
}
1056 switch (ctx->co_op)
{
1057
...
1167
}
1168 continue ;
1169 case 0:
1170
...
1295 continue ;
1296
...
1313 default :
1314
err = "unknown
error" ;
1315 break ;
1316
}
1317
...
1444
}
1445
} NGX_LUA_EXCEPTION_CATCH {
1446
dd( "nginx
execution restored" );
1447
}
1448 return NGX_ERROR;
1449
1450
no_parent:
1451
...
1465 return (r->header_sent
|| ctx->header_sent) ?
1466
NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR;
1467
1468
done:
1469
...
1481 return NGX_OK;
1482
}
|
1 |
|
1015行,通过lua_resume执行协程的函数,并根据返回的结果进行不同的处理
LUA_YIELD: 协程被挂起
0: 协程执行结束
其他: 运行出错,如内存不足等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
1032 switch (rv)
{
1033 case LUA_YIELD:
1034
...
1047 if (r->uri_changed)
{
1048 return ngx_http_lua_handle_rewrite_jump(L,
r, ctx); //调用了ngx.redirect
1049
}
1050
1051 if (ctx->exited)
{
1052 return ngx_http_lua_handle_exit(L,
r, ctx); //调用了ngx.exit
1053
}
1054
1055 if (ctx->exec_uri.len)
{
1056 return ngx_http_lua_handle_exec(L,
r, ctx); //调用了ngx.exec
1057
}
|
1 |
|
lua_resume返回LUA_YIELD,表示被挂起
先处理以下3种情况:
r->uri_changed为true表明调用了ngx.redirect
ext->exited为true表明调用了ngx.exit
ctx->exec_uri.len为true表明调用了ngx.exec
其余情况需要再比较ctx->co_op的返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
1063 switch (ctx->co_op)
{
1064 case NGX_HTTP_LUA_USER_CORO_NOP:
1065
...
1069
ctx->cur_co_ctx = NULL;
1070 return NGX_AGAIN;
1071 case NGX_HTTP_LUA_USER_THREAD_RESUME: //ngx.thread.spawn
1072
...
1075
ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP;
1076
nrets = lua_gettop(ctx->cur_co_ctx->co) - 1;
1077
dd( "nrets
= %d" ,
nrets);
1078
...
1084 break ;
1085 case NGX_HTTP_LUA_USER_CORO_RESUME: //coroutine.resume
1086
...
1093
ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP;
1094
old_co = ctx->cur_co_ctx->parent_co_ctx->co;
1095
nrets = lua_gettop(old_co);
1096 if (nrets)
{
1097
dd( "moving
%d return values to parent" ,
nrets);
1098
lua_xmove(old_co, ctx->cur_co_ctx->co, nrets);
1099
...
1103
}
1104 break ;
1105 default : //coroutine.yield
1106
...
|
在openresty内部重新实现的coroutine.yield 和coroutine.resume 和 ngx.thread.spawn中 会对ctx->co_op进行赋值
1064行,case NGX_HTTP_LUA_USER_CORO_NOP表示不再有协程需要处理了,跳出这一次循环,等等下一次的读写时间,或者定时器到期
1071行,case NGX_HTTP_USER_THREAD_RESUME 对应 ngx.thread.spawn被调用的情况
1085行,case NGX_HTTP_LUA_CORO_RESUME 对应有lua代码调用coroutine.resume,把当前线程标记为NGX_HTTP_LUA_USER_CORO_NOP
1106行,default 对应NGX_HTTP_LUA_CODO_YIELD,表示coroutine.yield被调用的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
1113 default :
1114
...
1119
ctx->co_op = NGX_HTTP_LUA_USER_CORO_NOP;
1120
1121 if (ngx_http_lua_is_thread(ctx))
{
1122
...
1132
ngx_http_lua_probe_info( "set
co running" );
1133
ctx->cur_co_ctx->co_status = NGX_HTTP_LUA_CO_RUNNING;
1134
1135 if (ctx->posted_threads)
{
1136
ngx_http_lua_post_thread(r, ctx, ctx->cur_co_ctx);
1137
ctx->cur_co_ctx = NULL;
1138 return NGX_AGAIN;
1139
}
1140
...
1144
nrets = 0;
1145 continue ;
1146
}
1147
...
1150
nrets = lua_gettop(ctx->cur_co_ctx->co);
1151
next_coctx = ctx->cur_co_ctx->parent_co_ctx;
1152
next_co = next_coctx->co;
1153
...
1158
lua_pushboolean(next_co, 1);
1159
1160 if (nrets)
{
1161
dd( "moving
%d return values to next co" ,
nrets);
1162
lua_xmove(ctx->cur_co_ctx->co, next_co, nrets);
1163
}
1164
nrets++; /*
add the true boolean value */
1165
ctx->cur_co_ctx = next_coctx;
1166 break ;
1167
}
|
default 对应NGX_HTTP_LUA_CODO_YIELD,表示coroutine.yield被调用的情况
1121行,判断是不是主协程,或者是调用ngx.thread.spawn的协程
1135行,判断链表中有没有排队需要执行的协程,如果有的话,调用ngx_http_lua_post_thread将这个协程放到他们的后面,没有的话,直接让自己恢复执行即可,回到 for 循环开头
1136-1167行,ngx.thread.spawn创建的子协程,需要将返回值放入父协程中
1150-1152行,和 1165行 将当前需要执行的协程 由子协程切换为父协程
1159行,放入布尔值true
1161行,将子协程的所有返回值通过lua_xmove放入父协程中
1170行,由于多了一个布尔值true返回值个数+1
1166行,回到for循环开头 在父协程上执行lua_resume
lua_resume返回0,表示当前协程执行完毕
这里因为有ngx.thread API的存在,可能有多个协程在跑,需要判断父协程和所有的子协程的运行情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
1172 case 0:
1173
...
1183 if (ngx_http_lua_is_entry_thread(ctx))
{
1184
...
1187
ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);
1188 if (ctx->uthreads)
{
1189
ctx->cur_co_ctx = NULL;
1190 return NGX_AGAIN;
1191
}
1192 /*
all user threads terminated already */
1193 goto done;
1194
}
1195 if (ctx->cur_co_ctx->is_uthread)
{
1196
...
1223
ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);
1224
ctx->uthreads--;
1225 if (ctx->uthreads
== 0) {
1226 if (ngx_http_lua_entry_thread_alive(ctx))
{
1227
ctx->cur_co_ctx = NULL;
1228 return NGX_AGAIN;
1229
}
1230 goto done;
1231
}
1232 /*
some other user threads still running */
1233
ctx->cur_co_ctx = NULL;
1234 return NGX_AGAIN;
1235
}
|
1183行,判断是不是主协程
1187行,执行完毕的协程是主协程,从全局table中删除这个协程
1188-1193行,判断还在运行的子协程个数,如何非0 返回NGX_AGAIN,否则goto done 进行一些数据相应工作并返回NGX_OK
1195-1233,判断执行完毕的是不是子协程
1223行,从全局table中删除这个协程
1223行,还在运行的子协程个数-1
1226行,判断主协程是否还需要运行,是的话 返回NGX_AGAIN,否则goto done,进行一些数据相应工作并返回NGX_OK
1232-1234行,表示有子协程还在运行,返回NGX_AGAIN
##总结
1、在init_by_lua等阶段 openresty是在主协程中通过lua_pcall直接执行lua代码,而在access_by_lua content_by_lua等阶段中 openresty创建一个新的协程,通过lua_resume执行lua代码
2、openresty将要延后执行的协程放入链表中,在*_run_posted_threads函数中通过调用ngx_http_lua_run_thread进行执行
评论暂时关闭