struct ngx_connection_s { void *data; // 将要关联的模型,listening, request, ... 或其它 ngx_event_t *read; // 读事件 ngx_event_t *write; // 写事件 ngx_socket_t fd; // 句柄 ngx_listening_t *listening; // 对应的监听 }; struct ngx_event_s { void *data; // 将要关联的模型,connection, ... 或其它 unsigned write:1; // 是否可写 unsigned accept:1; // 是否是accept产生的事件 unsigned instance:1; // 避免惊群的一个设计 unsigned active:1; // 是否有效,当加入epoll_ctl时就置为1 unsigned ready:1; // epoll_wait捕获到时就置为1 unsigned timedout:1; // 是否超时 unsigned timer_set:1; // 是否置为定时器,即加入超时定时器红黑树时就置为1 ngx_event_handler_pt handler; // 事件处理函数,核心 ngx_rbtree_node_t timer; // 加入红黑树时需要的辅助节点 };
struct ngx_cycle_s { ... ngx_array_t listening; // 是个数组,结构体为ngx_listening_s ... } struct ngx_listening_s { ngx_socket_t fd; // 句柄描述符 struct sockaddr *sockaddr; socklen_t socklen; ... };监听是有读事件,而没有写事件的,epoll有两个模式LT和ET,监听采用LT,监听的read事件的处理函数为ngx_event_accept。
struct ngx_http_request_s { uint32_t signature; /* "HTTP" */ ngx_connection_t *connection; // 对应的连接 /* 这个结构体是非常庞大的,但不复杂,比如它处理了并重新保存了配置文件的上下文 */ void **ctx; void **main_conf; void **srv_conf; void **loc_conf; /* 比如请求有关的信息的会保存到它的成员里 */ u_char *uri_start; u_char *uri_end; u_char *uri_ext; u_char *args_start; u_char *request_start; u_char *request_end; u_char *method_end; u_char *schema_start; u_char *schema_end; u_char *host_start; u_char *host_end; u_char *port_start; u_char *port_end; unsigned http_minor:16; unsigned http_major:16; };
ngx_event_expire_timers(void) { ... for ( ;; ) { node = ngx_rbtree_min(root, sentinel); /* node->key <= ngx_current_time,很简单巧妙的设计,怎么视为超时 */ if ((ngx_msec_int_t) (node->key - ngx_current_msec) <= 0) { ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer)); ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer); ev->timer_set = 0; // 重置timer_set ev->timedout = 1; // 标记为超时 ev->handler(ev); // 马上处理,注意这里没有处理成如果超时就关闭连接,这是由handler自行处理的 // 后面会再解释这个设计 continue; } break; // 如果没有超时的事件,结束退出 } }
for ( ;; ) { timer = ngx_event_find_timer(); events = epoll_wait(ep, event_list, (int) nevents, timer); ;更新时间 ;超时处理 ;正常事件处理 }
ngx_http_init_request(ngx_event_t *rev) { ... if (rev->timedout) { ngx_http_close_connection(c); return; } ... } ngx_http_process_request_line(ngx_event_t *rev) { ... if (rev->timedout) { c->timedout = 1; ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); return; } ... }所以前面提到,在超时检查时,nginx只是将event标记为timedout,而没有关闭连接,这是因为,nginx可以处理
ngx_url_t u; ngx_peer_connection_t peer; ngx_memzero(&u, sizeof(ngx_url_t)); ngx_memzero(&peer, sizeof(ngx_peer_connection_t)); ngx_str_set(&u.url, "127.0.0.1:8080"); ngx_parse_url(pool, &u); peer.sockaddr = u.addrs->sockaddr; peer.socklen = u.addrs->socklen; peer.name = u.addrs->name; peer.get = ngx_event_get_peer; ngx_event_connect_peer(&peer); peer.connection->read->handler = ngx_mail_auth_http_read_handler; peer.connection->write->handler = ngx_mail_auth_http_write_handler; ngx_add_timer(peer.connection->read, ahcf->timeout); ngx_add_timer(peer.connection->write, ahcf->timeout);
ps:写这些文章时,有些代码都是凭经验敲的,有错误之处请指正。能谈到设计上的层次就足矣。
阅读原文:http://nglua.com/reads/4.html