Nginx(三):http模块的处理流程解析之正向代理
无疑,在nginx的核心服务中,http服务占据了相当大的份量。那么,要想多了解nginx多一点,则必须要了解其http模块的工作机制。
而在上一篇文章中,我们已完全了解了nginx的worker工作机制,以及它是如何接入http服务的,但很明显那很粗,我们需要更深入点理解http模块的工作原理。
而本身nginx对模块的支持又是复杂的,至少我们认为有两个大方向,正向代理和反向代理。正向代理实际上就是一个http服务器,明显简单些,所以,我们本篇就来说说nginx的正向代理实现吧。
0. 整体时序图
如果你对nginx的http模块工作原理有过深入理解,相信只需要这一张时序图就够。为了节省大家宝贵时,可以先一览宏图。
1.异步io事件的交接
我们知道,nginx的核心是事件io机制的使用,当外部网络io就绪时,内核会回应nginx, 而nginx则会通过accept(), receive(), fd_set 等方法,将事件接入进来,从而转交到http服务模块。其中select模块我们上一篇中已经讲过,此时再简单回顾下:(需要的话)
// event/modules/ngx_select_module.c// io 事件监听static ngx_int_tngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,ngx_uint_t flags) {int ready, nready;ngx_err_t err;ngx_uint_t i, found;ngx_event_t *ev;ngx_queue_t *queue;struct timeval tv, *tp;ngx_connection_t *c;// 获取 max_fd, 系统传值需要if (max_fd == -1) {for (i = 0; i < nevents; i++) {c = event_index[i]->data;if (max_fd < c->fd) {max_fd = c->fd;}}ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,"change max_fd: %i", max_fd);}#if (NGX_DEBUG)if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {for (i = 0; i < nevents; i++) {ev = event_index[i];c = ev->data;ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,"select event: fd:%d wr:%d", c->fd, ev->write);}ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,"max_fd: %i", max_fd);}#endifif (timer == NGX_TIMER_INFINITE) {tp = NULL;} else {tv.tv_sec = (long) (timer / 1000);tv.tv_usec = (long) ((timer % 1000) * 1000);tp = &tv;}ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,"select timer: %M", timer);work_read_fd_set = master_read_fd_set;work_write_fd_set = master_write_fd_set;// 在此处交由内核进行处理网络事件,epoll 机制,至少有一个事件到来时返回// tp 代表是否要超时退出ready = select(max_fd + 1, &work_read_fd_set, &work_write_fd_set, NULL, tp);err = (ready == -1) ? ngx_errno : 0;if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {// 事件结束后,先尝试更新gmtTime 时间信息ngx_time_update();}ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,"select ready %d", ready);if (err) {ngx_uint_t level;if (err == NGX_EINTR) {if (ngx_event_timer_alarm) {ngx_event_timer_alarm = 0;return NGX_OK;}level = NGX_LOG_INFO;} else {level = NGX_LOG_ALERT;}ngx_log_error(level, cycle->log, err, "select() failed");if (err == NGX_EBADF) {ngx_select_repair_fd_sets(cycle);}return NGX_ERROR;}if (ready == 0) {if (timer != NGX_TIMER_INFINITE) {return NGX_OK;}ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,"select() returned no events without timeout");return NGX_ERROR;}nready = 0;// 遍历所有事件for (i = 0; i < nevents; i++) {ev = event_index[i];c = ev->data;found = 0;// 写事件处理if (ev->write) {if (FD_ISSET(c->fd, &work_write_fd_set)) {found = 1;ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,"select write %d", c->fd);}}// 读或accept事件else {if (FD_ISSET(c->fd, &work_read_fd_set)) {found = 1;ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,"select read %d", c->fd);}}// 读写就绪事件 found 都为1if (found) {ev->ready = 1;ev->available = -1;// 如果是 accept 事件则取 ngx_posted_accept_events 队列// 否则取 ngx_posted_events 队列queue = ev->accept ? &ngx_posted_accept_events: &ngx_posted_events;// 将事件插入到相应队列尾部ngx_post_event(ev, queue);// 有效就绪事件+1nready++;}}// 如果两个值不相等,则需要修正下if (ready != nready) {ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,"select ready != events: %d:%d", ready, nready);ngx_select_repair_fd_sets(cycle);}return NGX_OK;}
反正大概意思就是会调用内核级别的select/poll或epoll等io机制,等待io事件的发生,然后返回到用户态。当然,为了保证系统例外情况,都会进行超时设置,避免系统事件检测的偶发异常,可以在超时机制帮助下正常工作。
此处接收到的事件可能写入两个队列: 即是否是 accept 队列之分。最开始建立连接时,自然是放入accept队列的,后续则一般放ngx_posted_events队列中。这两个队列,后续将被分开处理。accept 队列会将socket接入,并注册read监听。而 posted_events 则是需要进行正式处理的队列,将会读取数据,写入客户端等更多工作。
其中,ngx_http_init_connection 的初始化过程,如需要请点击查看.
// http/ngx_http_request.c// 初始化socket连接, 接入 http模块voidngx_http_init_connection(ngx_connection_t *c){ngx_uint_t i;ngx_event_t *rev;struct sockaddr_in *sin;ngx_http_port_t *port;ngx_http_in_addr_t *addr;ngx_http_log_ctx_t *ctx;ngx_http_connection_t *hc;#if (NGX_HAVE_INET6)struct sockaddr_in6 *sin6;ngx_http_in6_addr_t *addr6;#endif// 分配数据内存hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));if (hc == NULL) {ngx_http_close_connection(c);return;}c->data = hc;/* find the server configuration for the address:port */port = c->listening->servers;if (port->naddrs > 1) {/** there are several addresses on this port and one of them* is an "*:port" wildcard so getsockname() in ngx_http_server_addr()* is required to determine a server address*/if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {ngx_http_close_connection(c);return;}// 根据网络类型处理switch (c->local_sockaddr->sa_family) {#if (NGX_HAVE_INET6)case AF_INET6:sin6 = (struct sockaddr_in6 *) c->local_sockaddr;addr6 = port->addrs;/* the last address is "*" */for (i = 0; i < port->naddrs - 1; i++) {if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {break;}}hc->addr_conf = &addr6[i].conf;break;#endifdefault: /* AF_INET */sin = (struct sockaddr_in *) c->local_sockaddr;addr = port->addrs;/* the last address is "*" */for (i = 0; i < port->naddrs - 1; i++) {if (addr[i].addr == sin->sin_addr.s_addr) {break;}}hc->addr_conf = &addr[i].conf;break;}} else {switch (c->local_sockaddr->sa_family) {#if (NGX_HAVE_INET6)case AF_INET6:addr6 = port->addrs;hc->addr_conf = &addr6[0].conf;break;#endifdefault: /* AF_INET */addr = port->addrs;hc->addr_conf = &addr[0].conf;break;}}/* the default server configuration for the address:port */hc->conf_ctx = hc->addr_conf->default_server->ctx;ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t));if (ctx == NULL) {ngx_http_close_connection(c);return;}ctx->connection = c;ctx->request = NULL;ctx->current_request = NULL;c->log->connection = c->number;// 每个http server 都有自己的日志记录控制c->log->handler = ngx_http_log_error;c->log->data = ctx;c->log->action = "waiting for request";c->log_error = NGX_ERROR_INFO;rev = c->read;// 设置接收数据处理器为 ngx_http_wait_request_handlerrev->handler = ngx_http_wait_request_handler;c->write->handler = ngx_http_empty_handler;#if (NGX_HTTP_V2)if (hc->addr_conf->http2) {rev->handler = ngx_http_v2_init;}#endif#if (NGX_HTTP_SSL){ngx_http_ssl_srv_conf_t *sscf;sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);if (sscf->enable || hc->addr_conf->ssl) {hc->ssl = 1;c->log->action = "SSL handshaking";rev->handler = ngx_http_ssl_handshake;}}#endifif (hc->addr_conf->proxy_protocol) {hc->proxy_protocol = 1;c->log->action = "reading PROXY protocol";}if (rev->ready) {/* the deferred accept(), iocp */if (ngx_use_accept_mutex) {ngx_post_event(rev, &ngx_posted_events);return;}rev->handler(rev);return;}// 将rev 放入到 ngx_event_timer_rbtree 队列中, 红黑树实现ngx_add_timer(rev, c->listening->post_accept_timeout);// 重用 connectionngx_reusable_connection(c, 1);// 处理 读就绪事件,注册 read 监听if (ngx_handle_read_event(rev, 0) != NGX_OK) {ngx_http_close_connection(c);return;}}
经过 ngx_http_init_connection 之后,就注册了read事件, 该事件基本已就绪, 所以将会在下一次进行select操作时返回该事件, 即下一次worker巡检时触发 read. 而此时的handler则被设置为 ngx_http_wait_request_handler.
// http/ngx_http_request.c// 处理socket读事件static voidngx_http_wait_request_handler(ngx_event_t *rev){u_char *p;size_t size;ssize_t n;ngx_buf_t *b;ngx_connection_t *c;ngx_http_connection_t *hc;ngx_http_core_srv_conf_t *cscf;c = rev->data;ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler");if (rev->timedout) {ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");ngx_http_close_connection(c);return;}if (c->close) {ngx_http_close_connection(c);return;}hc = c->data;cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);// 默认1024 缓冲大小size = cscf->client_header_buffer_size;b = c->buffer;// 首次接入时,创建初始空间if (b == NULL) {// 创建缓冲区接收http传过来的数据b = ngx_create_temp_buf(c->pool, size);if (b == NULL) {ngx_http_close_connection(c);return;}c->buffer = b;} else if (b->start == NULL) {// 缓冲冲填满,需要另外增加空间?b->start = ngx_palloc(c->pool, size);if (b->start == NULL) {ngx_http_close_connection(c);return;}b->pos = b->start;b->last = b->start;b->end = b->last + size;}// 接收数据n = c->recv(c, b->last, size);if (n == NGX_AGAIN) {if (!rev->timer_set) {ngx_add_timer(rev, c->listening->post_accept_timeout);ngx_reusable_connection(c, 1);}if (ngx_handle_read_event(rev, 0) != NGX_OK) {ngx_http_close_connection(c);return;}/** We are trying to not hold c->buffer's memory for an idle connection.*/// 如果还要等待更多数据,释放占有空间if (ngx_pfree(c->pool, b->start) == NGX_OK) {b->start = NULL;}return;}if (n == NGX_ERROR) {ngx_http_close_connection(c);return;}if (n == 0) {ngx_log_error(NGX_LOG_INFO, c->log, 0,"client closed connection");ngx_http_close_connection(c);return;}b->last += n;// 如果配置了 proxy_pass (且匹配了模式), 则走代理逻辑if (hc->proxy_protocol) {hc->proxy_protocol = 0;p = ngx_proxy_protocol_read(c, b->pos, b->last);if (p == NULL) {ngx_http_close_connection(c);return;}b->pos = p;if (b->pos == b->last) {c->log->action = "waiting for request";b->pos = b->start;b->last = b->start;ngx_post_event(rev, &ngx_posted_events);return;}}c->log->action = "reading client request line";// 设置不可重用连接ngx_reusable_connection(c, 0);// 创建 http 连接请求, 分配内存空, 设置下一个 handler 等等c->data = ngx_http_create_request(c);if (c->data == NULL) {ngx_http_close_connection(c);return;}// 设置读取数据的处理器为 ngx_http_process_request_line, 以便下次使用rev->handler = ngx_http_process_request_line;ngx_http_process_request_line(rev);}
ngx_http_wait_request_handler 非常重要的一个任务就是接收客户端的传送数据,即调用 recv 方法处理数据, 使用缓冲区的方式进行读取, 默认缓冲区大小为 1024, 即实际是处理不了太多数据的. 最多读取1024字节, 然后正常情况下就进入到 ngx_http_process_request_line() 逻辑了. 也就是说, 剩下的数据会在接下再被读取, 而非一次性被读取完成. 其中, ngx_http_create_request 主要是为 body体分配内存空间.
// http/ngx_http_request.cngx_http_request_t *ngx_http_create_request(ngx_connection_t *c){ngx_http_request_t *r;ngx_http_log_ctx_t *ctx;ngx_http_core_loc_conf_t *clcf;// 重要: 分配请求的上下文信息r = ngx_http_alloc_request(c);if (r == NULL) {return NULL;}c->requests++;clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);ngx_set_connection_log(c, clcf->error_log);ctx = c->log->data;ctx->request = r;ctx->current_request = r;#if (NGX_STAT_STUB)(void) ngx_atomic_fetch_add(ngx_stat_reading, 1);r->stat_reading = 1;(void) ngx_atomic_fetch_add(ngx_stat_requests, 1);#endifreturn r;}
// http/ngx_http_request.cstatic ngx_http_request_t *ngx_http_alloc_request(ngx_connection_t *c){ngx_pool_t *pool;ngx_time_t *tp;ngx_http_request_t *r;ngx_http_connection_t *hc;ngx_http_core_srv_conf_t *cscf;ngx_http_core_main_conf_t *cmcf;hc = c->data;cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);pool = ngx_create_pool(cscf->request_pool_size, c->log);if (pool == NULL) {return NULL;}r = ngx_pcalloc(pool, sizeof(ngx_http_request_t));if (r == NULL) {ngx_destroy_pool(pool);return NULL;}r->pool = pool;r->http_connection = hc;r->signature = NGX_HTTP_MODULE;r->connection = c;// 设置配置信息备用r->main_conf = hc->conf_ctx->main_conf;r->srv_conf = hc->conf_ctx->srv_conf;r->loc_conf = hc->conf_ctx->loc_conf;// 可能使用读取方式为 blocking, 如果是异步读取, 则无需blockingr->read_event_handler = ngx_http_block_reading;// 将刚刚读取出的数据引用给到header指针, 以便重新读取r->header_in = hc->busy ? hc->busy->buf : c->buffer;if (ngx_list_init(&r->headers_out.headers, r->pool, 20,sizeof(ngx_table_elt_t))!= NGX_OK){ngx_destroy_pool(r->pool);return NULL;}if (ngx_list_init(&r->headers_out.trailers, r->pool, 4,sizeof(ngx_table_elt_t))!= NGX_OK){ngx_destroy_pool(r->pool);return NULL;}r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);if (r->ctx == NULL) {ngx_destroy_pool(r->pool);return NULL;}cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts* sizeof(ngx_http_variable_value_t));if (r->variables == NULL) {ngx_destroy_pool(r->pool);return NULL;}#if (NGX_HTTP_SSL)if (c->ssl) {r->main_filter_need_in_memory = 1;}#endifr->main = r;r->count = 1;// 分配系统默认值tp = ngx_timeofday();r->start_sec = tp->sec;r->start_msec = tp->msec;r->method = NGX_HTTP_UNKNOWN;r->http_version = NGX_HTTP_VERSION_10;r->headers_in.content_length_n = -1;r->headers_in.keep_alive_n = -1;r->headers_out.content_length_n = -1;r->headers_out.last_modified_time = -1;r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1;r->http_state = NGX_HTTP_READING_REQUEST_STATE;r->log_handler = ngx_http_log_error_handler;return r;}
而接下来的handler被设置为了 ngx_http_process_request_line , 则会进一步读取数据, 处理事件, 也是处理的核心任务.
2. 核心数据读取解析
前面我们看到, nginx 通过调用系统级recv() 接收部分客户端数据过来, 但那里仅有一个缓冲区的大小, 有可能取到的数据是不完整的. 那么, 自然需要进一步处理, 即: ngx_http_process_request_line . 它会在第一次接到数据时就进行调用, 但如果存在多数据段, 则会反复进行该 handler 的调用(这是一个核心的异步io的处理实现, 类似断点续传):
// http/ngx_http_request.c// 读取body数据,并响应客户端static voidngx_http_process_request_line(ngx_event_t *rev){ssize_t n;ngx_int_t rc, rv;ngx_str_t host;ngx_connection_t *c;ngx_http_request_t *r;c = rev->data;r = c->data;ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,"http process request line");if (rev->timedout) {ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");c->timedout = 1;ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);return;}rc = NGX_AGAIN;for ( ;; ) {if (rc == NGX_AGAIN) {// 读取header, 因为前面已经读取了部分buff, 可能就直接返回n = ngx_http_read_request_header(r);if (n == NGX_AGAIN || n == NGX_ERROR) {break;}}// 读取body 数据, 按照http协议解析,非常长// 如解析出 GET HTTP1.1...rc = ngx_http_parse_request_line(r, r->header_in);if (rc == NGX_OK) {/* the request line has been parsed successfully */// 解析成功部分信息, 进行记录处理r->request_line.len = r->request_end - r->request_start;r->request_line.data = r->request_start;r->request_length = r->header_in->pos - r->request_start;ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,"http request line: \"%V\"", &r->request_line);r->method_name.len = r->method_end - r->request_start + 1;r->method_name.data = r->request_line.data;if (r->http_protocol.data) {r->http_protocol.len = r->request_end - r->http_protocol.data;}// 处理 uri, 解析路径, 放入 r->uri.dataif (ngx_http_process_request_uri(r) != NGX_OK) {break;}if (r->schema_end) {r->schema.len = r->schema_end - r->schema_start;r->schema.data = r->schema_start;}if (r->host_end) {host.len = r->host_end - r->host_start;host.data = r->host_start;rc = ngx_http_validate_host(&host, r->pool, 0);if (rc == NGX_DECLINED) {ngx_log_error(NGX_LOG_INFO, c->log, 0,"client sent invalid host in request line");ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);break;}if (rc == NGX_ERROR) {ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);break;}if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {break;}r->headers_in.server = host;}if (r->http_version < NGX_HTTP_VERSION_10) {if (r->headers_in.server.len == 0&& ngx_http_set_virtual_server(r, &r->headers_in.server)== NGX_ERROR){break;}ngx_http_process_request(r);break;}if (ngx_list_init(&r->headers_in.headers, r->pool, 20,sizeof(ngx_table_elt_t))!= NGX_OK){ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);break;}c->log->action = "reading client request headers";// 如果运行到此处, 则意味着header数据未读取完成, 需要下一次io事件继续// handler 变更为 ngx_http_process_request_headersrev->handler = ngx_http_process_request_headers;ngx_http_process_request_headers(rev);break;}if (rc != NGX_AGAIN) {/* there was error while a request line parsing */ngx_log_error(NGX_LOG_INFO, c->log, 0,ngx_http_client_errors[rc - NGX_HTTP_CLIENT_ERROR]);if (rc == NGX_HTTP_PARSE_INVALID_VERSION) {ngx_http_finalize_request(r, NGX_HTTP_VERSION_NOT_SUPPORTED);} else {ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);}break;}/* NGX_AGAIN: a request line parsing is still incomplete */if (r->header_in->pos == r->header_in->end) {rv = ngx_http_alloc_large_header_buffer(r, 1);if (rv == NGX_ERROR) {ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);break;}if (rv == NGX_DECLINED) {r->request_line.len = r->header_in->end - r->request_start;r->request_line.data = r->request_start;ngx_log_error(NGX_LOG_INFO, c->log, 0,"client sent too long URI");ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE);break;}}}// 处理请求, 响应客户端ngx_http_run_posted_requests(c);}// http/ngx_http_request.c// 进一步处理 header 信息static voidngx_http_process_request_headers(ngx_event_t *rev){u_char *p;size_t len;ssize_t n;ngx_int_t rc, rv;ngx_table_elt_t *h;ngx_connection_t *c;ngx_http_header_t *hh;ngx_http_request_t *r;ngx_http_core_srv_conf_t *cscf;ngx_http_core_main_conf_t *cmcf;c = rev->data;r = c->data;ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,"http process request header line");if (rev->timedout) {ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");c->timedout = 1;ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);return;}cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);rc = NGX_AGAIN;for ( ;; ) {// 依次解析每一行 header 信息if (rc == NGX_AGAIN) {// 如果解析到最后还没完, 说明后续还需要读取数据if (r->header_in->pos == r->header_in->end) {rv = ngx_http_alloc_large_header_buffer(r, 0);if (rv == NGX_ERROR) {ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);break;}if (rv == NGX_DECLINED) {p = r->header_name_start;r->lingering_close = 1;if (p == NULL) {ngx_log_error(NGX_LOG_INFO, c->log, 0,"client sent too large request");ngx_http_finalize_request(r,NGX_HTTP_REQUEST_HEADER_TOO_LARGE);break;}len = r->header_in->end - p;if (len > NGX_MAX_ERROR_STR - 300) {len = NGX_MAX_ERROR_STR - 300;}ngx_log_error(NGX_LOG_INFO, c->log, 0,"client sent too long header line: \"%*s...\"",len, r->header_name_start);ngx_http_finalize_request(r,NGX_HTTP_REQUEST_HEADER_TOO_LARGE);break;}}// 将会尝试读取更多的 header 数据n = ngx_http_read_request_header(r);if (n == NGX_AGAIN || n == NGX_ERROR) {break;}}/* the host header could change the server configuration context */cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);// 重新解析 header 信息rc = ngx_http_parse_header_line(r, r->header_in,cscf->underscores_in_headers);if (rc == NGX_OK) {r->request_length += r->header_in->pos - r->header_name_start;if (r->invalid_header && cscf->ignore_invalid_headers) {/* there was error while a header line parsing */ngx_log_error(NGX_LOG_INFO, c->log, 0,"client sent invalid header line: \"%*s\"",r->header_end - r->header_name_start,r->header_name_start);continue;}/* a header line has been parsed successfully */h = ngx_list_push(&r->headers_in.headers);if (h == NULL) {ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);break;}h->hash = r->header_hash;h->key.len = r->header_name_end - r->header_name_start;h->key.data = r->header_name_start;h->key.data[h->key.len] = '\0';h->value.len = r->header_end - r->header_start;h->value.data = r->header_start;h->value.data[h->value.len] = '\0';h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);if (h->lowcase_key == NULL) {ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);break;}if (h->key.len == r->lowcase_index) {ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);} else {ngx_strlow(h->lowcase_key, h->key.data, h->key.len);}hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,h->lowcase_key, h->key.len);// ngx_http_process_host, 处理 host 验证// ngx_http_process_connection, 处理 keepaliveif (hh && hh->handler(r, h, hh->offset) != NGX_OK) {break;}ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"http header: \"%V: %V\"",&h->key, &h->value);continue;}// header 解析完成后, 处理body 数据了if (rc == NGX_HTTP_PARSE_HEADER_DONE) {/* a whole header has been parsed successfully */ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"http header done");r->request_length += r->header_in->pos - r->header_name_start;r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;rc = ngx_http_process_request_header(r);if (rc != NGX_OK) {break;}ngx_http_process_request(r);break;}if (rc == NGX_AGAIN) {/* a header line parsing is still not complete */continue;}/* rc == NGX_HTTP_PARSE_INVALID_HEADER */ngx_log_error(NGX_LOG_INFO, c->log, 0,"client sent invalid header line");ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);break;}ngx_http_run_posted_requests(c);}
以上是整个http的主要流程之一, 主要分为读取 header 处理header, 读取body处理body, 最后 ngx_http_run_posted_requests, 做后置处理. 其中重要的实现方式是, 依次读取每个字节, 进行http协议解析, 按行划分 header, 以及读取部分buffer就处理部分header等优化.
接下来, 我们看看其对 body 部分的处理:
// http/ngx_http_request.c// 处理 body 部分处理, 并处理响应对应请求voidngx_http_process_request(ngx_http_request_t *r){ngx_connection_t *c;c = r->connection;#if (NGX_HTTP_SSL)// https 处理...#endif// 删除 timerif (c->read->timer_set) {ngx_del_timer(c->read);}#if (NGX_STAT_STUB)(void) ngx_atomic_fetch_add(ngx_stat_reading, -1);r->stat_reading = 0;(void) ngx_atomic_fetch_add(ngx_stat_writing, 1);r->stat_writing = 1;#endif// 设置读写 handler 为 ngx_http_request_handler, 以便在必要的时候使用c->read->handler = ngx_http_request_handler;c->write->handler = ngx_http_request_handler;r->read_event_handler = ngx_http_block_reading;// 由 ngx_http_handler 处理细节, 下节再看ngx_http_handler(r);}// http/ngx_http_request.c// 读写处理器static voidngx_http_request_handler(ngx_event_t *ev){ngx_connection_t *c;ngx_http_request_t *r;c = ev->data;r = c->data;ngx_http_set_log_request(c->log, r);ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,"http run request: \"%V?%V\"", &r->uri, &r->args);if (c->close) {r->main->count++;ngx_http_terminate_request(r, 0);ngx_http_run_posted_requests(c);return;}if (ev->delayed && ev->timedout) {ev->delayed = 0;ev->timedout = 0;}if (ev->write) {r->write_event_handler(r);} else {r->read_event_handler(r);}ngx_http_run_posted_requests(c);}
大体就是如何使用 recv() 读取数据的过程, 看着流程多, 但实际上其时间复杂度基本为 O(1), 所以效率蛮高的.
3. http 请求的处理
经过数据准备, 数据解析后, 就可以进行逻辑处理了. ngx 中支持许多的功能操作, 如配置内容跳转, 反向代理, 负载均衡 等等. 这些都统一归为一类操作.
voidngx_http_handler(ngx_http_request_t *r){ngx_http_core_main_conf_t *cmcf;r->connection->log->action = NULL;// 设置 keepalive 标识if (!r->internal) {switch (r->headers_in.connection_type) {case 0:r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);break;case NGX_HTTP_CONNECTION_CLOSE:r->keepalive = 0;break;case NGX_HTTP_CONNECTION_KEEP_ALIVE:r->keepalive = 1;break;}r->lingering_close = (r->headers_in.content_length_n > 0|| r->headers_in.chunked);r->phase_handler = 0;} else {cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);r->phase_handler = cmcf->phase_engine.server_rewrite_index;}r->valid_location = 1;#if (NGX_HTTP_GZIP)r->gzip_tested = 0;r->gzip_ok = 0;r->gzip_vary = 0;#endif// 设置写处理器为 ngx_http_core_run_phases, 即开始响应客户端流程r->write_event_handler = ngx_http_core_run_phases;ngx_http_core_run_phases(r);}// http/ngx_http_core_module.c// 响应客户端操作, 多阶段式操作voidngx_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;cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);ph = cmcf->phase_engine.handlers;// 依次调用各 checker, 直到有响应 OK 的checker为止while (ph[r->phase_handler].checker) {// 每次调用 checker 之后, 内部都会将 r->phase_handler++, 即迭代下一个// 此处的 checker 非常之多, 是在各模块启动时, 自动向 ngx_http_core_module.main_conf 中进行注册的/*** 定义如下:typedef enum {NGX_HTTP_POST_READ_PHASE = 0,NGX_HTTP_SERVER_REWRITE_PHASE,NGX_HTTP_FIND_CONFIG_PHASE,NGX_HTTP_REWRITE_PHASE,NGX_HTTP_POST_REWRITE_PHASE,NGX_HTTP_PREACCESS_PHASE,NGX_HTTP_ACCESS_PHASE,NGX_HTTP_POST_ACCESS_PHASE,NGX_HTTP_PRECONTENT_PHASE,NGX_HTTP_CONTENT_PHASE,NGX_HTTP_LOG_PHASE} ngx_http_phases;// 注册方式cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);*h = ngx_http_access_handler;*/// 将请求信息和 handler 本身传入调用(不是面向, 只能这么做了)rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);// 只要有一个处理成功, 则后续不再调用if (rc == NGX_OK) {return;}}}// http/modules/ngx_http_rewrite_module.c// 路径重写模块ngx_int_tngx_http_core_rewrite_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph){ngx_int_t rc;ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"rewrite phase: %ui", r->phase_handler);rc = ph->handler(r);if (rc == NGX_DECLINED) {r->phase_handler++;return NGX_AGAIN;}if (rc == NGX_DONE) {return NGX_OK;}/* NGX_OK, NGX_AGAIN, NGX_ERROR, NGX_HTTP_... */ngx_http_finalize_request(r, rc);return NGX_OK;}static ngx_int_tngx_http_rewrite_handler(ngx_http_request_t *r){ngx_int_t index;ngx_http_script_code_pt code;ngx_http_script_engine_t *e;ngx_http_core_srv_conf_t *cscf;ngx_http_core_main_conf_t *cmcf;ngx_http_rewrite_loc_conf_t *rlcf;cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);index = cmcf->phase_engine.location_rewrite_index;if (r->phase_handler == index && r->loc_conf == cscf->ctx->loc_conf) {/* skipping location rewrite phase for server null location */return NGX_DECLINED;}rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);if (rlcf->codes == NULL) {return NGX_DECLINED;}e = ngx_pcalloc(r->pool, sizeof(ngx_http_script_engine_t));if (e == NULL) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}e->sp = ngx_pcalloc(r->pool,rlcf->stack_size * sizeof(ngx_http_variable_value_t));if (e->sp == NULL) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}e->ip = rlcf->codes->elts;e->request = r;e->quote = 1;e->log = rlcf->log;e->status = NGX_DECLINED;while (*(uintptr_t *) e->ip) {code = *(ngx_http_script_code_pt *) e->ip;code(e);}return e->status;}// http/modules/ngx_http_core_module.cngx_int_tngx_http_core_find_config_phase(ngx_http_request_t *r,ngx_http_phase_handler_t *ph){u_char *p;size_t len;ngx_int_t rc;ngx_http_core_loc_conf_t *clcf;r->content_handler = NULL;r->uri_changed = 0;rc = ngx_http_core_find_location(r);if (rc == NGX_ERROR) {ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);return NGX_OK;}clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);if (!r->internal && clcf->internal) {ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);return NGX_OK;}ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"using configuration \"%s%V\"",(clcf->noname ? "*" : (clcf->exact_match ? "=" : "")),&clcf->name);ngx_http_update_location_config(r);ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"http cl:%O max:%O",r->headers_in.content_length_n, clcf->client_max_body_size);if (r->headers_in.content_length_n != -1&& !r->discard_body&& clcf->client_max_body_size&& clcf->client_max_body_size < r->headers_in.content_length_n){ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,"client intended to send too large body: %O bytes",r->headers_in.content_length_n);r->expect_tested = 1;(void) ngx_http_discard_request_body(r);ngx_http_finalize_request(r, NGX_HTTP_REQUEST_ENTITY_TOO_LARGE);return NGX_OK;}if (rc == NGX_DONE) {ngx_http_clear_location(r);r->headers_out.location = ngx_list_push(&r->headers_out.headers);if (r->headers_out.location == NULL) {ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);return NGX_OK;}r->headers_out.location->hash = 1;ngx_str_set(&r->headers_out.location->key, "Location");if (r->args.len == 0) {r->headers_out.location->value = clcf->name;} else {len = clcf->name.len + 1 + r->args.len;p = ngx_pnalloc(r->pool, len);if (p == NULL) {ngx_http_clear_location(r);ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);return NGX_OK;}r->headers_out.location->value.len = len;r->headers_out.location->value.data = p;p = ngx_cpymem(p, clcf->name.data, clcf->name.len);*p++ = '?';ngx_memcpy(p, r->args.data, r->args.len);}ngx_http_finalize_request(r, NGX_HTTP_MOVED_PERMANENTLY);return NGX_OK;}r->phase_handler++;return NGX_AGAIN;}// http/ngx_http_core_module.cngx_int_tngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph){ngx_int_t rc;/** generic phase checker,* used by the post read and pre-access phases*/ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"generic phase: %ui", r->phase_handler);rc = ph->handler(r);if (rc == NGX_OK) {r->phase_handler = ph->next;return NGX_AGAIN;}if (rc == NGX_DECLINED) {r->phase_handler++;return NGX_AGAIN;}if (rc == NGX_AGAIN || rc == NGX_DONE) {return NGX_OK;}/* rc == NGX_ERROR || rc == NGX_HTTP_... */ngx_http_finalize_request(r, rc);return NGX_OK;}static ngx_int_tngx_http_index_handler(ngx_http_request_t *r){u_char *p, *name;size_t len, root, reserve, allocated;ngx_int_t rc;ngx_str_t path, uri;ngx_uint_t i, dir_tested;ngx_http_index_t *index;ngx_open_file_info_t of;ngx_http_script_code_pt code;ngx_http_script_engine_t e;ngx_http_core_loc_conf_t *clcf;ngx_http_index_loc_conf_t *ilcf;ngx_http_script_len_code_pt lcode;if (r->uri.data[r->uri.len - 1] != '/') {return NGX_DECLINED;}if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {return NGX_DECLINED;}ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);allocated = 0;root = 0;dir_tested = 0;name = NULL;/* suppress MSVC warning */path.data = NULL;index = ilcf->indices->elts;for (i = 0; i < ilcf->indices->nelts; i++) {if (index[i].lengths == NULL) {if (index[i].name.data[0] == '/') {return ngx_http_internal_redirect(r, &index[i].name, &r->args);}reserve = ilcf->max_index_len;len = index[i].name.len;} else {ngx_memzero(&e, sizeof(ngx_http_script_engine_t));e.ip = index[i].lengths->elts;e.request = r;e.flushed = 1;/* 1 is for terminating '\0' as in static names */len = 1;while (*(uintptr_t *) e.ip) {lcode = *(ngx_http_script_len_code_pt *) e.ip;len += lcode(&e);}/* 16 bytes are preallocation */reserve = len + 16;}if (reserve > allocated) {name = ngx_http_map_uri_to_path(r, &path, &root, reserve);if (name == NULL) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}allocated = path.data + path.len - name;}if (index[i].values == NULL) {/* index[i].name.len includes the terminating '\0' */ngx_memcpy(name, index[i].name.data, index[i].name.len);path.len = (name + index[i].name.len - 1) - path.data;} else {e.ip = index[i].values->elts;e.pos = name;while (*(uintptr_t *) e.ip) {code = *(ngx_http_script_code_pt *) e.ip;code((ngx_http_script_engine_t *) &e);}if (*name == '/') {uri.len = len - 1;uri.data = name;return ngx_http_internal_redirect(r, &uri, &r->args);}path.len = e.pos - path.data;*e.pos = '\0';}ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,"open index \"%V\"", &path);ngx_memzero(&of, sizeof(ngx_open_file_info_t));of.read_ahead = clcf->read_ahead;of.directio = clcf->directio;of.valid = clcf->open_file_cache_valid;of.min_uses = clcf->open_file_cache_min_uses;of.test_only = 1;of.errors = clcf->open_file_cache_errors;of.events = clcf->open_file_cache_events;if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)!= NGX_OK){if (of.err == 0) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,"%s \"%s\" failed", of.failed, path.data);#if (NGX_HAVE_OPENAT)if (of.err == NGX_EMLINK|| of.err == NGX_ELOOP){return NGX_HTTP_FORBIDDEN;}#endifif (of.err == NGX_ENOTDIR|| of.err == NGX_ENAMETOOLONG|| of.err == NGX_EACCES){return ngx_http_index_error(r, clcf, path.data, of.err);}if (!dir_tested) {rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);if (rc != NGX_OK) {return rc;}dir_tested = 1;}if (of.err == NGX_ENOENT) {continue;}ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,"%s \"%s\" failed", of.failed, path.data);return NGX_HTTP_INTERNAL_SERVER_ERROR;}uri.len = r->uri.len + len - 1;if (!clcf->alias) {uri.data = path.data + root;} else {uri.data = ngx_pnalloc(r->pool, uri.len);if (uri.data == NULL) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}p = ngx_copy(uri.data, r->uri.data, r->uri.len);ngx_memcpy(p, name, len - 1);}return ngx_http_internal_redirect(r, &uri, &r->args);}return NGX_DECLINED;}static ngx_int_tngx_http_static_handler(ngx_http_request_t *r){u_char *last, *location;size_t root, len;ngx_str_t path;ngx_int_t rc;ngx_uint_t level;ngx_log_t *log;ngx_buf_t *b;ngx_chain_t out;ngx_open_file_info_t of;ngx_http_core_loc_conf_t *clcf;if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {return NGX_HTTP_NOT_ALLOWED;}if (r->uri.data[r->uri.len - 1] == '/') {return NGX_DECLINED;}log = r->connection->log;/** ngx_http_map_uri_to_path() allocates memory for terminating '\0'* so we do not need to reserve memory for '/' for possible redirect*/last = ngx_http_map_uri_to_path(r, &path, &root, 0);if (last == NULL) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}path.len = last - path.data;ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,"http filename: \"%s\"", path.data);clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);ngx_memzero(&of, sizeof(ngx_open_file_info_t));of.read_ahead = clcf->read_ahead;of.directio = clcf->directio;of.valid = clcf->open_file_cache_valid;of.min_uses = clcf->open_file_cache_min_uses;of.errors = clcf->open_file_cache_errors;of.events = clcf->open_file_cache_events;if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)!= NGX_OK){switch (of.err) {case 0:return NGX_HTTP_INTERNAL_SERVER_ERROR;case NGX_ENOENT:case NGX_ENOTDIR:case NGX_ENAMETOOLONG:level = NGX_LOG_ERR;rc = NGX_HTTP_NOT_FOUND;break;case NGX_EACCES:#if (NGX_HAVE_OPENAT)case NGX_EMLINK:case NGX_ELOOP:#endiflevel = NGX_LOG_ERR;rc = NGX_HTTP_FORBIDDEN;break;default:level = NGX_LOG_CRIT;rc = NGX_HTTP_INTERNAL_SERVER_ERROR;break;}if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {ngx_log_error(level, log, of.err,"%s \"%s\" failed", of.failed, path.data);}return rc;}r->root_tested = !r->error_page;ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);if (of.is_dir) {ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");ngx_http_clear_location(r);r->headers_out.location = ngx_list_push(&r->headers_out.headers);if (r->headers_out.location == NULL) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}len = r->uri.len + 1;if (!clcf->alias && r->args.len == 0) {location = path.data + root;*last = '/';} else {if (r->args.len) {len += r->args.len + 1;}location = ngx_pnalloc(r->pool, len);if (location == NULL) {ngx_http_clear_location(r);return NGX_HTTP_INTERNAL_SERVER_ERROR;}last = ngx_copy(location, r->uri.data, r->uri.len);*last = '/';if (r->args.len) {*++last = '?';ngx_memcpy(++last, r->args.data, r->args.len);}}r->headers_out.location->hash = 1;ngx_str_set(&r->headers_out.location->key, "Location");r->headers_out.location->value.len = len;r->headers_out.location->value.data = location;return NGX_HTTP_MOVED_PERMANENTLY;}#if !(NGX_WIN32) /* the not regular files are probably Unix specific */if (!of.is_file) {ngx_log_error(NGX_LOG_CRIT, log, 0,"\"%s\" is not a regular file", path.data);return NGX_HTTP_NOT_FOUND;}#endifif (r->method == NGX_HTTP_POST) {return NGX_HTTP_NOT_ALLOWED;}rc = ngx_http_discard_request_body(r);if (rc != NGX_OK) {return rc;}log->action = "sending response to client";r->headers_out.status = NGX_HTTP_OK;r->headers_out.content_length_n = of.size;r->headers_out.last_modified_time = of.mtime;if (ngx_http_set_etag(r) != NGX_OK) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}if (ngx_http_set_content_type(r) != NGX_OK) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}if (r != r->main && of.size == 0) {return ngx_http_send_header(r);}r->allow_ranges = 1;/* we need to allocate all before the header would be sent */b = ngx_calloc_buf(r->pool);if (b == NULL) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));if (b->file == NULL) {return NGX_HTTP_INTERNAL_SERVER_ERROR;}rc = ngx_http_send_header(r);if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {return rc;}b->file_pos = 0;b->file_last = of.size;b->in_file = b->file_last ? 1: 0;b->last_buf = (r == r->main) ? 1: 0;b->last_in_chain = 1;b->file->fd = of.fd;b->file->name = path;b->file->log = log;b->file->directio = of.is_directio;out.buf = b;out.next = NULL;return ngx_http_output_filter(r, &out);}// http/ngx_http_request.cvoidngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc){ngx_connection_t *c;ngx_http_request_t *pr;ngx_http_core_loc_conf_t *clcf;c = r->connection;ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,"http finalize request: %i, \"%V?%V\" a:%d, c:%d",rc, &r->uri, &r->args, r == c->data, r->main->count);if (rc == NGX_DONE) {ngx_http_finalize_connection(r);return;}if (rc == NGX_OK && r->filter_finalize) {c->error = 1;}if (rc == NGX_DECLINED) {r->content_handler = NULL;r->write_event_handler = ngx_http_core_run_phases;ngx_http_core_run_phases(r);return;}if (r != r->main && r->post_subrequest) {rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc);}if (rc == NGX_ERROR|| rc == NGX_HTTP_REQUEST_TIME_OUT|| rc == NGX_HTTP_CLIENT_CLOSED_REQUEST|| c->error){if (ngx_http_post_action(r) == NGX_OK) {return;}ngx_http_terminate_request(r, rc);return;}if (rc >= NGX_HTTP_SPECIAL_RESPONSE|| rc == NGX_HTTP_CREATED|| rc == NGX_HTTP_NO_CONTENT){if (rc == NGX_HTTP_CLOSE) {c->timedout = 1;ngx_http_terminate_request(r, rc);return;}if (r == r->main) {if (c->read->timer_set) {ngx_del_timer(c->read);}if (c->write->timer_set) {ngx_del_timer(c->write);}}c->read->handler = ngx_http_request_handler;c->write->handler = ngx_http_request_handler;ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc));return;}if (r != r->main) {if (r->buffered || r->postponed) {if (ngx_http_set_write_handler(r) != NGX_OK) {ngx_http_terminate_request(r, 0);}return;}pr = r->parent;if (r == c->data || r->background) {if (!r->logged) {clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);if (clcf->log_subrequest) {ngx_http_log_request(r);}r->logged = 1;} else {ngx_log_error(NGX_LOG_ALERT, c->log, 0,"subrequest: \"%V?%V\" logged again",&r->uri, &r->args);}r->done = 1;if (r->background) {ngx_http_finalize_connection(r);return;}r->main->count--;if (pr->postponed && pr->postponed->request == r) {pr->postponed = pr->postponed->next;}c->data = pr;} else {ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,"http finalize non-active request: \"%V?%V\"",&r->uri, &r->args);r->write_event_handler = ngx_http_request_finalizer;if (r->waited) {r->done = 1;}}if (ngx_http_post_request(pr, NULL) != NGX_OK) {r->main->count++;ngx_http_terminate_request(r, 0);return;}ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,"http wake parent request: \"%V?%V\"",&pr->uri, &pr->args);return;}if (r->buffered || c->buffered || r->postponed) {if (ngx_http_set_write_handler(r) != NGX_OK) {ngx_http_terminate_request(r, 0);}return;}if (r != c->data) {ngx_log_error(NGX_LOG_ALERT, c->log, 0,"http finalize non-active request: \"%V?%V\"",&r->uri, &r->args);return;}r->done = 1;r->read_event_handler = ngx_http_block_reading;r->write_event_handler = ngx_http_request_empty_handler;if (!r->post_action) {r->request_complete = 1;}if (ngx_http_post_action(r) == NGX_OK) {return;}if (c->read->timer_set) {ngx_del_timer(c->read);}if (c->write->timer_set) {c->write->delayed = 0;ngx_del_timer(c->write);}if (c->read->eof) {ngx_http_close_request(r, 0);return;}ngx_http_finalize_connection(r);}
可以看出,nginx处理http流程框架非常简单,即一直遍历执行所有checker,但本身checker数量又比较多,所以给我们的处理带来了复杂度。此处相当于是责任链模式的应用,而且这链是可以被任意注册的,所以也为我们扩展性打开了方便之门。从部分上按一定序执行checker,只要有一个处理完成,即带调用后续checker。
这些checker大体上被分为了几类,body数据读取类,config配置文件读取类,rewrite路径重写类,access权限验证类,content内容处理类,log日志记录类。从总体上是有序的,但对于某类处理,则是任意的。
本文讲解了nginx作为正向代理(http服务器)的处理过程,当然我们可以简单认为是一个文件路径查找的过程。无非就是解析请求头请求体信息,然后查找所有可能的地方,验证可能的权限,然后就输出内容到客户端了。其实和其他的http服务器没啥差别,但nginx的优势在于性能,在于配置的简便性。性能上基于非阻塞io,配置上则已形成自有的一套简洁语法。

腾讯、阿里、滴滴后台面试题汇总总结 — (含答案)
面试:史上最全多线程面试题 !
最新阿里内推Java后端面试题
JVM难学?那是因为你没认真看完这篇文章

关注作者微信公众号 —《JAVA烂猪皮》
了解更多java后端架构知识以及最新面试宝典


看完本文记得给作者点赞+在看哦~~~大家的支持,是作者源源不断出文的动力
作者:等你归去来
出处:https://www.cnblogs.com/yougewe/p/13742238.html
