淘宝的某位大佬曾经做过测试,在一台24G内存的机器上,Nginx的最大并发连接数达到了200万。同学们听到这个结论后,是不是被Nginx的超高性能深深折服了,它内部的架构设计究竟是怎么样的呢?这篇文章就带同学们来认识一下Nginx的架构设计吧。

本文主要参考了淘宝技术团队写的Nginx文章,将会从以下个方面去进行分享:

  • Nginx进程模型
  • Nginx事件模型

Nginx进程模型

Nginx默认以多进程的方式启动运行,当然Nginx也是支持多线程的方式的,只是我们主流的方式还是多进程的方式,也是Nginx的默认方式。Nginx采用多进程的方式有很多的好处,这一点与我们大部分同学们的认知发生冲突了。大部分同学会说进程比线程重,进程更耗费资源等等,总之可以说出一大堆线程优于进程的证明。如果有同学恰恰是这样想的,那就得扩充下认知啦。比如我们大名鼎鼎的Redis,Oracle里边都是有多进程概念的 ,而这些软件无一例外都具有超高性能。所以,我们今天就主要来学习下Nginx的多进程模式。

Nginx的进程模型长个什么样子,废话不多说,直接上图。

Nginx在启动后,会有一个master进程和多个worker进程。master进程主要用来管理worker进程,包含:接收来自操作系统的信号,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。而基本的网络事件,则是放在worker进程中来处理了。多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其它worker进程的请求。worker进程的个数是可以设置的,一般设置与机器cpu核数一致,这里面的原因与Nginx的进程模型以及事件处理模型是分不开的。

Nginx的多进程模型给它带来了一大优点: 优雅重启,服务不间断。具体它是怎么做到呢?从上面我们已经得知,master管理worker进程,所以我们只需要与master进程通信就行了。master进程会接收来自操作系统发来的信号,再根据信号做不同的事情。所以我们要控制Nginx,只需要通过操作系统提供的kill命令向master进程发送信号就行了。比如使用kill -HUP pid重启Nginx,master进程在接收到HUP信号后是怎么做的呢?首先master进程在接到信号后,会先重新加载配置文件,然后再启动新的worker进程,并向所有老的worker进程发送信号,告诉他们可以光荣退休了。新的worker在启动后,就开始接收新的请求,而老的worker在收到来自master的信号后,就不再接收新的请求,并且在当前进程中的所有未处理完的请求处理完成后,再退出。

我们知道了在操作Nginx的时候,Nginx内部做了些什么事情,那么,worker进程又是如何客户端处理请求的呢?我们前面有提到,worker进程之间是平等的,每个进程,处理请求的机会也是一样的。当我们提供80端口的http服务时,一个连接请求过来,每个进程都有可能处理这个连接,怎么做到的呢?首先,每个worker进程都是从master进程fork过来,在master进程里面,先创建好ServerSocket,开启监听,然后再fork出多个worker进程。所有worker进程都将和master进程持有同一份socket句柄,并且句柄会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有worker进程在注册句柄读事件前抢accept_mutex,抢到互斥锁的那个进程注册句柄读事件,在读事件里调用accept接受该连接。当一个worker进程在accept这个连接之后,就生成了一个新的socket,通过这个socket开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,一个完整的请求就是这样玩完了。我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。

那么,Nginx采用这种进程模型有什么好处呢?实在是太多了,重点说上几条。首先,对于每个worker进程来说,独立的进程,不需要加锁,所以省掉了锁带来的开销,同时在编程以及问题查找时,也会方便很多。其次,采用独立的进程,可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中断,master进程则很快启动新的worker进程。当然,worker进程的异常退出,肯定是程序有bug了,异常退出,会导致当前worker上的所有请求失败,不过不会影响到所有请求,所以降低了风险。当然,好处还有很多,同学们可以慢慢体会。

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);
}

好的,文章到这里就结束啦。希望Nginx的进程模型和事件模型的设计思想,能对朋友们以后的系统设计有所帮助。最后,喜欢我的文章的同学们,欢迎关注我的公众号“小瑾守护线程”,不错过任何有价值的干货。

Nginx架构赏析的更多相关文章

  1. Nginx学习笔记(一) Nginx架构

    Nginx架构 Nginx全程是什么? Nginx ("engine x") 是一个高性能的 HTTP 和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器. ...

  2. Nginx架构的企业级应用

    Nginx架构的企业级应用 ==================================================== 实现HA高可用集群 实现LB负载均衡集群 Nginx实现反向代理 ...

  3. [转载] 深入 nginx 架构

    原文: http://www.cnbeta.com/articles/402709.htm 了解 nginx 架构帮助我们学习如何开发高性能 web 服务. 为了更好地理解设计,你需要了解NGINX是 ...

  4. nginx架构与基础概念

    1       Nginx架构 Nginx 高性能,与其架构有关. Nginx架构: nginx运行时,在unix系统中以daemon形式在后台运行,后台进程包含一个master进程和多个worker ...

  5. Nginx从入门到放弃-第5章 Nginx架构篇

    5-1 Nginx常见问题_架构篇介绍 5-2 Nginx常见问题_多个server中虚拟主机读取的优先级 5-3 Nginx常见问题_多个location匹配的优先级1 5-4 Nginx常见问题_ ...

  6. 关于nginx架构探究(1)

    nginx的架构主要是有一个主监控进程:master;三个工作进程:worker:还有Cache的两个进程.back-end-server是后端服务器,主要是处理后台逻辑.nginx作为代理服务器需要 ...

  7. 转:初探nginx架构(一)

    来源:http://tengine.taobao.org/book/chapter_02.html 众所周知,nginx性能高,而nginx的高性能与其架构是分不开的.那么nginx究竟是怎么样的呢? ...

  8. Nginx架构分析(20200202)

    Nginx模块化 Nginx基于模块化设计,每个模块是一个功能实现,分布式开发,团队协作 核心模块.标准HTTP模块.可选HTTP模块.邮件模块.第三方模块 编译后的源码目录objs/ngx_modu ...

  9. 采用flask+uwsgi+nginx架构将flask应用程序部署在腾讯云

    最近一星期加班为学校做了一个教师发展中心平台,在此总结一下部署经验 环境:Centos7.0  python2.7.5 1.安装nginx 命令行输入指令:sudo yum install nginx ...

随机推荐

  1. Jupyter Notebook在多个虚拟环境配置与使用

    1 问题描述 使用Anaconda配置了包括Pytorch.Tensorflow等多个虚拟环境后,依然无法使用Jupyter Notebook选择不同的虚拟环境运行代码,问题如下图所示. 2 解决方法 ...

  2. 2020软件测试工程师面试题汇总(内含答案)-看完BATJ面试官对你竖起大拇指!

    测试技术面试题 1.什么是兼容性测试?兼容性测试侧重哪些方面? 参考答案: 兼容测试主要是检查软件在不同的硬件平台.软件平台上是否可以正常的运行,即是通常说的软件的可移植性. 兼容的类型,如果细分的话 ...

  3. RabbiMQ重新安装会遇到的错误-SpiritMark

    这里只做安装过程中遇到错误的介绍,不喜勿喷,如果对您有帮助右上角关注一下,是对我最大的肯定 重新安装的注意事项: 先卸载RabbitMQ,后卸载Erlang RabbitMQ卸载,选择uninstal ...

  4. 在matlab 画箭头

    [转载]在matlab 画箭头 原文地址:在matlab 画箭头作者:纯情小郎君 完整见链接http://www.mathworks.com/matlabcentral/fx_files/14056/ ...

  5. Python批量创建word文档(1)- 纯文字

    Python创建word文档,任务要求:小杨在一家公司上班,每天都需要给不同的客户发送word文档,以告知客户每日黄金价格.最后贴上自己的联系方式.代码如下: 1 ''' 2 #python根据需求新 ...

  6. Impala的count(distinct QUESTION_ID) 与ndv(QUESTION_ID)

    在impala中,一个select执行多个count(distinct col)会报错,举例: select C_DEPT2, count(distinct QUESTION_BUSI_ID) as ...

  7. ajax上传单个文件

    jsp页面 <%@ page language="java" pageEncoding="UTF-8"%> <!DOCTYPE HTML> ...

  8. Java Object to Class

    User user = new User(username,password); //添加构成JWT的参数 JwtBuilder builder = Jwts.builder().setHeaderP ...

  9. Sqoop(四)增量导入、全量导入、减量导入

    增量导入 一.说明 当在生产环境中,我们可能会定期从与业务相关的关系型数据库向Hadoop导入数据,导入数仓后进行后续离线分析.这种情况下我们不可能将所有数据重新再导入一遍,所以此时需要数据增量导入. ...

  10. VRP CommandLines

    <> 用户视图 通过 system-view 进入系统视图 [] 系统视图 通过interface 0/0/0 进入接口视图 CTRL+Z 返回用户视图 CTRL+A 把光标移动到当前命令 ...