淘宝的某位大佬曾经做过测试,在一台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. TP学习—第一天:框架的简单学习;创建应用;

    一.框架目录文件的介绍     common  核心函数库目录 conf   框架的核心配置文件 lang   语言包目录 library   核心资源库目录 tpl 不用管,就是几个模板  Thin ...

  2. java_day_02

    一.return的两个作用 1.停止当前方法 2.将后面的结果数据返回值还给调用处 二.方法的三种调用格式 1.单独调用:方法名(参数): public class Method { public s ...

  3. HTTP Error 405.0 - Method Not Allowed 无法显示您正在查找的页面,因为使用了无效方法(HTTP 谓词)。

    将submit改成button即可   因为触发了form表单

  4. 图解 Java 数据结构

    图解Java数据结构: 一.链表       Java ListNode     https://www.cnblogs.com/easyidea/p/13371863.html 二.栈       ...

  5. 线程上下文类加载器(Context ClassLoader)

    1.线程上下文类加载器是从jdk1.2开始引入的,类Thread中的getContextClassLoader()与setContextClassLoader(ClassLoader c1),分别用来 ...

  6. springboot项目父依赖管理

    springboot项目,pom文件中,要引用父pom文件,进而使用其依赖关系 例如: <parent> <groupId>org.springframework.boot&l ...

  7. 腾讯IOT安卓开发初探

    目录 腾讯IOT 安卓开发初探 Tecent IOT 开发平台的使用 新建项目 创建产品 添加自定义功能 设备开发 微信小程序配置 面板配置 新建设备 使用设备 安卓开发 前置配置 data.json ...

  8. flume集成hdfs(hdfs开启kerberos认证)

    )当 sink 到 hdfs 时: ) 需修改 flume-env.sh 配置,增添 hdfs 依赖库: FLUME_CLASSPATH="/root/TDH-Client/hadoop/h ...

  9. java使用正则的例子

    package com.accord.util; import java.util.ArrayList; import java.util.List; import java.util.regex.M ...

  10. vue element 表单验证不通过,滚动到固对应位置

    我们在使用elementIUI实现表单验证,内容比较多的时候,提示内容会被遮挡,这时候用户不清楚什么情况,还会连续点击提交按钮.这个时候需求来啦:我们需要在表单验证不通过的时候,页面滚动到对应的位置. ...