在nginx中connection就是对tcp连接的封装,其中包括连接的socket,读事件,写事件。利用nginx封装的connection,我们可以很方便的使用nginx来处理与连接相关的事情,比如,建立连接,发送与接受数据等。而nginx中的http请求的处理就是建立在connection之上的,所以nginx不仅可以作为一个web服务器,也可以作为邮件服务器。
结合TCP连接的生命周期来看,nginx在启动时,会解析配置文件,得到需要监听的端口与ip地址,然后在nginx的master进程里面,先初始化好这个监控的socket(创建socket,设置addrreuse等选项,绑定到指定的ip地址端口,再listen),然后再fork出多个子进程出来,然后子进程会竞争accept新的连接。此时,客户端就可以向nginx发起连接了。当客户端与服务端通过三次握手建立好一个连接后,nginx的某一个子进程会accept成功,得到这个建立好的连接的socket,然后创建nginx对连接的封装,即ngx_connection_t结构体。接着,设置读写事件处理函数并添加读写事件来与客户端进行数据的交换。最后,nginx或客户端来主动关掉连接,到此,一个连接就寿终正寝了。
ngx_connection_t结构体
//服务器的被动连接struct ngx_connection_s {//连接未使用时候,data域充当连接链表中的next指针.//当连接被使用时候,data域的意义由模块而定.void *data;//连接对应的读事件ngx_event_t *read;//连接对应的写事件ngx_event_t *write;//套接字句柄ngx_socket_t fd;//直接接收网络字节流的方法ngx_recv_pt recv;//直接放松网络字节流的方法ngx_send_pt send;//以ngx_chain链表为参数,接收网络字节流的方法ngx_recv_chain_pt recv_chain;//以ngx_chain链表为参数,发送网络字节流的方法ngx_send_chain_pt send_chain;//这个链接对应的listening_t监听对象.//此链接由ngx_listening_t监听的事件建立ngx_listening_t *listening;//这个连接已经发送出去的字节数off_t sent;//记录日志ngx_log_t *log;//在accept一个新连接的时候,会创建一个内存池,而这个连接结束时候,会销毁一个内存池.//这里所说的连接是成功建立的tcp连接.内存池的大小由pool_size决定//所有的ngx_connect_t结构体都是预分配的ngx_pool_t *pool;//连接客户端的结构体struct sockaddr *sockaddr;//连接客户端的结构体长度socklen_t socklen;//连接客户端的ip(字符串形式)ngx_str_t addr_text;#if (NGX_SSL)ngx_ssl_connection_t *ssl;
#endif//本机中监听端口对应的socketaddr结构体//也就是listen监听对象中的socketaddr成员struct sockaddr *local_sockaddr;//用于接收和缓存客户端发来的字符流ngx_buf_t *buffer;//该字段表示将该连接以双向链表形式添加到cycle结构体中的//reusable_connections_queen双向链表中,表示可以重用的连接.ngx_queue_t queue;//连接使用次数,每次建立一条来自客户端的连接,//或者建立一条与后端服务器的连接,number+1ngx_atomic_uint_t number;//处理请求的次数ngx_uint_t requests;//unsigned buffered:8;//日志级别unsigned log_error:3; /* ngx_connection_log_error_e *///不期待字符流结束unsigned unexpected_eof:1;//连接超时unsigned timedout:1;//连接处理过程中出现错误unsigned error:1;//标识此链接已经销毁,内存池,套接字等都不可用unsigned destroyed:1;//连接处于空闲状态unsigned idle:1;//连接可以重用unsigned reusable:1;//连接关闭unsigned close:1;//正在将文件中的数据法网另一端unsigned sendfile:1;//连接中发送缓冲区的数据高于低潮,才发送数据.//与ngx_handle_write_event方法中的lowat相对应unsigned sndlowat:1;//使用tcp的nodely特性unsigned tcp_nodelay:2; /* ngx_connection_tcp_nodelay_e *///使用tcp的nopush特性unsigned tcp_nopush:2; /* ngx_connection_tcp_nopush_e */#if (NGX_HAVE_IOCP)unsigned accept_context_updated:1;
#endif#if (NGX_HAVE_AIO_SENDFILE)unsigned aio_sendfile:1;ngx_buf_t *busy_sendfile;
#endif#if (NGX_THREADS)ngx_atomic_t lock;
#endif
};
最大连接数,对于HTTP请求本地资源来说,能够支持的最大并发数量是worker_connections * worker_processes,而如果是HTTP作为反向代理来说,最大并发数量应该是worker_connections * worker_processes/2。
accept机会公平性
首先,nginx的处理得先打开accept_mutex选项,此时,只有获得了accept_mutex的进程才会去添加accept事件,也就是说,nginx会控制进程是否添加accept事件。nginx使用一个叫ngx_accept_disabled的变量来控制是否去竞争accept_mutex锁。
ngx_accept_disabled = ngx_cycle->connection_n / 8- ngx_cycle->free_connection_n;if (ngx_accept_disabled > 0) {ngx_accept_disabled--;} else {if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {return;}if (ngx_accept_mutex_held) {flags |= NGX_POST_EVENTS;} else {if (timer == NGX_TIMER_INFINITE|| timer > ngx_accept_mutex_delay){timer = ngx_accept_mutex_delay;}}
}
在第一段代码中,计算ngx_accept_disabled的值,这个值是nginx单进程的所有连接总数的八分之一,减去剩下的空闲连接数量,得到的这个ngx_accept_disabled有一个规律,当剩余连接数小于总连接数的八分之一时,其值才大于0,而且剩余的连接数越小,这个值越大。再看第二段代码,当ngx_accept_disabled大于0时,不会去尝试获取accept_mutex锁,并且将ngx_accept_disabled减1,于是,每次执行到此处时,都会去减1,直到小于0。不去获取accept_mutex锁,就是等于让出获取连接的机会,很显然可以看出,当空余连接越少时,ngx_accept_disable越大,于是让出的机会就越多,这样其它进程获取锁的机会也就越大。不去accept,自己的连接就控制下来了,其它进程的连接池就会得到利用,这样,nginx就控制了多进程间连接的平衡了。