Nginx事件模型
Nginx采用了NIO的方式来处理请求,这是Nginx可以同时处理成千上万个请求的根本原因。想想低版本Tomcat的IO模型(高版本已支持NIO),每个请求会独占一个工作线程,当并发数上到几千时,就同时有几千的线程在处理请求了。这对操作系统来说,是个不小的挑战,线程带来的内存占用非常大,线程的上下文切换带来的cpu开销很大,自然性能就上不去了,而这些开销完全是没有意义的。
Nginx为什么要使用NIO呢?NIO到底是怎么回事呢?IO的本质就是读写事件,而当读写事件没有准备好时,必然不可操作,如果不用非阻塞的方式来调用,那就得阻塞调用了,事件没有准备好,那就只能等了,等事件准备好了,工作线程再继续吧。阻塞调用会进入内核等待,cpu就会让出去给别人用了,工作线程只能傻傻的睡觉等待,对单线程的worker来说,显然不合适,当网络事件越多时,大家都在等待呢,cpu空闲下来没人用,cpu利用率自然上不去了,更别谈高并发了。所以,为了高性能,必须使用NIO。关于NIO,我们这里就是一笔带过,想详细学习的同学可以去看我的NIO源码分析文章。
接下来,我们使用一段伪代码来总结一下Nginx的事件处理模型吧。
Date now; // 表示当前时间
while (true) {
// 处理任务队列里的所有任务
for task in tasks:
task.handler();
flushTime(now); // 刷新当前时间
timeout = initValue; // 超时时间
// waitTasks可以理解为注册到epoll里的所有有效任务
for task in waitTasks:
// 列表里的第一个task的超时时间是最短的,如果它已经超时了,就调用超时处理函数
if (task.time <= now) {
task.timeoutHandler();
} else {
// 更新超时时间
timeout = t.time - now;
break;
}
// 通过epoll拿到已经就绪的事件
nevents = epoll(events, timeout);
// 挨个遍历处理事件
for i in nevents:
Task task;
// 读事件
if (events[i].type == READ) {
task.handler = readHandler;
} else { // 写事件
task.handler = writeHandler;
}
// 防到一个专门的执行队列里
tasks(task);
}
USB Microphone https://www.soft-voice.com/
Wooden Speakers https://www.zeshuiplatform.com/
亚马逊测评 www.yisuping.cn
深圳网站建设www.sz886.com