这次详细分析一下各个模型的connect调用,对于WEB服务器,必然调用accept,但也少不了connect,一般用于连接后端WEB服务器或者邮件服务器
在调用 rc = connect(s, pc->sockaddr, pc->socklen);之后NGX会调用 ngx_add_event来注册connect的事件
ngx_add_event是一个宏对于不同的网络模型会有对应的具体函数,
对于IOCP来说是ngx_iocp_add_event
对于SELECT来说是ngx_select_add_event
对于EPOLL来说是ngx_epoll_add_event
connect的返回值,不同操作系统返回值略有差别。先看看select 模型的处理流程
以http_up_stream模块为例。
ngx_http_upstream_connect函数里面调用ngx_event_connect_peer,将结构体ngx_peer_connection_s传递进去。
ngx_event_connect_peer执行初始化socket,设置收/发缓存,绑定等初始化工作。
r然后调用connect,如果返回NGX_OK,那么最后调用ngx_http_upstream_send_request发送请求。
如果connect返回-1,还要判断errno,如果不是EWOULDBLOCK类似的错误码直接返回。如果是的话则会向select模型中添加读,写句柄。调用的即前面提到的 ngx_select_add_event,
ngx_select_add_event 会把读写FD添加到全局的master_read_fd_set,master_write_fd_set读写符号集里面。这是select的标准动作。剩下的事情则交给select模型。
而循环检查读写状态的是ngx_select_process_events,其关键代码如下:
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) {
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;
}
这里发现读/写 符号就绪以后,就会调用ngx_post_event(ev, queue);把代表单次I/O上下文的ev对象放入全局的队列中,实际上是2个队列ngx_posted_accept_events,ngx_posted_events。
而对ev-handle的执行则是在ngx_worker_process_cycle->ngx_process_events_and_timers->ngx_event_process_posted函数里面,其唯一的调用就是ev->handler(ev);
实际上调用的是ngx_http_upstream_send_request_handler,在这个函数中经过一些处理之后调用ngx_http_upstream_send_request。
如果返回的是NGX_OK,则直接调用ngx_http_upstream_send_request
如果connect处于等待状态,还会调用ngx_event_add_timer,添加一个超时对象。一旦此次connect超时,则会在ngx_process_events_and_timers中触发相应的超时处理函数。
在回头看看ngx_http_upstream_connect函数,有2个参数 ngx_http_request_t和ngx_http_upstream_t
在简单处理了时间方面的记录以后就调用ngx_event_connect_peer,如果返回NGX_ERROR,表示当前socket出现异常,调用ngx_http_upstream_finalize_request关闭这个socket。
如果返回NGX_DECLINED,表示连接对象连接失败,则调用ngx_http_upstream_next继续执行下一个反向代理目标。
如果connect立即成功或者进入异步等待状态,设置读写Handle等一系列参数
c = u->peer.connection;
c->data = r;
c->write->handler = ngx_http_upstream_handler;
c->read->handler = ngx_http_upstream_handler;
u->write_event_handler = ngx_http_upstream_send_request_handler;
u->read_event_handler = ngx_http_upstream_process_header;
c->sendfile &= r->connection->sendfile;
u->output.sendfile = c->sendfile;
一旦异步connect成功返回,则会调用这些读写handle来执行读写操作。
最后如果是异步状态,调用下面的代码
if (rc == NGX_AGAIN) {
ngx_add_timer(c->write, u->conf->connect_timeout);
return;
}
对这个connect建立一个定时器来监控connect的超时,一旦超时则会触发相应的处理函数来撤销这个异步connect。
如果connect立即成功,则调用ngx_http_upstream_send_request,开始向反向代理目标发送http request。