The phase of nginx has eleven processing stages, using NGX_ http_ phase_ The T structure stores the handler available for each stage, which is actually a dynamic array ngx_array_t. The element type is ngx_http_handler_pt, stored in NGX_ http_ core_ main_ conf_ In t:
typedef struct { ngx_array_t servers; /* ngx_http_core_srv_conf_t */ ngx_http_phase_engine_t phase_engine; ngx_hash_t headers_in_hash; ngx_hash_t variables_hash; ngx_array_t variables; /* ngx_http_variable_t */ ngx_array_t prefix_variables; /* ngx_http_variable_t */ ngx_uint_t ncaptures; ngx_uint_t server_names_hash_max_size; ngx_uint_t server_names_hash_bucket_size; ngx_uint_t variables_hash_max_size; ngx_uint_t variables_hash_bucket_size; ngx_hash_keys_arrays_t *variables_keys; ngx_array_t *ports; ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1]; } ngx_http_core_main_conf_t;
typedef struct { ngx_array_t handlers; } ngx_http_phase_t;
This is actually a two-dimensional linked list:
Engine processing
Using two-dimensional array phases can call all handler modules, but its organization is not flexible and efficient. In fact, nginx does not directly use handler, but implements a specific checker function for each stage, calls handler in the checker, and realizes the flexible jump of the stage according to the return value.
//Store the handler/checker, which uses next to realize the fast jump of the stage
struct ngx_http_phase_handler_s { // checker function of stage ngx_http_phase_handler_pt checker; // Each module has its own processing function ngx_http_handler_pt handler; // Point to the position of the first module in the array in the next stage ngx_uint_t next; };
//All http requests are processed using this engine
typedef struct { // Store the array of all handlers / checkers, and use next to realize the fast jump of the stage ngx_http_phase_handler_t *handlers; // Jump location of server override ngx_uint_t server_rewrite_index; // location override jump position ngx_uint_t location_rewrite_index; } ngx_http_phase_engine_t;
Engine initialization:
When parsing to http {} block, NGX_ http_ The block () function will call the postconfiguration function of all modules to add its handler processing function to the phases array.
Then nginx executes the function ngx_http_init_phase_handlers(), this function will traverse the phases array, calculate the number of handler modules, count the number of registered handlers, allocate memory, install stage classification, combine each handler with the checker of the corresponding stage, and fill in the engine array.
For example, suppose post_ In the read} phase, three modules have registered callback functions and server respectively_ If two modules of rewrite have registered callback functions, then ph[0-2] is in post_ In the read phase, ph[3-4] is in the SERVER_REWRITE phase.
All NGX of the previous stage_ http_ phase_ handler_ The next in s points to the beginning of the next stage. For example, the value of the following three is 3, and sequence number 3 is just the server of the next stage_ Rewrite starts with the serial number.
ph[0]->next = 3
ph[1]->next = 3
ph[2]->next = 3
Take a runtime example.
Contents of the runtime ph array
Array subscript , checker , handler , next
Engine running mechanism
ngx_http_core_run_phases:
ngx_ http_ request_ Member phase in t_ Handler marks the handler sequence number of the stage in the current processing process, starting from 0, usually NGX_ HTTP_ SERVER_ REWRITE_ The first module of phase ngx_http_rewrite_module.
After receiving the request header, nginx starts calling the function ngx_http_core_run_phases execution engine.
//Start engine array processing request
//From phase_ The location of the handler starts calling the module processing
void ngx_http_core_run_phases(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_phase_handler_t *ph; ngx_http_core_main_conf_t *cmcf; // Get the core main configuration cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); // Get the handler array in the engine ph = cmcf->phase_engine.handlers; // From phase_ The location of the handler starts calling the module processing // The starting sequence number of the engine array of the external request is 0. Execute the engine array from scratch, that is, start with Post read // Internal request, i.e. sub request Skip post read and directly start from server rewrite, that is, find the server while (ph[r->phase_handler].checker) { // Call the checker in the engine array rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]); // The checker checks the return value of the handler // If the handler returns again/done, it returns ok // Exit processing of engine array // Because R - > write_ event_ handler = ngx_ http_ core_ run_ phases // When there is another write event, it will continue to execute from the previous module // If the checker returns again, continue to execute in the engine array // The module consists of R - > phase_ The handler sequence number specifies that there may be stage jumps if (rc == NGX_OK) { return; } } }
The checker processes at different stages are similar. Let's take a look at NGX_ HTTP_ CONTENT_ NGX of phase_ http_ core_ content_ phase().
ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { size_t root; ngx_int_t rc; ngx_str_t path; // Check whether the request has a handler, that is, the handler is defined in the location if (r->content_handler) { // Set write event to ngx_http_request_empty_handler // That is, it will not enter NGX for the time being_ http_ core_ run_ phases // This is because the content generation stage is already the "last" stage, and there is no need to go through other stages // After that, it will be changed to NGX when sending data_ http_ set_ write_ handler // But we can also modify it so that the write event triggers our own callback r->write_event_handler = ngx_http_request_empty_handler; // Call the content processing handler dedicated to location // The return value is passed to ngx_http_finalize_request // It is equivalent to ending the request after processing // This usage simplifies the client code and is equivalent to the template method pattern // rc = handler(r); ngx_http_finalize_request(rc); // End request // But if count > 1, it will not really end // handler may return done and again, for example, call read body ngx_http_finalize_request(r, r->content_handler(r)); // You need to continue processing in the subsequent processing function. You cannot call write_event_handler return NGX_OK; } // There is no special handler // Call each module's own processing function rc = ph->handler(r); if (rc != NGX_DECLINED) { ngx_http_finalize_request(r, rc); return NGX_OK; } // Module handler returns decline. You need to continue to execute the next handler in this stage. ph++; // The next module has a checker if (ph->checker) { // Index plus 1 r->phase_handler++; // again continues the cycle of the engine array return NGX_AGAIN; } // You have reached the end of the engine array // No content module can handle it // End cycle of engine array return NGX_OK; }
Log processing phase
Log processing is not in NGX_ http_ core_ run_ Called in phases, but when the request is completed.
//The request has ended. Call the log module to record the log
//In ngx_http_finalize_request and NGX_ http_ free_ Call in request
static void ngx_http_log_request(ngx_http_request_t *r) { ngx_uint_t i, n; ngx_http_handler_pt *log_handler; ngx_http_core_main_conf_t *cmcf; cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); // The log handler is not in the engine array log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts; n = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.nelts; // Instead of checking the return value of the handler, call directly without using the checker for (i = 0; i < n; i++) { log_handler[i](r); } }
The log phase is called after a response is given
Configuration resolution phase:
static ngx_int_t ngx_http_log_init(ngx_conf_t *cf) { ngx_str_t *value; ngx_array_t a; ngx_http_handler_pt *h; ngx_http_log_fmt_t *fmt; ngx_http_log_main_conf_t *lmcf; ngx_http_core_main_conf_t *cmcf; lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module); if (lmcf->combined_used) { if (ngx_array_init(&a, cf->pool, 1, sizeof(ngx_str_t)) != NGX_OK) { return NGX_ERROR; } value = ngx_array_push(&a); if (value == NULL) { return NGX_ERROR; } *value = ngx_http_combined_fmt; fmt = lmcf->formats.elts; if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0) != NGX_CONF_OK) { return NGX_ERROR; } } cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_log_handler; return NGX_OK; }