这次详细分析一下各个模型的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。