diff --git a/README.markdown b/README.markdown index 28ba6e68c0..23f5f225dd 100644 --- a/README.markdown +++ b/README.markdown @@ -1050,6 +1050,8 @@ Directives * [init_worker_by_lua](#init_worker_by_lua) * [init_worker_by_lua_block](#init_worker_by_lua_block) * [init_worker_by_lua_file](#init_worker_by_lua_file) +* [exit_worker_by_lua_block](#exit_worker_by_lua_block) +* [exit_worker_by_lua_file](#exit_worker_by_lua_file) * [set_by_lua](#set_by_lua) * [set_by_lua_block](#set_by_lua_block) * [set_by_lua_file](#set_by_lua_file) @@ -1528,6 +1530,43 @@ This directive was first introduced in the `v0.9.5` release. [Back to TOC](#directives) +exit_worker_by_lua_block +------------------------ + +**syntax:** *exit_worker_by_lua_block { lua-script }* + +**context:** *http* + +**phase:** *exiting-worker* + +Runs the specified Lua code upon every Nginx worker process's exiting when the master process is enabled. + +This hook is often used to release resources when [init_worker_by_lua*](#init_worker_by_lua_block) create resources, To prevent the worker exit abnormal. + +For example, + +```nginx + + exit_worker_by_lua_block { + print("log from exit_worker_by_lua_block") + } +``` + +[Back to TOC](#directives) + +exit_worker_by_lua_file +----------------------- + +**syntax:** *exit_worker_by_lua_file { lua--file-path }* + +**context:** *http* + +**phase:** *exiting-worker* + +Similar to [exit_worker_by_lua_block](#exit_worker_by_lua_block), but accepts the file path to a Lua source file or Lua bytecode file. + +[Back to TOC](#directives) + set_by_lua ---------- diff --git a/config b/config index 044deb974e..f7260b74ad 100644 --- a/config +++ b/config @@ -346,6 +346,7 @@ HTTP_LUA_SRCS=" \ $ngx_addon_dir/src/ngx_http_lua_bodyfilterby.c \ $ngx_addon_dir/src/ngx_http_lua_initby.c \ $ngx_addon_dir/src/ngx_http_lua_initworkerby.c \ + $ngx_addon_dir/src/ngx_http_lua_exitworkerby.c \ $ngx_addon_dir/src/ngx_http_lua_socket_udp.c \ $ngx_addon_dir/src/ngx_http_lua_req_method.c \ $ngx_addon_dir/src/ngx_http_lua_phase.c \ @@ -407,6 +408,7 @@ HTTP_LUA_DEPS=" \ $ngx_addon_dir/src/ngx_http_lua_bodyfilterby.h \ $ngx_addon_dir/src/ngx_http_lua_initby.h \ $ngx_addon_dir/src/ngx_http_lua_initworkerby.h \ + $ngx_addon_dir/src/ngx_http_lua_exitworkerby.h \ $ngx_addon_dir/src/ngx_http_lua_socket_udp.h \ $ngx_addon_dir/src/ngx_http_lua_req_method.h \ $ngx_addon_dir/src/ngx_http_lua_phase.h \ diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 8ad2b2d4a9..73b5dfc977 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -1228,6 +1228,36 @@ Similar to [[#init_worker_by_lua|init_worker_by_lua]], but accepts the file path This directive was first introduced in the v0.9.5 release. +== exit_worker_by_lua_block == + +'''syntax:''' ''exit_worker_by_lua_block { lua-script }'' + +'''context:''' ''http'' + +'''phase:''' ''exiting-worker'' + +Runs the specified Lua code upon every Nginx worker process's exiting when the master process is enabled. + +This hook is often used to release resources when [[#init_worker_by_lua_block|init_worker_by_lua*]] create resources, To prevent the worker exit abnormal. + +For example, + + + exit_worker_by_lua_block { + print("log from exit_worker_by_lua_block") + } + + +== exit_worker_by_lua_file == + +'''syntax:''' ''exit_worker_by_lua_file { lua--file-path }'' + +'''context:''' ''http'' + +'''phase:''' ''exiting-worker'' + +Similar to [[#exit_worker_by_lua_block|exit_worker_by_lua_block]], but accepts the file path to a Lua source file or Lua bytecode file. + == set_by_lua == '''syntax:''' ''set_by_lua $res [$arg1 $arg2 ...]'' diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index e38978389c..0f0863e1b3 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -129,6 +129,7 @@ typedef struct { #define NGX_HTTP_LUA_CONTEXT_SSL_CERT 0x0400 #define NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE 0x0800 #define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH 0x1000 +#define NGX_HTTP_LUA_CONTEXT_EXIT_WORKER 0x2000 #ifndef NGX_LUA_NO_FFI_API @@ -203,6 +204,9 @@ struct ngx_http_lua_main_conf_s { ngx_http_lua_main_conf_handler_pt init_worker_handler; ngx_str_t init_worker_src; + ngx_http_lua_main_conf_handler_pt exit_worker_handler; + ngx_str_t exit_worker_src; + ngx_http_lua_balancer_peer_data_t *balancer_peer_data; /* balancer_by_lua does not support yielding and * there cannot be any conflicts among concurrent requests, diff --git a/src/ngx_http_lua_directive.c b/src/ngx_http_lua_directive.c index 014a47237c..b2315ff706 100644 --- a/src/ngx_http_lua_directive.c +++ b/src/ngx_http_lua_directive.c @@ -23,6 +23,7 @@ #include "ngx_http_lua_bodyfilterby.h" #include "ngx_http_lua_initby.h" #include "ngx_http_lua_initworkerby.h" +#include "ngx_http_lua_exitworkerby.h" #include "ngx_http_lua_shdict.h" #include "ngx_http_lua_ssl_certby.h" #include "ngx_http_lua_lex.h" @@ -1210,6 +1211,66 @@ ngx_http_lua_init_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, } +char * +ngx_http_lua_exit_worker_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + char *rv; + ngx_conf_t save; + + save = *cf; + cf->handler = ngx_http_lua_exit_worker_by_lua; + cf->handler_conf = conf; + + rv = ngx_http_lua_conf_lua_block_parse(cf, cmd); + + *cf = save; + + return rv; +} + + +char * +ngx_http_lua_exit_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf) +{ + u_char *name; + ngx_str_t *value; + ngx_http_lua_main_conf_t *lmcf = conf; + + dd("enter"); + + /* must specify a content handler */ + if (cmd->post == NULL) { + return NGX_CONF_ERROR; + } + + if (lmcf->exit_worker_handler) { + return "is duplicate"; + } + + value = cf->args->elts; + + lmcf->exit_worker_handler = (ngx_http_lua_main_conf_handler_pt) cmd->post; + + if (cmd->post == ngx_http_lua_exit_worker_by_file) { + name = ngx_http_lua_rebase_path(cf->pool, value[1].data, + value[1].len); + if (name == NULL) { + return NGX_CONF_ERROR; + } + + lmcf->exit_worker_src.data = name; + lmcf->exit_worker_src.len = ngx_strlen(name); + + } else { + lmcf->exit_worker_src = value[1]; + } + + return NGX_CONF_OK; +} + + #if defined(NDK) && NDK static ngx_int_t ngx_http_lua_set_by_lua_init(ngx_http_request_t *r) diff --git a/src/ngx_http_lua_directive.h b/src/ngx_http_lua_directive.h index 5abfe4dde2..020da6f2af 100644 --- a/src/ngx_http_lua_directive.h +++ b/src/ngx_http_lua_directive.h @@ -49,6 +49,10 @@ char *ngx_http_lua_init_worker_by_lua_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); char *ngx_http_lua_init_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +char *ngx_http_lua_exit_worker_by_lua_block(ngx_conf_t *cf, + ngx_command_t *cmd, void *conf); +char *ngx_http_lua_exit_worker_by_lua(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); char *ngx_http_lua_code_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); #if defined(NDK) && NDK diff --git a/src/ngx_http_lua_exitworkerby.c b/src/ngx_http_lua_exitworkerby.c new file mode 100644 index 0000000000..01cff24856 --- /dev/null +++ b/src/ngx_http_lua_exitworkerby.c @@ -0,0 +1,101 @@ + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#include "ngx_http_lua_exitworkerby.h" +#include "ngx_http_lua_util.h" + + +void +ngx_http_lua_exit_worker(ngx_cycle_t *cycle) +{ + ngx_http_lua_main_conf_t *lmcf; + ngx_connection_t *c = NULL; + ngx_http_request_t *r = NULL; + ngx_http_lua_ctx_t *ctx; + ngx_http_conf_ctx_t *conf_ctx; + + lmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_lua_module); + if (lmcf == NULL + || lmcf->exit_worker_handler == NULL + || lmcf->lua == NULL) + { + return; + } + + conf_ctx = ((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index]); + + c = ngx_http_lua_create_fake_connection(NULL); + if (c == NULL) { + goto failed; + } + + c->log = ngx_cycle->log; + + r = ngx_http_lua_create_fake_request(c); + if (r == NULL) { + goto failed; + } + + r->main_conf = conf_ctx->main_conf; + r->srv_conf = conf_ctx->srv_conf; + r->loc_conf = conf_ctx->loc_conf; + + ctx = ngx_http_lua_create_ctx(r); + if (ctx == NULL) { + goto failed; + } + + ctx->context = NGX_HTTP_LUA_CONTEXT_EXIT_WORKER; + ctx->cur_co_ctx = NULL; + + ngx_http_lua_set_req(lmcf->lua, r); + + (void) lmcf->exit_worker_handler(cycle->log, lmcf, lmcf->lua); + + ngx_destroy_pool(c->pool); + return; + +failed: + + if (c) { + ngx_http_lua_close_fake_connection(c); + } + + return; +} + + +ngx_int_t +ngx_http_lua_exit_worker_by_inline(ngx_log_t *log, + ngx_http_lua_main_conf_t *lmcf, lua_State *L) +{ + int status; + + status = luaL_loadbuffer(L, (char *) lmcf->exit_worker_src.data, + lmcf->exit_worker_src.len, "=exit_worker_by_lua") + || ngx_http_lua_do_call(log, L); + + return ngx_http_lua_report(log, L, status, "exit_worker_by_lua"); +} + + +ngx_int_t +ngx_http_lua_exit_worker_by_file(ngx_log_t *log, + ngx_http_lua_main_conf_t *lmcf, lua_State *L) +{ + int status; + + status = luaL_loadfile(L, (char *) lmcf->exit_worker_src.data) + || ngx_http_lua_do_call(log, L); + + return ngx_http_lua_report(log, L, status, "exit_worker_by_file"); +} diff --git a/src/ngx_http_lua_exitworkerby.h b/src/ngx_http_lua_exitworkerby.h new file mode 100644 index 0000000000..38d28fe1a0 --- /dev/null +++ b/src/ngx_http_lua_exitworkerby.h @@ -0,0 +1,25 @@ + +/* + * Copyright (C) Yichun Zhang (agentzh) + */ + + +#ifndef _NGX_HTTP_LUA_EXITWORKERBY_H_INCLUDE_ +#define _NGX_HTTP_LUA_EXITWORKERBY_H_INCLUDE_ + + +#include "ngx_http_lua_common.h" + + +ngx_int_t ngx_http_lua_exit_worker_by_inline(ngx_log_t *log, + ngx_http_lua_main_conf_t *lmcf, lua_State *L); + +ngx_int_t ngx_http_lua_exit_worker_by_file(ngx_log_t *log, + ngx_http_lua_main_conf_t *lmcf, lua_State *L); + +void ngx_http_lua_exit_worker(ngx_cycle_t *cycle); + + +#endif /* _NGX_HTTP_LUA_EXITWORKERBY_H_INCLUDE_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index 9d914e86e8..b816d57731 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -22,6 +22,7 @@ #include "ngx_http_lua_bodyfilterby.h" #include "ngx_http_lua_initby.h" #include "ngx_http_lua_initworkerby.h" +#include "ngx_http_lua_exitworkerby.h" #include "ngx_http_lua_probe.h" #include "ngx_http_lua_semaphore.h" #include "ngx_http_lua_balancer.h" @@ -205,6 +206,20 @@ static ngx_command_t ngx_http_lua_cmds[] = { 0, (void *) ngx_http_lua_init_worker_by_file }, + { ngx_string("exit_worker_by_lua_block"), + NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS, + ngx_http_lua_exit_worker_by_lua_block, + NGX_HTTP_MAIN_CONF_OFFSET, + 0, + (void *) ngx_http_lua_exit_worker_by_inline }, + + { ngx_string("exit_worker_by_lua_file"), + NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, + ngx_http_lua_exit_worker_by_lua, + NGX_HTTP_MAIN_CONF_OFFSET, + 0, + (void *) ngx_http_lua_exit_worker_by_file }, + #if defined(NDK) && NDK /* set_by_lua $res { inline Lua code } [$arg1 [$arg2 [...]]] */ { ngx_string("set_by_lua_block"), @@ -616,7 +631,7 @@ ngx_module_t ngx_http_lua_module = { ngx_http_lua_init_worker, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ - NULL, /* exit process */ + ngx_http_lua_exit_worker, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; diff --git a/src/ngx_http_lua_phase.c b/src/ngx_http_lua_phase.c index 50c53110b8..28704456fe 100644 --- a/src/ngx_http_lua_phase.c +++ b/src/ngx_http_lua_phase.c @@ -92,6 +92,10 @@ ngx_http_lua_ngx_get_phase(lua_State *L) lua_pushliteral(L, "ssl_session_fetch"); break; + case NGX_HTTP_LUA_CONTEXT_EXIT_WORKER: + lua_pushliteral(L, "exit_worker"); + break; + default: return luaL_error(L, "unknown phase: %#x", (int) ctx->context); } diff --git a/src/ngx_http_lua_util.h b/src/ngx_http_lua_util.h index 2f995e0455..53230a56dd 100644 --- a/src/ngx_http_lua_util.h +++ b/src/ngx_http_lua_util.h @@ -85,6 +85,7 @@ extern char ngx_http_lua_headers_metatable_key; : (c) == NGX_HTTP_LUA_CONTEXT_BODY_FILTER ? "body_filter_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_TIMER ? "ngx.timer" \ : (c) == NGX_HTTP_LUA_CONTEXT_INIT_WORKER ? "init_worker_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_EXIT_WORKER ? "exit_worker_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_BALANCER ? "balancer_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_SSL_CERT ? "ssl_certificate_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE ? \ diff --git a/t/089-phase.t b/t/089-phase.t index 94b7619ec9..8788d3bd73 100644 --- a/t/089-phase.t +++ b/t/089-phase.t @@ -176,3 +176,25 @@ GET /lua init_worker --- no_error_log [error] + + + +=== TEST 11: get_phase in exit_worker_by_lua +--- http_config + exit_worker_by_lua_block { + local phase = ngx.get_phase() + ngx.log(ngx.NOTICE, phase) + } +--- config + location /lua { + content_by_lua_block { + ngx.say("ok") + } + } +--- request +GET /lua +--- response_body +ok +--- shutdown_error_log +exit_worker + diff --git a/t/151-exit-worker.t b/t/151-exit-worker.t new file mode 100644 index 0000000000..38eaf92327 --- /dev/null +++ b/t/151-exit-worker.t @@ -0,0 +1,132 @@ +# vim:set ft= ts=4 sw=4 et fdm=marker: + +use Test::Nginx::Socket::Lua; + +repeat_each(2); + +plan tests => repeat_each() * (blocks() * 2); + +#log_level("warn"); +no_long_string(); +our $HtmlDir = html_dir; + +run_tests(); + +__DATA__ + +=== TEST 1: simple exit_worker_by_lua_block +--- http_config + exit_worker_by_lua_block { + ngx.log(ngx.NOTICE, "log from exit_worker_by_lua_block") + } +--- config + location /t { + echo "ok"; + } +--- request +GET /t +--- response_body +ok +--- shutdown_error_log +log from exit_worker_by_lua_block + + + +=== TEST 2: simple exit_worker_by_lua_file +--- http_config + exit_worker_by_lua_file html/exit_worker.lua; +--- config + location /t { + echo "ok"; + } +--- user_files +>>> exit_worker.lua +ngx.log(ngx.NOTICE, "log from exit_worker_by_lua_file") +--- request +GET /t +--- response_body +ok +--- shutdown_error_log +log from exit_worker_by_lua_file + + + +=== TEST 3: exit_worker_by_lua (require a global table) +--- http_config eval + qq{lua_package_path '$::HtmlDir/?.lua;;'; + exit_worker_by_lua_block { + foo = require("foo") + ngx.log(ngx.NOTICE, foo.bar) + }} +--- config + location /t { + content_by_lua_block { + foo = require("foo") + foo.bar = "hello, world" + ngx.say("ok") + } + } +--- user_files +>>> foo.lua +return {} +--- request +GET /t +--- response_body +ok +--- shutdown_error_log +hello, world + + + +=== TEST 4: exit_worker_by_lua single process ngx.timer not work +--- http_config + exit_worker_by_lua_block { + local function bar() + ngx.log(ngx.ERR, "run the timer!" + end + + local ok, err = ngx.timer.at(0, bar) + if not ok then + ngx.log(ngx.ERR, "failed to create timer: " .. err) + else + ngx.log(ngx.NOTICE, "success") + end + } +--- config + location /t { + echo "ok"; + } +--- request +GET /t +--- response_body +ok +--- no_error_log + + + +=== TEST 5: exit_worker_by_lua use shdict +--- http_config + lua_shared_dict dog 10m; + exit_worker_by_lua_block { + local dog = ngx.shared.dog + local val, err = dog:get("foo") + if not val then + ngx.log(ngx.ERR, "failed get shdict: " .. err) + else + ngx.log(ngx.NOTICE, "get val: " .. val) + end + } +--- config + location /t { + content_by_lua_block { + local dog = ngx.shared.dog + dog:set("foo", 100) + ngx.say("ok") + } + } +--- request +GET /t +--- response_body +ok +--- shutdown_error_log +get val: 100