openresty 소스 분석 - 루아 코드의 실행
12785 단어 openresty
이전 글 중(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
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
openresty 소스 분석 - 루아 코드의 실행accessby_lua content_by_lua 등 단계에서 Openresty에서 새로운 협정을 만듭니다.luaresume 실행 루아 코드 content_by_**단계에 대응하는 요청이 오면 실행 절차는ngxht...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.