openresty源码剖析——lua代码的执行
上一篇文章中(http://www.cnblogs.com/magicsoar/p/6774872.html)我們討論了openresty是如何加載lua代碼的
那么加載完成之后的lua代碼又是如何執(zhí)行的呢
?
##代碼的執(zhí)行?
在init_by_lua等階段 ?openresty是在主協(xié)程中通過(guò)lua_pcall直接執(zhí)行l(wèi)ua代碼
而在access_by_lua ?content_by_lua等階段中,openresty創(chuàng)建一個(gè)新的協(xié)程,通過(guò)lua_resume執(zhí)行l(wèi)ua代碼
二者的區(qū)別在于能否執(zhí)行ngx.slepp. ngx.thread ngx.socket 這些有讓出操作的函數(shù)
我們依舊以content_by_**階段為例進(jìn)行講解
?
#content_by_**階段
content_by_**階段對(duì)應(yīng)的請(qǐng)求來(lái)臨時(shí),執(zhí)行流程為 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 完成了請(qǐng)求上下文初始化,代碼加載等操作
ngx_http_lua_content_by_chunk進(jìn)行代碼的執(zhí)行工作
? #ngx_http_lua_content_by_chunk 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;//標(biāo)示當(dāng)前進(jìn)入了content_phase 51 52 /* {{{ new coroutine to handle request */ 53 co = ngx_http_lua_new_thread(r, L, &co_ref);//創(chuàng)建了一個(gè)新的lua協(xié)程 54 61 ... 62 /* move code closure to new coroutine */ 63 lua_xmove(L, co, 1);//主協(xié)程的棧頂是需要執(zhí)行的lua函數(shù),通過(guò)lua_xmove將棧頂函數(shù)交換到新lua協(xié)程中 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);//把當(dāng)前請(qǐng)求r賦值給新協(xié)程的全局變量中 71 ... 103 rc = ngx_http_lua_run_thread(L, r, ctx, 0);//運(yùn)行新協(xié)程 104 ... 109 if (rc == NGX_AGAIN) { 110 return ngx_http_lua_content_run_posted_threads(L, r, ctx, 0);//執(zhí)行需要延后執(zhí)行的協(xié)程,0表示上面?zhèn)鱽?lái)的狀態(tài)是NGX_AGAIN 111 } 112 113 if (rc == NGX_DONE) { 114 return ngx_http_lua_content_run_posted_threads(L, r, ctx, 1);//執(zhí)行需要延后執(zhí)行的協(xié)程,1表示上面?zhèn)鱽?lái)的狀態(tài)是NGX_DONE 115 } 116 117 return NGX_OK; 118 }27-50行,有一步是重新設(shè)置請(qǐng)求的上下文,將用于標(biāo)示當(dāng)前進(jìn)入了那個(gè)階段的變量重置為0
855 ctx->entered_rewrite_phase = 0; 856 ctx->entered_access_phase = 0; 857 ctx->entered_content_phase = 0;這幾個(gè)字段的用處在ngx_http_lua_content_handler函數(shù)中用于確認(rèn)之前是否進(jìn)入過(guò)對(duì)應(yīng)階段
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 } ?53行,創(chuàng)建了一個(gè)新的lua協(xié)程
63行,加載代碼的時(shí)候,我們把需要執(zhí)行的lua函數(shù)放到了主協(xié)程的棧頂,所以這里我們需要通過(guò)lua_xmove將函數(shù)移到新協(xié)程中
70行,把當(dāng)前請(qǐng)求r賦值給新協(xié)程的全局變量中,從而可以讓lua執(zhí)行獲取和請(qǐng)求相關(guān)的一些函數(shù),比如ngx.req.get_method()和ngx.set_method,ngx.req.stat_time()等
103行,運(yùn)行新創(chuàng)建的lua協(xié)程
109-114行,ngx.thread.spawn中創(chuàng)建子協(xié)程后,會(huì)調(diào)用ngx_http_lua_post_thread。ngx_http_lua_post_thread函數(shù)將父協(xié)程放在了ctx->posted_threads指向的鏈表中,這里的ngx_http_lua_content_run_posted_threads運(yùn)行延后執(zhí)行的主協(xié)程
?
#ngx_http_lua_new_thread創(chuàng)建協(xié)程
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);//獲取全局變量中儲(chǔ)存協(xié)程的table 315 lua_rawget(L, LUA_REGISTRYINDEX); 316 317 co = lua_newthread(L);//創(chuàng)建新協(xié)程 319 ... 334 *ref = luaL_ref(L, -2);//將創(chuàng)建的新協(xié)程保存對(duì)應(yīng)的全局變量中 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);//恢復(fù)主協(xié)程的棧空間大小 342 return co; 343 }312行,獲得了主協(xié)程棧中有多少元素
314-315行,獲得全局變量中儲(chǔ)存協(xié)程的table ?LUA_REGISTRYINDEX[‘ngx_http_lua_code_coroutines_key’]
因?yàn)閘ua中協(xié)程也是GC的對(duì)象,會(huì)被lua系統(tǒng)進(jìn)行垃圾回收,為了保證掛起的協(xié)程不會(huì)被GC掉,openresty使用了?LUA_REGISTRYINDEX[‘ngx_http_lua_code_coroutines_key’]來(lái)保存新創(chuàng)建的協(xié)程,在協(xié)程執(zhí)行完畢后將協(xié)程從table
中刪除,使的GC可以將這個(gè)協(xié)程垃圾回收掉
317行,創(chuàng)建了一個(gè)lua_newthread并把其壓入主協(xié)程的棧頂
334行,將新創(chuàng)建的協(xié)程保存到LUA_REGISTRYINDEX[‘ngx_http_lua_code_coroutines_key’]
341行,恢復(fù)主協(xié)程的棧空間大小
343行,返回新創(chuàng)建的協(xié)程
?
#ngx_http_lua_run_thread運(yùn)行協(xié)程
ngx_http_lua_run_thread函數(shù)的代碼行數(shù)比較多,有500多行,內(nèi)容如下:
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);//通過(guò)lua_resume執(zhí)行協(xié)程中的函數(shù) 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 } ?1015行,通過(guò)lua_resume執(zhí)行協(xié)程的函數(shù),并根據(jù)返回的結(jié)果進(jìn)行不同的處理
LUA_YIELD: 協(xié)程被掛起
0: 協(xié)程執(zhí)行結(jié)束
其他: 運(yùn)行出錯(cuò),如內(nèi)存不足等
?
1032 switch (rv) { 1033 case LUA_YIELD: 1034 ... 1047 if (r->uri_changed) { 1048 return ngx_http_lua_handle_rewrite_jump(L, r, ctx);//調(diào)用了ngx.redirect 1049 } 1050 1051 if (ctx->exited) { 1052 return ngx_http_lua_handle_exit(L, r, ctx);//調(diào)用了ngx.exit 1053 } 1054 1055 if (ctx->exec_uri.len) { 1056 return ngx_http_lua_handle_exec(L, r, ctx);//調(diào)用了ngx.exec 1057 }?? ?lua_resume返回LUA_YIELD,表示被掛起
先處理以下3種情況:
r->uri_changed為true表明調(diào)用了ngx.redirect
ext->exited為true表明調(diào)用了ngx.exit
ctx->exec_uri.len為true表明調(diào)用了ngx.exec
其余情況需要再比較ctx->co_op的返回值
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內(nèi)部重新實(shí)現(xiàn)的coroutine.yield ?和coroutine.resume 和 ngx.thread.spawn中 會(huì)對(duì)ctx->co_op進(jìn)行賦值
1064行,case NGX_HTTP_LUA_USER_CORO_NOP表示不再有協(xié)程需要處理了,跳出這一次循環(huán),等待下一次的讀寫時(shí)間,或者定時(shí)器到期
1071行,case NGX_HTTP_USER_THREAD_RESUME 對(duì)應(yīng)?ngx.thread.spawn被調(diào)用的情況
1085行,case NGX_HTTP_LUA_CORO_RESUME 對(duì)應(yīng)有l(wèi)ua代碼調(diào)用coroutine.resume,把當(dāng)前線程標(biāo)記為NGX_HTTP_LUA_USER_CORO_NOP
1106行,default 對(duì)應(yīng)NGX_HTTP_LUA_CODO_YIELD,對(duì)應(yīng)coroutine.yield被調(diào)用的情況
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 對(duì)應(yīng)NGX_HTTP_LUA_CODO_YIELD,表示coroutine.yield被調(diào)用的情況
1121行,判斷是不是主協(xié)程,或者是調(diào)用ngx.thread.spawn的協(xié)程
1135行,判斷鏈表中有沒(méi)有排隊(duì)需要執(zhí)行的協(xié)程,如果有的話,調(diào)用ngx_http_lua_post_thread將這個(gè)協(xié)程放到他們的后面,沒(méi)有的話,直接讓他自己恢復(fù)執(zhí)行即可,回到 for 循環(huán)開頭
1136-1167行,ngx.thread.spawn創(chuàng)建的子協(xié)程,需要將返回值放入父協(xié)程中
1150-1152行和1165行,將當(dāng)前需要執(zhí)行的協(xié)程,由子協(xié)程切換為父協(xié)程
1159行,放入布爾值true
1161行,將子協(xié)程的所有返回值通過(guò)lua_xmove放入父協(xié)程中
1170行,由于多了一個(gè)布爾值true返回值個(gè)數(shù)+1
1166行,回到for循環(huán)開頭,在父協(xié)程上執(zhí)行l(wèi)ua_resume
?
lua_resume返回0,表示當(dāng)前協(xié)程執(zhí)行完畢
這里因?yàn)橛衝gx.thread API的存在,可能有多個(gè)協(xié)程在跑,需要判斷父協(xié)程和所有的子協(xié)程的運(yùn)行情況。
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行,判斷是不是主協(xié)程
1187行,執(zhí)行完畢的協(xié)程是主協(xié)程,從全局table中刪除這個(gè)協(xié)程
1188-1193行,判斷還在運(yùn)行的子協(xié)程個(gè)數(shù),如果非0 返回NGX_AGAIN,否則goto done 進(jìn)行一些數(shù)據(jù)發(fā)送的相關(guān)工作并返回NGX_OK
1195-1233,判斷執(zhí)行完畢的是不是子協(xié)程
1223行,由于協(xié)程已經(jīng)執(zhí)行完畢,從全局table中刪除這個(gè)協(xié)程,可以被lua ?GC掉
1223行,還在運(yùn)行的子協(xié)程個(gè)數(shù)-1
1226行,判斷主協(xié)程是否還需要運(yùn)行,是的話,返回NGX_AGAIN,否則goto done,進(jìn)行一些數(shù)據(jù)發(fā)送的相關(guān)工作并返回NGX_OK
1232-1234行,表示有子協(xié)程還在運(yùn)行,返回NGX_AGAIN
?
?##總結(jié)
1、在init_by_lua等階段 ,openresty是在主協(xié)程中通過(guò)lua_pcall直接執(zhí)行l(wèi)ua代碼,而在access_by_lua、content_by_lua等階段中,openresty創(chuàng)建一個(gè)新的協(xié)程,通過(guò)lua_resume執(zhí)行l(wèi)ua代碼
2、openresty將要延后執(zhí)行的協(xié)程放入鏈表中,在*_run_posted_threads函數(shù)中通過(guò)調(diào)用ngx_http_lua_run_thread進(jìn)行執(zhí)行
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/magicsoar/p/6832204.html
總結(jié)
以上是生活随笔為你收集整理的openresty源码剖析——lua代码的执行的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ios整理(一)控件
- 下一篇: ijcai statistics