事件驱动程序nginx如何只用2个工作进程处理高并发请求?

时间:2011-06-06 09:28:47

标签: nginx software-design

我们知道nginx没有线程,默认情况下只有2个工作进程。

我们也知道accept()会阻止,直到有新请求出现:

        s = accept(lc->fd, (struct sockaddr *) sa, &socklen);

如何同时处理2个以上的请求,基本上2进程并发运行2以上的例程?

有人可以用一些伪代码来解释这个吗?

1 个答案:

答案 0 :(得分:6)

技巧是它在第二个进程中使用非阻塞IO。有关激励非阻塞IO服务器的一些背景知识,我建议您阅读the c10k problem网站。俱佳。

无论如何,第二个进程将向内核注册其对可读事件可写事件错误事件的兴趣与非阻止IO接口:select(2)poll(2)epoll(4)或类似内容。 select(2)最容易谈论,所以我们走了。首先,使用SOCK_NONBLOCK打开所有套接字,以确保套接字上的操作不会阻塞。其次,有一个控制循环正在等待客户端套接字上的活动:

fd_set *f
FD_CLR(f)
foreach client socket
    FD_SET(socket, f)

n_ready_to_read = select(max(socketvalue), f, NULL, f, 0)

for (i=0; i<max(socketvalue) && n_ready_to_read; n_ready_to_read--, i++)
    if FD_ISSET(i, f)
        handle_input_from_client(i)
}

这只是一个草图,说明当客户端发送数据时进程如何发现,而在读取时不会阻塞。因此套接字上的read(2)调用不会阻塞。当然,由于单个服务器同时写入许多客户端,它需要跟踪它写入的套接字,并在准备好接受write(2)调用时向它们发送数据,类似于处理错误。

select_tut(2)中有一个更好,更全面的例子。

select(2)在较新的系统中已基本被替换,因为在创建要检查的fds列表时会产生大量开销,fd_set在Linux上实现为1024位位,并且出于ABI原因永远不会超越这个,因为没有好的方法告诉内核你只对该列表的稀疏子集感兴趣,它必须每次从0- 检查,即使数组没有设置很多fds。

因此,它被替换为允许从长期存储集添加和删除特定文件描述符的机制,允许边缘触发和级别触发的“就绪”通知,并且不执行对大型的愚蠢搜索固定大小的数组,用于查找您感兴趣的文件描述符。

libevent库为此过程提供了一个很好的抽象层,可以在各种平台上自动选择最佳可用接口,并允许程序员专注于特定于他们的代码服务器