nginx源码分析之事件机制
2014-11-14 11:47
232 查看
事件机制尤如nginx的心脏一般,不停的运转,保证了nginx的请求响应模式得以正常工作。
本文将剖析事件机制的原理和实现。
nginx本身支持多种机制,如 poll, epoll, select, aio, kqueue等,这里分析epoll,因为这是nginx的杀手锏。
初略接触时,我们大概只知道监听、请求、接受、响应这几个概念。我们沿着这个思维展开,看nginx如何设计这些结构体的。
1、大体上设计
不管是监听,还是请求,只要能产生fd的,都将视为连接,一个fd对应一个连接(connection)。
每个连接都可以读(read)和写(write),这两个都视为事件(event),结构体为:
?
2、监听listen
当处理完配置文件解析(针对listen指令)时,nginx开始处理这些listen。将它们放在 ngx_cycle->listening里。
?
监听是有读事件,而没有写事件的,epoll有两个模式LT和ET,监听采用LT,监听的read事件的处理函数为ngx_event_accept。
3、接受accept
这个产生的fd,有读和写事件,对读事件的处理函数为ngx_http_init_request。因此一个连接请求一旦发送完,就从这个函数开始执行
这也是request开始的生命周期,这里的结构体为:
?
4、神奇的超时
因为处理了超时,整个代码的复杂度至少提升了一个档次,像libevent这种东东,它是用信号处理超时的,
nginx作者应该认为这种处理方式不是线程安全的,所以自己实现了一个。这不是重复创造轮子,超时机制
是应用程序的一部分逻辑,在应用程序代码里面实现无可厚非。
超时机制用了红黑树,因为有频繁的插入,查找和删除,用红黑树的效率是非常高的。
初始化:专门的变量ngx_event_timer_rbtree
ngx_event_timer_init(cycle->log);
超时检查:epoll所有事件处理之前,检查一遍哪些是超时的,将event标记为timedout,并且马上执行事件处理
?
5、epoll的应用
一个连接(或事件)它要添加到epoll里,才会被处理,不然即使它可读或可写了,也不会理会。
?
事件操作:
ngx_epoll_add_event
ngx_epoll_del_event
6、梳理
如果我们自己写业务逻辑,如何处理一个事件呢?
假设fd已经有了,可能是你通过socket函数产生的。
获取连接:
c = ngx_get_connection(fd);
处理read,write:
c->read->handler = ngx_http_init_request;
c->write->handler = ngx_http_empty_handler;
定时器处理:定时器是针对事件的
ngx_add_timer(c->read, c->listening->post_accept_timeout);
ngx_add_timer(c->write, ...);
注册事件:即加入epoll,这里一般采用ET模式。
ngx_handle_read_event(c->read, 0);
ngx_handle_read_event(c->write, 0);
上面的例子将read,write都处理了,实际情况不一定得这样,看你要不要处理读或写事件,哪个需要,启用哪个。
7、不能忘却的timedout。
如果你用心,你会发现,所有的event的handler函数体的前面都有一段这么代码
?
所以前面提到,在超时检查时,nginx只是将event标记为timedout,而没有关闭连接,这是因为,nginx可以处理
http, mail或不同的连接,每个连接都有自己不同的处理方式,所以这段代码无处不在,将就咯 -_-
8、你看得懂这段代码吗?
?
这是截取mail部分auth_http的代码,nginx用的很广的一个就是自己创建socket连接到另一服务器,
像fastcgi, proxy,都是这样,里面的核心就是 ngx_event_connect_peer,这主题比较深,留以后专门分析,抛下砖头先。
本文将剖析事件机制的原理和实现。
nginx本身支持多种机制,如 poll, epoll, select, aio, kqueue等,这里分析epoll,因为这是nginx的杀手锏。
初略接触时,我们大概只知道监听、请求、接受、响应这几个概念。我们沿着这个思维展开,看nginx如何设计这些结构体的。
1、大体上设计
不管是监听,还是请求,只要能产生fd的,都将视为连接,一个fd对应一个连接(connection)。
每个连接都可以读(read)和写(write),这两个都视为事件(event),结构体为:
?
当处理完配置文件解析(针对listen指令)时,nginx开始处理这些listen。将它们放在 ngx_cycle->listening里。
?
3、接受accept
这个产生的fd,有读和写事件,对读事件的处理函数为ngx_http_init_request。因此一个连接请求一旦发送完,就从这个函数开始执行
这也是request开始的生命周期,这里的结构体为:
?
因为处理了超时,整个代码的复杂度至少提升了一个档次,像libevent这种东东,它是用信号处理超时的,
nginx作者应该认为这种处理方式不是线程安全的,所以自己实现了一个。这不是重复创造轮子,超时机制
是应用程序的一部分逻辑,在应用程序代码里面实现无可厚非。
超时机制用了红黑树,因为有频繁的插入,查找和删除,用红黑树的效率是非常高的。
初始化:专门的变量ngx_event_timer_rbtree
ngx_event_timer_init(cycle->log);
超时检查:epoll所有事件处理之前,检查一遍哪些是超时的,将event标记为timedout,并且马上执行事件处理
?
一个连接(或事件)它要添加到epoll里,才会被处理,不然即使它可读或可写了,也不会理会。
?
ngx_epoll_add_event
ngx_epoll_del_event
6、梳理
如果我们自己写业务逻辑,如何处理一个事件呢?
假设fd已经有了,可能是你通过socket函数产生的。
获取连接:
c = ngx_get_connection(fd);
处理read,write:
c->read->handler = ngx_http_init_request;
c->write->handler = ngx_http_empty_handler;
定时器处理:定时器是针对事件的
ngx_add_timer(c->read, c->listening->post_accept_timeout);
ngx_add_timer(c->write, ...);
注册事件:即加入epoll,这里一般采用ET模式。
ngx_handle_read_event(c->read, 0);
ngx_handle_read_event(c->write, 0);
上面的例子将read,write都处理了,实际情况不一定得这样,看你要不要处理读或写事件,哪个需要,启用哪个。
7、不能忘却的timedout。
如果你用心,你会发现,所有的event的handler函数体的前面都有一段这么代码
?
http, mail或不同的连接,每个连接都有自己不同的处理方式,所以这段代码无处不在,将就咯 -_-
8、你看得懂这段代码吗?
?
像fastcgi, proxy,都是这样,里面的核心就是 ngx_event_connect_peer,这主题比较深,留以后专门分析,抛下砖头先。
相关文章推荐
- ExtJs源码分析与学习—ExtJs事件机制(五)
- Nginx源码分析-事件驱动的初始化
- ExtJs源码分析与学习—ExtJs事件机制(二)
- Android源码分析-点击事件派发机制
- Cocos2d-X3.0 刨根问底(七)----- 事件机制Event源码分析
- 4(phonegap源码分析)通道模块的事件订阅机制(channel)
- Nginx源码分析-事件驱动的初始化
- ExtJs源码分析与学习—ExtJs事件机制(四)
- Android源码分析-点击事件派发机制
- nginx的源码分析--间接回调机制的使用和类比
- ExtJs源码分析与学习—ExtJs事件机制(四)
- rt-thread的IPC机制之事件源码分析
- 文章5:Nginx源码分析--事件循环
- Android源码分析-点击事件派发机制
- Nginx源码分析-事件循环
- 分析Nginx源码中的事件循环
- 分析源码,探究AWT事件处理机制
- Nginx源码分析-事件循环
- nginx源码分析--event事件驱动初始化
- Android源码分析-点击事件派发机制