HTTP 连接的建立 -- ngx_http_init_connection

2015-10-03 20:13:20   最后更新: 2015-10-03 20:13:20   访问数量:1270




在 nginx 连接池的介绍中,我们介绍了 nginx 连接结构 ngx_connection_t:

// struct ngx_connection_s // nginx 连接结构 {{{ struct ngx_connection_s { // 连接未使用时,充当连接池空闲链表中的 next 指针 // 连接使用后,由模块定义其意义 // HTTP 模块中,data 指向 ngx_http_request_t void *data; // 连接对应的读事件 ngx_event_t *read; // 连接对应的写事件 ngx_event_t *write; // 连接 fd ngx_socket_t fd; // 直接接收网络字符流的方法 ngx_recv_pt recv; // 直接发送网络字符流的方法 ngx_send_pt send; // 以链表来接收网络字符流的方法 ngx_recv_chain_pt recv_chain; // 以链表来发送网络字符流的方法 ngx_send_chain_pt send_chain; // 监听对象,此连接由listening监听端口的事件建立 ngx_listening_t *listening; // 这个连接上已发送的字节数 off_t sent; ngx_log_t *log; // 一般在accept一个新的连接时,会创建一个内存池 // 而在这个连接结束时会销毁内存池 // 内存池大小是由 listening 成员的 pool_size 决定的 ngx_pool_t *pool; // 连接客户端的sockaddr struct sockaddr *sockaddr; // sockaddr结构体的长度 socklen_t socklen; // 连接客户段字符串形式的IP地址 ngx_str_t addr_text; // 代理协议地址 ngx_str_t proxy_protocol_addr; #if (NGX_SSL) ngx_ssl_connection_t *ssl; #endif // 本机监听端口对应的sockaddr结构体 struct sockaddr *local_sockaddr; // sockaddr结构体的长度 socklen_t local_socklen; // 用户接受、缓存客户端发来的字符流,分配在连接池中 ngx_buf_t *buffer; // 用来将当前连接以双向链表元素的形式添加到 ngx_cycle_t 核心结构体的 // reuseable_connection_queue 双向链表中,表示可以重用的连接 ngx_queue_t queue; // 连接使用次数 ngx_atomic_uint_t number; // 处理的请求次数 ngx_uint_t requests; // 缓存中业务类型 unsigned buffered:8; // 日志级别 unsigned log_error:3; /* ngx_connection_log_error_e */ // 为1时表示独立的连接,为0表示依靠其他连接行为而建立起来的非独立连接 unsigned unexpected_eof:1; // 为1表示连接已经超时 unsigned timedout:1; // 为1表示连接处理过程中出现错误 unsigned error:1; // 为1表示连接已经销毁 unsigned destroyed:1; // 为1表示连接处于空闲状态,如 keepalive 两次请求中间的状态 unsigned idle:1; // 为1表示连接可重用,与 queue 字段对应使用 unsigned reusable:1; // 为1表示连接关闭 unsigned close:1; // 为1表示正在将文件中的数据发往连接的另一端 unsigned sendfile:1; // 为1表示只有连接套接字对应的发送缓冲区必须满足最低设置的大小阀值时, // 事件驱动模块才会分发该事件 // 这与ngx_handle_write_event方法中的lowat参数是对应的 unsigned sndlowat:1; unsigned tcp_nodelay:2; /* ngx_connection_tcp_nodelay_e */ unsigned tcp_nopush:2; /* ngx_connection_tcp_nopush_e */ unsigned need_last_buf:1; #if (NGX_HAVE_IOCP) unsigned accept_context_updated:1; #endif #if (NGX_HAVE_AIO_SENDFILE) unsigned aio_sendfile:1; unsigned busy_count:2; ngx_buf_t *busy_sendfile; #endif #if (NGX_THREADS) ngx_atomic_t lock; #endif }; // }}}

 

 

在这个结构中,保存了两个事件结构:read 和 write,分别对应该连接对应的读事件和写事件,而事件模块 ngx_event_t 中提供了 ngx_event_handler_pt 回调函数,在事件触发时调用相应的方法

// struct ngx_event_s // 事件描述结构体 {{{ struct ngx_event_s { // 事件上下文数据,指向 ngx_connection_t 结构对象 // 对于文件异步 IO,可能指向 ngx_event_aio_t 结构对象 void *data; // 为 1 表示事件可写 unsigned write:1; // 为 1 表示可以建立新的连接 unsigned accept:1; /* used to detect the stale events in kqueue, rtsig, and epoll */ // 判断当前事件是否过期 unsigned instance:1; /* * the event was passed or would be passed to a kernel; * in aio mode - operation was posted. */ // 为 1 表示当前活跃事件 unsigned active:1; // 用于 kqueue 和 rtsing 事件,是否禁用 unsigned disabled:1; /* the ready event; in aio mode 0 means that no operation can be posted */ // 用于异步IO,当有请求需要处理时置位 unsigned ready:1; // 用于 kqueue 和 eventport unsigned oneshot:1; /* aio operation is complete */ // 用于异步 IO 事件,是否已经完成 unsigned complete:1; // 为 1 表示字符流已结束 unsigned eof:1; unsigned error:1; // 为 1 表示事件已超时 unsigned timedout:1; // 为 1 表示该事件存储在定时器中 unsigned timer_set:1; // 仅用于限速功能中,为 1 表示需要延时处理 unsigned delayed:1; // 为 1 表示 TCP 三次握手后暂时不建立连接,等到真正收到数据包后才建立连接 unsigned deferred_accept:1; /* the pending eof reported by kqueue, epoll or in aio chain operation */ // 只用于 kqueue 和 aio 事件中,为 1 表示字符流结束 unsigned pending_eof:1; unsigned posted:1; #if (NGX_WIN32) /* setsockopt(SO_UPDATE_ACCEPT_CONTEXT) was successful */ unsigned accept_context_updated:1; #endif #if (NGX_HAVE_KQUEUE) unsigned kq_vnode:1; /* the pending errno reported by kqueue */ int kq_errno; #endif /* * kqueue only: * accept: number of sockets that wait to be accepted * read: bytes to read when event is ready * or lowat when event is set with NGX_LOWAT_EVENT flag * write: available space in buffer when event is ready * or lowat when event is set with NGX_LOWAT_EVENT flag * * iocp: TODO * * otherwise: * accept: 1 if accept many, 0 otherwise */ #if (NGX_HAVE_KQUEUE) || (NGX_HAVE_IOCP) int available; #else unsigned available:1; #endif // 事件发生时处理的回调函数 ngx_event_handler_pt handler; #if (NGX_HAVE_AIO) #if (NGX_HAVE_IOCP) ngx_event_ovlp_t ovlp; #else struct aiocb aiocb; #endif #endif ngx_uint_t index; ngx_log_t *log; // 定时器节点 ngx_rbtree_node_t timer; /* the posted queue */ ngx_queue_t queue; // 为 1 表示当前事件已关闭 unsigned closed:1; /* to test on worker exit */ unsigned channel:1; unsigned resolver:1; #if 0 /* the threads support */ /* * the event thread context, we store it here * if $(CC) does not understand __thread declaration * and pthread_getspecific() is too costly */ void *thr_ctx; #if (NGX_EVENT_T_PADDING) /* event should not cross cache line in SMP */ uint32_t padding[NGX_EVENT_T_PADDING]; #endif #endif }; // }}}

 

 

nginx HTTP 框架初始化 中,ngx_http_block 调用 ngx_http_optimize_servers 调用 ngx_http_init_listening 调用 ngx_http_add_listening 创建并初始化了 nginx 监听结构,进行了一步赋值:

ls->handler = ngx_http_init_connection

 

指定了 http 建立连接的回调函数为 ngx_http_init_connection

 

事件驱动函数 ngx_process_events_and_timers 中调用了 ngx_event_process_posted 函数调用了事件的 handler 回调函数 ngx_event_accept,在 ngx_event_accept 函数中,首先调用了 accept 函数,随后在连接建立以后,调用了 nginx 连接结构的 ngx_listening_t 的回调函数

在 nginx HTTP 模块初始化的过程中,ngx_listening_t 的回调函数 handler 已经被初始化为了 ngx_http_init_connection

 

// void ngx_http_init_connection(ngx_connection_t *c) // http 连接建立回调 {{{ void ngx_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 // 创建 http 连接描述结构 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; // 只有当有多个虚拟地址时才需要获取本地 IP 地址 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 */ // 获取本地地址,初始化 c->local_socklen 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; #endif default: /* 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; #endif default: /* 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; 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; // 赋值 http 请求处理回调函数 rev->handler = ngx_http_wait_request_handler; c->write->handler = ngx_http_empty_handler; #if (NGX_HTTP_SPDY) if (hc->addr_conf->spdy) { rev->handler = ngx_http_spdy_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) { c->log->action = "SSL handshaking"; if (hc->addr_conf->ssl && sscf->ssl.ctx == NULL) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "no \"ssl_certificate\" is defined " "in server listening on SSL port"); ngx_http_close_connection(c); return; } hc->ssl = 1; rev->handler = ngx_http_ssl_handshake; } } #endif // 是否使用代理 if (hc->addr_conf->proxy_protocol) { hc->proxy_protocol = 1; c->log->action = "reading PROXY protocol"; } if (rev->ready) { /* the deferred accept(), rtsig, aio, iocp */ if (ngx_use_accept_mutex) { ngx_post_event(rev, &ngx_posted_events); return; } // ngx_http_wait_request_handler rev->handler(rev); return; } // 将事件添加至定时器,以监控收到的连接是否超时 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; } } // }}}

 

 

 

 






技术帖      龙潭书斋      nginx      源码      opensource      sourcecode      http      开源      连接      ngx_http_init_connection      http模块     


京ICP备15018585号