nginx事件模块之客户端连接与超时管理
2016-12-27 07:26
267 查看
上一篇文章分析了nginx是如何管理监听事件,并把监听事件注册到epoll事件管理器中。接下来在这基础上分析当有客户端连接请求到来时,nginx是如何与客户端建立tcp连接,以及连接建立后又是如何管理超时事件。
一、连接事件管理
在函数ngx_event_process_init中,会设置读事件的回调为ngx_event_accept。 这样设置后,在nginx服务器监听到来自客户端的连接请求后,该回调会被触发,用来与客户端建立tcp连接。连接建立后,就可以正常与客户端进行数据交互。
ngx_event_accept用来接收来自客户端的连接请求。tcp建立后,从连接池中获取一个新连接对象(同时获取到读、写事件), 并把读事件加入到epoll中。这个新连接对象与监听对象作用是不同的。 监听对象注意用来监听来自客户端的连接,是还没有与客户端建立连接前被调用。而这个新连接对象是在tcp连接后被调用,用来与客户端进行数据读写。
在函数中会调用ngx_listening_s对象的handler方法。这个方法其实就是ngx_http_init_connection,在ngx_http_add_listening函数中设置。
ngx_http_init_listening
---> ngx_http_add_listening
--->
nginx服务器在监听到来自客户端的连接请求后,会与客户端建立一个tcp连接,并为这个新连接注册读写事件,并将读事件的超时事件加入到红黑树实现的定时器中。
从上面的ngx_http_init_connection函数中就可以看出这些操作,并把读事件添加到了红黑树实现的定时器中。
ngx_event_add_timer负责将事件注册到红黑树实现的定时器中。红黑树中的所有超时事件节点都是通过ngx_event_s对象的timer成员给串接起来。而定时器中每一个超时事件节点的key就是超时时间,记录该事件的超时时间。
将读事件加入到红黑树定时器后,接下来work进程进入事件循环,阻塞在epoll_wait调用。那epoll_wait什么时候返回呢? 在接收到客户端的数据后,或者每个事件的定时时间到后,可以从epoll_wait返回。接下来看下如何设置epoll_wait的超时时间,使得定时时间到后,能及时从epoll_wait返回。
红黑树是一颗二叉排序树,因此最小超时时间实际上就是左子树的最小值。因此可以看到ngx_event_find_timer函数的实现,就是在左子树种查找最小值。如不清楚红黑树的实现,则可以查看july大神的博客http://www.cnblogs.com/v-July-v/archive/2010/12/29/1983707.html
一、连接事件管理
在函数ngx_event_process_init中,会设置读事件的回调为ngx_event_accept。 这样设置后,在nginx服务器监听到来自客户端的连接请求后,该回调会被触发,用来与客户端建立tcp连接。连接建立后,就可以正常与客户端进行数据交互。
//ngx_event_core_module模块的init_process方法。在函数ngx_worker_process_init中被调用 static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle) { //对于每一个监听端口,从连接池中取出一个连接对象(也将从读时间,写事件池取出对象, //使得连接,读、写保持一一对应关系),负责监听来自客户端的连接 ls = cycle->listening.elts; for (i = 0; i < cycle->listening.nelts; i++) { c = ngx_get_connection(ls[i].fd, cycle->log); //建立连接对象与监听对象的关系 c->listening = &ls[i]; //建立监听对象与连接对象的关系 ls[i].connection = c; rev = c->read; //设置连接回调,当有客户端连接时,将触发回调 rev->handler = ngx_event_accept; //如果work进程之间没有使用枷锁,则把读事件加入epoll中 //此时写事件的回调为NULL,因为在ngx_get_connection函数中会把整个结构进行清0操作 if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) { return NGX_ERROR; } }
ngx_event_accept用来接收来自客户端的连接请求。tcp建立后,从连接池中获取一个新连接对象(同时获取到读、写事件), 并把读事件加入到epoll中。这个新连接对象与监听对象作用是不同的。 监听对象注意用来监听来自客户端的连接,是还没有与客户端建立连接前被调用。而这个新连接对象是在tcp连接后被调用,用来与客户端进行数据读写。
//客户端请求连接回调 void ngx_event_accept(ngx_event_t *ev) { do { //接收客户端连接 s = accept(lc->fd, (struct sockaddr *) sa, &socklen); //work进程之间赋值均衡,但一个work进程超过每一个进程的最大连接数的7/8时, //则该work进程不在监听来自客户端的连接请求。但已经建立tcp连接的客户端不收影响,正常进行数据读写 ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n; //获取一个空闲连接对象(同时也获取到读,写事件) c = ngx_get_connection(s, ev->log); //给新连接对象赋值 c->pool = ngx_create_pool(ls->pool_size, ev->log); c->sockaddr = ngx_palloc(c->pool, socklen); ngx_memcpy(c->sockaddr, sa, socklen); //设置从内核读取数据,写入数据的的公共方法。这些方法实际上就是ngx_os_io结构的各个成员 //这些方法为什么不设置在事件对象上,而是设置在连接对象。因为这对读写事件而言,这些方法是公共的。 //连接对象里面包含了读写事件对象的引用关系,如果设置在相应的读事件,或者写事件上,则每个事件都需要设置一次 //而在连接对象上只需要设置一次 c->recv = ngx_recv; c->send = ngx_send; c->recv_chain = ngx_recv_chain; c->send_chain = ngx_send_chain; //连接对象里面的监听指针指向ls,但ls并没有把连接指向当前已经调用accept的这个连接, //而是指向监听连接对象 c->listening = ls; //调用监听对象的方法, 将读事件写入到epoll //考虑下为什么要把这个回调设置在监听对象上,而不是连接对象上。 //因为如果有5个客户端连接上同一个监听socket, 则会创建5个连接对象。而每一个连接对象都需要设置 //这个回调,占用4字节指针空间,浪费内存资源。而如果回调设置在监听对象上,则只需要设置一次回调就可以了。 ls->handler(c); //ngx_http_init_connection } while (ev->available); }
在函数中会调用ngx_listening_s对象的handler方法。这个方法其实就是ngx_http_init_connection,在ngx_http_add_listening函数中设置。
ngx_http_init_listening
---> ngx_http_add_listening
--->
//创建一个ngx_listening_t对象,并给对象的成员赋值。例如设置监听回调 ngx_listening_t * ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr) { //创建一个ngx_listening_t对象 ls = ngx_create_listening(cf, &addr->opt.u.sockaddr, addr->opt.socklen); //监听回调 ls->handler = ngx_http_init_connection; }ngx_http_init_connection是用来为新建立的客户端连接注册读事件回调ngx_http_init_request、写事件回调ngx_http_empty_handler、同时将注册读事件的超时事件到红黑树实现的定时器中。最终将读事件放入到epoll中。这些操作执行之后,就可以接收来自客户端的数据了。
//在收到客户端连接时,ngx_event_accept函数中会调用ngx_listening_t的handler,也就是本函数 //功能:注册客户端的读写事件回调 void ngx_http_init_connection(ngx_connection_t *c) { rev = c->read; //读事件回调 rev->handler = ngx_http_init_request; //该写回调没有做任何事件,因为这个阶段还不需要向客户端写入任何数据 c->write->handler = ngx_http_empty_handler; //将读事件插入到红黑树中,用于管理超时事件,post_accept_timeout超时事件 //为nginx.conf中的client_header_timeout选项 ngx_add_timer(rev, c->listening->post_accept_timeout); //将读事件注册到epoll中,此时并没有把写事件注册到epoll中,因为现在还不需要向客户端发送任何数据,所以写事件并不需要注册 ngx_handle_read_event(rev, 0); }nginx服务器处理完客户端的连接请求后,又回到了work进程的事件循环中。监听新建立的对象,等待客户端发来的数据,与客户端进行数据交互。
//work进程的事件循环 static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data) { for ( ;; ) { ngx_process_events_and_timers(cycle); } }二、超时事件管理
nginx服务器在监听到来自客户端的连接请求后,会与客户端建立一个tcp连接,并为这个新连接注册读写事件,并将读事件的超时事件加入到红黑树实现的定时器中。
从上面的ngx_http_init_connection函数中就可以看出这些操作,并把读事件添加到了红黑树实现的定时器中。
//在收到客户端连接时,ngx_event_accept函数中会调用ngx_listening_t的handler,也就是本函数 //功能:注册客户端的读写事件回调 void ngx_http_init_connection(ngx_connection_t *c) { rev = c->read; //读事件回调 rev->handler = ngx_http_init_request; //该写回调没有做任何事件,因为这个阶段还不需要向客户端写入任何数据 c->write->handler = ngx_http_empty_handler; //将读事件插入到红黑树中,用于管理超时事件,post_accept_timeout超时事件 //为nginx.conf中的client_header_timeout选项 ngx_add_timer(rev, c->listening->post_accept_timeout); //将读事件注册到epoll中 ngx_handle_read_event(rev, 0); }
ngx_event_add_timer负责将事件注册到红黑树实现的定时器中。红黑树中的所有超时事件节点都是通过ngx_event_s对象的timer成员给串接起来。而定时器中每一个超时事件节点的key就是超时时间,记录该事件的超时时间。
//将定时事件添加到红黑树中,timer为超时时间 static ngx_inline void ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer) { ngx_msec_t key; ngx_msec_int_t diff; key = ngx_current_msec + timer; //已经将事件插入到红黑树种,则先删除之前的事件 if (ev->timer_set) { ngx_del_timer(ev); } //设置定时器的唯一id,也就是时间 ev->timer.key = key; //插入到红黑树种 ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer); //表示事件已经存在红黑树中了 ev->timer_set = 1; }
将读事件加入到红黑树定时器后,接下来work进程进入事件循环,阻塞在epoll_wait调用。那epoll_wait什么时候返回呢? 在接收到客户端的数据后,或者每个事件的定时时间到后,可以从epoll_wait返回。接下来看下如何设置epoll_wait的超时时间,使得定时时间到后,能及时从epoll_wait返回。
//work进程事件循环 void ngx_process_events_and_timers(ngx_cycle_t *cycle) { //在红黑树中查找所有事件的最小超时事件,返回值timer就是所有事件的最小超时时间 timer = ngx_event_find_timer(); //调用epoll_wait等待事件 (void) ngx_process_events(cycle, timer, flags); //epoll_wait返回后,处理所有超时事件 ngx_event_expire_timers(); }
红黑树是一颗二叉排序树,因此最小超时时间实际上就是左子树的最小值。因此可以看到ngx_event_find_timer函数的实现,就是在左子树种查找最小值。如不清楚红黑树的实现,则可以查看july大神的博客http://www.cnblogs.com/v-July-v/archive/2010/12/29/1983707.html
//返回红黑树中最小事件的超时事件; //返回值:>0 表示还剩多长事件超时 // <=0 表示事件已经超时 ngx_msec_t ngx_event_find_timer(void) { ngx_msec_int_t timer; ngx_rbtree_node_t *node, *root, *sentinel; //红黑树为空,则返回-1表示事件已经超时 if (ngx_event_timer_rbtree.root == &ngx_event_timer_sentinel) { return NGX_TIMER_INFINITE; } root = ngx_event_timer_rbtree.root; sentinel = ngx_event_timer_rbtree.sentinel; //查找左字数 node = ngx_rbtree_min(root, sentinel); //计算剩余超时时间 timer = (ngx_msec_int_t) node->key - (ngx_msec_int_t) ngx_current_msec; return (ngx_msec_t) (timer > 0 ? timer : 0); }而epoll_wait调用返回后,如果有事件超时了,那如何处理这些超时事件呢?ngx_event_expire_timers内部会遍历红黑树,查找所有已经超时的事件,并调用这些超时事件的处理回调。需要注意的是,函数也会从红黑树中删除这个超时事件,因此如果还需要管理这个超时事件,则需要重新把事件添加到红黑树实现的定时器中。
//调用红黑树中所有已经超时的事件回调,并把已经超时的事件从红黑树中删除 void ngx_event_expire_timers(void) { ngx_event_t *ev; ngx_rbtree_node_t *node, *root, *sentinel; sentinel = ngx_event_timer_rbtree.sentinel; //遍历红黑树,查找超时事件 for ( ;; ) { root = ngx_event_timer_rbtree.root; //红黑树为空则返回 if (root == sentinel) { return; } //取出红黑树中时间最小的节点 node = ngx_rbtree_min(root, sentinel); /* node->key <= ngx_current_time */ //发生超时 if ((ngx_msec_int_t) node->key - (ngx_msec_int_t) 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; //标示事件已经超时 ev->timedout = 1; //调用事件回调 ev->handler(ev); //直接处理下一个超时事件,前一个超时事件已经从红黑树中删除了 continue; } //没有事件超时则直接退出,因此最小时间都没有超时,那红黑树中其它时间也肯定没有超时 break; } }到此,超时事件的管理也分析完成了。
相关文章推荐
- Nginx学习之十-超时管理(定时器事件)
- nginx模块学习一 http_stub_status_module 客户端连接状态
- Nginx 事件驱动模块连接处理
- Nginx学习之十-超时管理(定时器事件)
- Nginx学习之十-超时管理(定时器事件)
- Nginx学习之十-超时管理(定时器事件)
- nginx源码学习——Http连接对应的事件驱动模块
- Nginx学习之十-超时管理(定时器事件)
- Nginx学习之十-超时管理(定时器事件)
- live555 接收客户端连接及rtsp交互---网络连接处理及RTSP连接模块
- (转)Red5的一般客户端连接各个事件的触发顺序
- nginx 源码学习笔记(二十一)—— event 模块(二) ——事件驱动核心ngx_process_events_and_timers
- 详细解释:nginx中事件模块(Events Module)配置及各个参数含义 .
- 详细解释:nginx中事件模块(Events Module)配置及各个参数含义 .
- 高性能Web服务器Nginx的配置与部署研究(8)核心模块之事件模块
- nginx 的限制连接模块limit_zone与limit_req_zone
- live555 接收客户端连接及rtsp交互---网络连接处理及RTSP连接模块 .
- Oralce 远程客户端连接,提示Ora-12170 TNS操作超时
- 在客户端配置TNS测试报错ORA-12170:TNS:连接超时
- 服务器端如何管理很多个客户端的socket连接?