Nginx实现内参:为什么架构很重要?
Nginx在web开发者眼中就是高并发高性能的代名词,其基于事件的架构也被众多开发者效仿。我从Nginx的网站找到一篇技术文章将Nginx是怎样实现的,文章是Nginx的产品老大Owen
Garrett在加入公司22个月时写的,深入简出。这篇博客后面的内容尽量保证是对原文的翻译,如果有个人理解或者延伸阅读我会加标“译注”。原文地址Inside NGINX: How We Designed for Performance & Scale(https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/)
Nginx强劲的高性能表现来自其合理的软件设计。传统的web服务器和应用服务器架构设计上采用多进程或线程作为其处理业务的基本单位,而Nginx更多的使用了事件驱动的架构。正是这种架构使得Nginx可以轻松支持数十万的并发链接。【译注:Nginx相比其他的web服务器使用了更少的进程,将IO事件集中在固定的进程内处理,减少了很多系统开销,可以从下文理解到。】
The Inside NGINX infographic 较为清晰的讲诉了Nginx如何在一个进程内处理并发链接,下面我们深入看一下细节。
Nginx进程模型
在讲设计实现之前,有必要先看一下Ngxin如何在linux之上运行的。Nginx启动会创建一个主进程(主管进程,负责读取配置、绑定端口、管理其他子进程)和一些worker进程和辅助进程。
这个示例运行在4核的server上,Nginx主进程创建4个worker进程和2个cahce辅助进程。
为什么架构很重要?
Unix应用程序的基本要素是进程或者线程。(Linux OS调度不区分进程还是线程,二者的最大区别在于它们对于memory的共享程度。)进程或线程是一个自包含的可以独立运行的任务,OS可以调度它到某个CPU核上执行。有很多复杂的应用程序运行在多进程或线程模式下是基于以下两点考虑:
可以使用更多CPU资源。【译注:还有memory、IO等其他资源】
可以轻松做到并行处理(比如,同时处理多个链接)。
进程和线程都会消耗资源,需要占用memory和其他OS资源,并且在运行时还有context switch的系统开销。一般的server可以负担几百数量级的进程或者线程,当进程或线程数量继续上升到更高的数量级,memory消耗和IO阻塞引起的系统负荷会很高,使得应用程序运行比较低效。
在设计网络程序时,开发者会很自然的设计成每个进程或线程处理一个网络连接。这种架构比较简单容易实现,但是比较难以扩展,尤其是当网络连接增长到上千以后。
Nginx怎样工作的?
Nginx可配置数量的进程,推荐配置数量和CPU的核数量相当:
主进程读配置,绑定端口,然后启动一定数量的子进程。
cache loader子进程在启动的时候运行,负责把硬盘上的数据搬进内存,然后就退出了。因为它是一次性的任务,系统开销很小。
cahce manager子进程启动后监控维护cache区。
worker子进程是真正处理业务的进程,负责处理网络连接,读写硬盘,跟上游server交互等等。
Nginx推荐配置worker的数量跟CPU核数量线性关系,每个CPU核运行一个worker进程。可以通过配置 worker_processes auto来使用该推荐设置。
当Nginx server处理业务时,worker进程们是最繁忙的,每个worker通过非阻塞的IO复用方式处理很多连接,尽量减少不必要的上下文切换。
每个worker进程都是单线程的进程,接收连接上的request并处理后回应。进程间可以通过共享内存的方式进行进程间通信。
Nginx worker进程
每个Nginx worker进程由主进程读取配置创建,通过accept_mutex竞争获得要listen的socket并加入自己的IO监听列表中。
每来一个新的连接都会触发新的事件,这些事件送给worker内的状态机来处理。(Nginx支持各种类型的状态机,如http/tcp/SMTP/IMAP/POP3等)。大部分的web server逻辑上都有这样的状态机,只是实现方式不一样。
状态机调度
我们可以想象类比状态机是象棋游戏的规则。每个HTTP transaction(译注:一组的Http请求,可以对应成某个socket上发生的所有http请求)就是一个象棋游戏。对弈的一方是web server,可以类比为象棋大师。另一方为client,类比为象棋爱好者。
游戏的规则可以很复杂,比如web server需要跟其他application沟通协作完成业务处理,第三方的nginx模块甚至可以扩展规则。
阻塞式状态机
大多数的web服务器和应用程序使用每个连接对应一个进程或线程的模式来玩象棋游戏。每个进程或线程给一个client完成对弈直到游戏结束。在整个过程中,进程大部分时间都是处在阻塞状态--等待client完成下一步走棋。
web服务器主进程在服务端口上监听新的连接(客户端发起的新游戏的请求)。
有新的游戏请求时,主进程创建子进程负责完成跟客户端的对弈。主进程继续监听服务端口。
当游戏结束时,子进程要么等待client开始新游戏(通过keepalive机制保活一段时间连接)要么退出(keepalive超时后)。
这种模型每玩一局server都要创建一个对应的进程来完成对弈。这种架构简单并且容易容易扩展新功能,但有些大炮打蚊子,杀鸡用牛刀的感觉。进程是个重器,系统开销比较大,而解决的问题是个轻量级的问题。容易编程实现但是浪费比较大。
Nginx才是真正的并发大师
你可能听说过一人同时对战多人的象棋大赛
这就是Nginx worker进程的工作方式。每个worker进程(一般每个CPU核有一个worker进程)都是一个象棋大师,可以同时对弈数十万对手。
worker进程等待listen和connection sockets的事件。(译注:listen socket就是server用来监听新建连接的socket,connection socket是accept系统调用返回的新建socket,详细可参加accept的手册)
事件发生后,worker进程来处理这些事件:
listen socket的事件表示有新的客户端要开始新的游戏。worker通过accept()创建新的connection socket,并加入监听列表。
connection socket的事件表示客户端走了一步棋,worker进程可以做下一步应对。
worker进程从不会在网络IO上阻塞,当它应对完客户端的走棋走出自己的一步后,可以马上应对下一个客户端的走棋或接收新的连接请求。
为什么这样做比阻塞式的多进程架构更快?
Nginx的worker进程很容易扩展支持数十万并发连接。每个新接入的连接只需要创建新的socket消耗少量的内存,每个连接的系统开销相对要比进程开销小很多。另外通过Nginx worker进程绑定CPU技术可以进一步减少上下文切换和cache失效等系统开销。
而阻塞式每个进程服务一个连接的方式,每个连接都会消耗很多资源,而且进程切换比较频繁导致系统开销比较大。
更详细的解释,可以参考这篇文章--Nginx架构,作者是Ngxin的VP和共同创始人,Andrew Alexeev.
更新配置和升级Nginx
Nginx的这种少量进程的架构使得更新配置和升级Nginx版本很容易。
更新Ngxin配置是一件非常容易事情而且非常可靠。很简单的nginx -s reload就搞定了。运行这个命令实际上是给Nginx主进程发送了一个SIGHUP的信号,主进程收到该信号后做了两件事情:
重新加载配置并且根据新的配置创建一组新的worker进程,这些新的进程可以马上开始干活。
通知老的worker进程优雅地退出。
重新装载的过程会引起短暂的CPU和内存的使用高峰,但这种影响总体来说比较微小,你甚至可以每秒多次做这个操作。
Nginx程序的升级就更加漂亮了,根本不会影响正在处理的连接,轻轻松松升级完成用户根本没有感觉。
Ngxin程序升级跟更新配置相似。启动新的Nginx主进程,它会跟旧的主进程共享listen sockets。新的进程起来后,你可以发送信号给旧的进程退出。详细过程可以参看Controlling Nginx(http://nginx.org/en/docs/control.html).
总结
The Inside NGINX infographic描述了Nginx的整体功能,其实它概括性描述的背后是Nginx开发人员十几年的创新和优化。如果你想了解更多,可以参看这些材料:
Installing and Tuning Nginx for Performance(https://www.nginx.com/resources/webinars/installing-tuning-nginx/)
Tuning Nginx for Performance(https://www.nginx.com/blog/tuning-nginx/)
The Architecture of Open Source Applications – NGINX(http://www.aosabook.org/en/nginx.html)
Socket Sharding in NGINX Release 1.9.1 (using the SO_REUSEPORT socket option)(https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/)
原文地址:https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/
译文来源:SegmentFault
Nginx实现内参:为什么架构很重要?的更多相关文章
- 浅谈 Nginx 的内部核心架构设计
一.前言 Nginx---Ngine X,是一款免费的.自由的.开源的.高性能HTTP服务器和反向代理服务器:也是一个IMAP.POP3.SMTP代理服务器:Nginx以其高性能.稳定性.丰富的功能. ...
- 初探Nginx服务器的整体架构
高度模块化的设计是 Nginx 的架构基础.Nginx 服务器被分解为多个模块,每个模块就是一个功能模块,只负责自身的功能,模块之间严格遵循“高内聚,低耦合”的原则. 核心模块 核心模块是 Nginx ...
- 从 Nginx 优秀的核心架构设计,揭秘其为何能支持高并发?
目录: 1. Nginx的整体架构 2. Nginx的模块化设计 3. Nginx的请求方式处理 4. Nginx事件驱动模型 5. Nginx进程处理模型 写在前面 Nginx 是一个 免费的,开源 ...
- 企业Nginx+Keepalived双主架构案例实战
通过上一次课程的学习,我们知道Nginx+keepalived主从配置,始终有一台服务器处于空余状态,那如何更好的利用起来呢,我们需要借助Nginx+keepalived双主架构来实现,如下图通过改装 ...
- 构建企业级Nginx+Keepalived集群架构
随着Nginx在国内的发展潮流,越来越多的互联网公司都在使用Nginx. Nginx高性能.稳定性成为IT人士青睐的http和反向代理服务器,今天我们来实战构建Nginx+Keepalived高可用架 ...
- LVS和nginx反向代理网站架构
LVS和nginx反向代理网站架构 nginx反向代理和lvs的dr都存在单点,要keepalived做高可用,但是成本高了 f
- Ansible实战之Nginx代理Tomcat主机架构
author:JevonWei 版权声明:原创作品 实验架构:一台nginx主机为后端两台tomcat主机的代理,并使用Ansible主机配置 实验环境 Nginx 172.16.252.82 Tom ...
- 企业级Nginx服务基础到架构优化详解
1.隐藏nginx header版本号 2.更改源码隐藏软件名称 3.更改nginx默认用户及用户组 4.配置nginx worker进程个数 5.根据CPU核数进行nginx进程优化 6.nginx ...
- Azure环境中Nginx高可用性和部署架构设计
前几篇文章介绍了Nginx的应用.动态路由.配置.在实际生产环境部署时,我们需要同时考虑Nginx的高可用性和部署架构. Nginx自身不支持集群以保证自身的高可用性,商业版本的Nginx+推荐: T ...
随机推荐
- Scrum第一天任务认领情况
在团队项目“广商百货”的SCRUM项目中我认领的任务是对登录注册界面进行完善.具体功能还没有实现,还在学习中...
- tyvj 1049 最长不下降子序列 n^2/nlogn
P1049 最长不下降子序列 时间: 1000ms / 空间: 131072KiB / Java类名: Main 描述 求最长不下降子序列的长度 输入格式 第一行为n,表示n个数第二行n个数 输出格式 ...
- POJ 2253 Frogger
题目链接:http://poj.org/problem?id=2253 Frogger Time Limit: 1000MS Memory Limit: 65536K Total Submissi ...
- ZOJ 1240 IBM Minus One
/* You may have heard of the book '2001 - A Space Odyssey' by Arthur C. Clarke, or the film of the s ...
- jquery节点操作
很久没有jquery写东西了,最近使用jquery的时候发现很多节点的操作都不太熟悉了,于是就进行了一个小小的总结. 创建节点:var dom=$('<div></div>') ...
- spingmvc 返回json数据日期格式化方法
第一种: json 用的是这个依赖 <!-- JSON lib 开发包 以及它的依赖包 --> <dependency> <groupId>com.fasterxm ...
- MATLAB符号运算
1.符号运算 使用MATLAB可以进行多项式乘除运算,也可以进行因式分解. 例1. 多项式乘除运算(x+3)3 >> syms x;>> expand((x+3)^3) ans ...
- eclipse template里面的${user}更改
打开eclipse目录下的eclipse.ini文件,添加上一行 -Duser.name="whateveryouwant" 这样在eclipse中的${user}变量的值就变成了 ...
- psp0
周活动总结表 姓名:苗堃 ...
- kuangbin_UnionFind J (POJ 2492)
加权并查集mod2模板 本体的难点是bug的释义(笑) #include <iostream> #include <string> #include <cstdio> ...