openresty 소스 분석 - 루아 코드의 실행

12785 단어 openresty
텍스트 링크:https://cloud.tencent.com/developer/article/1037845
이전 글 중(https://cloud.tencent.com/developer/article/1037840) 우리는 Openresty가 루아 코드를 어떻게 불러오는지 토론했다
그러면 불러오기가 끝난 루아 코드는 어떻게 실행됩니까?
### 코드 실행
initby_루아 등 단계 오픈리스티는 메인 협업 중 루아를 통과한다pcall 직접 실행 루아 코드
accessby_lua  content_by_lua 등 단계에서 Openresty에서 새로운 협정을 만듭니다.luaresume 실행 루아 코드
양자의 차이는ngx를 집행할 수 있느냐 없느냐에 있다.slepp. ngx.thread ngx.socket
우리는 여전히 콘텐츠로...by_**단계별 설명
#content_by_**단계.
content_by_**단계에 대응하는 요청이 오면 실행 절차는ngxhttp_lua_content_handler -> ngx_http_lua_content_handler_file-> ngx_http_lua_content_by_chunk
ngx_http_lua_content_handler와ngxhttp_lua_content_handler_file 상하문 초기화, 코드 불러오기 요청 완료
ngx_http_lua_content_by_chunk 코드 실행 작업
#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;//       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으로 초기화합니다
855     ctx->entered_rewrite_phase = 0;
856     ctx->entered_access_phase = 0;
857     ctx->entered_content_phase = 0;

이 몇 개의 필드의 용도는ngxhttp_lua_content_handler 함수에서 대응하는 단계에 들어갔는지 확인하는 데 사용
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 줄, 새 루아 협정 만들기
63줄, 코드를 불러올 때 우리는 실행해야 하는 루아 함수를 메인 프로토콜의 창고 꼭대기에 놓았기 때문에 여기서 루아 를 통과해야 한다xmove 함수를 새 프로토콜로 이동
70 줄, 현재 요청 r를 새 협정의 전역 변수에 부여하여, 루아가 요청과 관련된 함수를 가져올 수 있도록 합니다. 예를 들어ngx입니다.req.get_method() 및 ngx.set_method,ngx.req.stat_시간 () 등
103줄, 새로 만든 루아 협업 실행
109-114행,ngx.thread.spawn에서 서브 스텝을 만들면ngxhttp_lua_post_thread.ngx_http_lua_post_thread 함수는 부모 협정을 ctx->posted 에 놓았습니다threads가 가리키는 체인 시계 중, 여기의ngxhttp_lua_content_run_posted_threads 실행 지연된 주 프로토콜
#ngx_http_lua_new_thread 스레드 작성
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’]
루아 중 협정도 GC의 대상이기 때문에 루아 시스템에 의해 쓰레기 회수됩니다. 끊어진 협정이 GC에서 떨어지지 않도록 openresty는 Lua 를 사용했습니다.REGISTRYINDEX['ngx http lua code coroutines key']를 사용하여 새로 생성된 스레드를 저장하고 스레드가 완료되면 테이블로부터 스레드를 이동합니다.
에서 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개 이상의 행으로 구성된 코드 행 수가 비교적 많습니다.
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 }
 

1015줄, 루아 통과resume는 협동 함수를 실행하고 되돌아오는 결과에 따라 다른 처리를 합니다
LUA_YIELD: 협업이 중단됨
0: 협동 실행 종료
기타: 메모리 부족 등 실행 오류
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                 }  
 

lua_Resume 반환 LUAYIELD - 일시 중지됨을 나타냅니다.
먼저 다음 3가지 상황을 처리합니다.
r->uri_changed는true로ngx가 호출되었음을 나타냅니다.redirect
ext->exited는true로ngx가 호출되었음을 나타냅니다.exit
ctx->exec_uri.len은true로ngx가 호출되었음을 나타냅니다.exec
나머지 상황 비교ctx->coop의 반환값
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->coop 할당
1064 행, case NGXHTTP_LUA_USER_CORO_NOP는 더 이상 협동 프로세스가 필요하지 않다는 것을 표시합니다. 이 순환에서 벗어나 다음 읽기와 쓰기 시간을 기다리거나 타이머가 만료됩니다.
1071행, case NGXHTTP_USER_THREAD_RESUME 대응ngx.thread.spawn이 호출된 상황
1085행, case NGXHTTP_LUA_CORO_RESUME에 대응하는 루아 코드가coroutine을 호출합니다.resume, 현재 스레드를 NGX 로 표시HTTP_LUA_USER_CORO_NOP
1106 줄, default 대응 NGXHTTP_LUA_CODO_YIELD, coroutine.yield가 호출된 상황
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 대응 NGXHTTP_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 순환의 시작으로 돌아가 부모 협정에서 루아resume
lua_resume가 0으로 되돌아오는 것은 현재 협정이 실행되었음을 나타냅니다
여기 ngx가 있어서.thread API의 존재로 여러 개의 협정이 달리고 있을 수 있으므로 부모 협정과 모든 하위 협정의 운행 상황을 판단해야 한다.
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행, 실행이 끝난 협정은 주 협정입니다. 전역 테이블에서 이 협정을 삭제합니다
1188-1193 행, 실행 중인 하위 스레드 개수를 판단합니다. 0이 아니면 NGX 로 돌아갑니다.AGAIN, 그렇지 않으면 goto done에서 데이터를 전송하고 NGX 로 돌아갑니다.OK
1195-1233, 집행이 끝난 것이 자협정인지 아닌지 판단
1223 줄, 협정이 이미 실행되었기 때문에 전역table에서 이 협정을 삭제하면 luaGC에서 삭제할 수 있습니다
1223 줄, 아직 운행 중인 하위 협정 개수 - 1
1226 줄, 메인 프로토콜이 실행되어야 하는지 판단, 예, NGX 로 돌아가기AGAIN, 그렇지 않으면 goto done, 데이터 전송과 관련된 작업을 수행하고 NGX 로 돌아갑니다.OK
1232-1234 행은 서브스텝이 실행 중임을 나타내며 NGX 로 돌아갑니다.AGAIN
###요약
1、initby_lua 등 단계, openresty는 주 협정에서 lua를 통과한다pcall에서 루아 코드를 직접 실행하고accessby_lua、content_by_lua 등 단계에서 Openresty에서 새로운 협정을 만듭니다.luaresume 실행 루아 코드
2、openresty는 연기할 협정을 체인 테이블에 넣고 *run_posted_threads 함수에서ngx 호출http_lua_run_thread 실행
본고는 텐센트 클라우드 미디어 공유 계획에 참여하고 읽고 있는 당신도 가입하여 함께 공유하는 것을 환영합니다.
2018-02-06

좋은 웹페이지 즐겨찾기