Nginx具有一系列的模块,包括HTTP模块,核心模块和mail模块等。简要分析一下一些具有代表性模块的原理。
event模块
event模块的主要功能是监听accept后建立的连接,对读写事件进行添加删除。事件处理模型和Nginx的非阻塞IO模型结合在一起使用。当IO可读可写的时候,相应的读写时间就会被唤醒,此时就会去处理事件的回调函数。
对于Linux,Nginx使用的是epoll
。epoll
是pool
和select
的增强版本。
select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符(fd
),一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。
所以epoll本质是同步IO + 阻塞的。
select模型的调用流程如下:
epoll对select模型和poll模型有诸多改进。
select用O(n)
的效率不断地去查看那些fd,效率太低。而epoll通过在内核中提供callback机制的方式,在内部使用链表把O(n)
降到了O(1)
,讨论参见SO。
我们看看怎么实现callback机制的。
当为一个用户建立一个socket连接时,调用epoll的current进程会加入到驱动的wait_queue
中。当其它用户连接时,进行同样的处理。所以current进程在所有驱动的等待队列中。
当IO没有就绪,调用read
函数会阻塞在这一步。一旦只要有一个用户连接的IO就绪,current进程就会被唤醒。
与select相比,epoll加入了callback这个hock,记录了这个唤醒者,避免了在current进程醒着的时候查询到底是谁唤醒了我。
##timer模块 先看一段代码,timer什么时候起作用。
void Request::run()
{
keep_alive_timer = new QTimer();
if (s_keep_alive_enable)
{
keep_alive = s_keep_alive_default;
keep_alive_timeout = s_keep_alive_timeout * 1000;// the wait time
connect(keep_alive_timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
keep_alive_timer->setSingleShot(true);
keep_alive_timer->setInterval(keep_alive_timeout);
keep_alive_timer->start();
}
//a new thread
socket = new QTcpSocket();
if (!socket->setSocketDescriptor(socketDescriptor))
return;
connect(socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()), Qt::DirectConnection);
connect(socket, SIGNAL(disconnected()), this, SLOT(onDisconnected()), Qt::DirectConnection);
exec();
}
代码出自一个简单的http服务器。它新建了一个定时器,然后计算自连接开始后的时间。主要用来判断连接是否timeout。
Nginx使用红黑树来构造定时器。定时器的机制就是,二叉树的值是其超时时间,每次查找二叉树的最小值,如果最小值已经过期,就删除该节点,然后继续查找,直到所有超时节点都被删除。我们知道,自平衡二叉搜索树rbtree的树节点中,最左边叶子节点(或根节点)所代表的那个定时器的超时时间是最小的,因此只需要O(1)
时间删除它就可以。
##参考
http://www.cnblogs.com/Anker/p/3265058.html
http://www.cnblogs.com/Anker/p/3254269.html
http://www.pento.cn/pin/31146170
http://blog.csdn.net/ce123_zhouwei/article/details/8459730
http://blog.csdn.net/v_july_v/article/details/6105630