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

com*_*fan 2 nginx software-design

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

而且我们也知道accept()会阻止新请求到来:

        s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
Run Code Online (Sandbox Code Playgroud)

它如何同时处理2个以上的请求,基本上2流程运行的2程序不仅仅是例程?

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

sar*_*old 6

诀窍是它在第二个进程中使用非阻塞IO.有关激活非阻塞IO服务器的一些背景知识,我建议您阅读c10k问题网站.高超.

无论如何,所述第二进程将与内核注册其在感兴趣可读事件,可写事件,和错误事件与非阻塞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)
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

的libevent库提供了一个奇妙的抽象层来此过程中自动选择最佳的可用接口上各种各样的平台,并允许程序员把重点放在具体到他们的服务器的代码.