Nginx入门
- 前言
- Nginx介绍
- 没有好用的?那就自己做一个!
- Nginx的发展历程
- Nginx的特性(为什么要用Nginx)
- 异步事件驱动
- 同步事件驱动
- 同步事件驱动的问题
- 异步事件驱动
- 异步非阻塞与同步非阻塞
- 并发和并行
- I/O多路复用
- Nginx请求处理的基本流程
- 协作式切换
- 进程?线程?协程?
- 反向代理
- 正向代理
- 反向代理
- 其他的核心特性
前言
日常开发中接触到Nginx
,但是CSDN
上没搜到讲得比较清楚的文章
,索性自己从网上搜集信息整理了此文
。本文内容仅仅用于简单介绍Nginx起源与背景,以及部分特性
,如果需要更细致地了解请参考官网内容。
Nginx介绍
Nginx
(发音为"engine X
")是一个开源的高性能、轻量级的Web服务器和反向代理服务器
。它最初由 伊戈尔·赛索耶夫(gor Sysoev)
于2004
年编写,目的是解决C10k
问题(即同时处理上万个并发连接
)。Nginx以其出色的性能和高度可靠性
而闻名,成为许多网站和应用程序的首选服务器
。
没有好用的?那就自己做一个!
没错这就是这位大佬 – 伊戈尔·赛索耶夫(gor Sysoev)
的想法
2002
年他在俄罗斯的Rambler.ru
站点工作,这是一个访问量非常高
的网站,每天有数百万
的用户使用。他发现当时的Web服务器,如Apache,不能很好地处理大量的并发请求
,因为它们使用多线程或多进程的模型
,会消耗大量的内存和CPU资源。他想要一个更轻量级、更高效、更可扩展的Web服务器
,而这也就是后来广为人知的Nginx
。在利用业余时间
开发的两年以后,Nginx
在2004
年闪亮登场,很快就受到了广泛的关注和使用,成为了最流行的Web服务器之一。
Nginx的发展历程
大部分时候都是一番风顺
,除了创始人被逮进去了
,不是删库跑路
,而是因为知识产权
。
-
2004
年:Igor Sysoev开始编写Nginx,并在同年发布了初始版本
。最初的目标是解决C10k
问题(即同时处理上万个并发连接
)。 -
2005
年:Nginx在俄罗斯
的许多大型网站上开始得到广泛应用
。它以其卓越的性能和高度可靠性赢得了用户的喜爱。 -
2008
年:Nginx发布了第一个稳定版本1.0
。这标志着Nginx进入了成熟的阶段,并受到了全球范围内更多开发者和运维人员的关注。 -
2011
年:Nginx成为全球最流行的Web服务器之一
。它在互联网公司、大型网站和高流量应用中被广泛采用。 -
2013
年:Nginx公司成立
,以支持和推广Nginx的发展。该公司提供商业支持、培训和咨询服务
,并持续推动Nginx的创新和改进。 -
2014
年:Nginx发布了开源的反向代理和负载均衡软件NGINX Plus
。NGINX Plus提供了一些高级功能,如动态加载模块、健康检查和高级负载均衡算法
等。 -
2015
年:Nginx发布了1.9.0
版本,引入了HTTP/2协议
的支持。HTTP/2是HTTP协议的新一代标准,具有更高的性能和效率
。 -
2018
年:F5 Networks宣布收购Nginx
,将两者合并为一家公司。这进一步加强了Nginx在应用交付和负载均衡领域的地位
。 -
2020
年:Nginx发布了Nginx Unit,这是一款轻量级的应用服务器,支持多种语言和框架,用于构建现代的应用程序
。同年12月,
Nginx创始人伊戈尔·赛索耶夫和另一名员工马克西姆·科诺瓦洛夫在莫斯科被警方带走
,原因是他们涉嫌侵犯
了他们曾经任职过的公司Rambler的知识产权
。Rambler声称Nginx是赛索耶夫在担任其员工时创建的,因此拥有所有Nginx源代码的版权。
(也就是我在你家公司工作,我自己业余时间搭建的网站也是你的?
有点强盗逻辑的赶脚了)这一事件引起了业界和社区的广泛关注和抗议,认为这是一场不公正的诉讼,是对开源软件和程序员的打压。 -
2021
年:Nginx发布了NGINX App Platform
,这是一个集成了NGINX Plus、NGINX App Protect、NGINX Unit、NGINX Controller
等多个组件的应用交付平台,可以帮助用户快速构建、部署、管理和保护应用和API 。
随着时间的推移,Nginx不断演进和改进,加入了许多新特性和功能,以满足不断变化的Web应用程序需求。它已经成为互联网基础设施中的关键组件之一,被广泛应用于构建高性能、可扩展的Web服务器和反向代理服务器
。
Nginx的特性(为什么要用Nginx)
下面介绍了基础版Nginx
的部分特性,最新版本请参考官网。
-
高性能
:Nginx以异步事件驱动的方式
(这个后文会详细说说)处理连接和请求,使其能够处理大量并发连接而不会过度消耗系统资源
。这使得Nginx非常适合高负载的Web环境
,能够处理大量的并发请求
。 -
负载均衡
:Nginx
可以作为反向代理服务器
(关于什么是反向代理后文会详细说说),将传入的请求
分发到多个后端服务器,实现负载均衡
。通过使用负载均衡算法,如轮询、IP哈希、最小连接数
等,Nginx能够有效地分发请求
,并提高系统的可伸缩性和可用性。 -
反向代理
:Nginx
充当客户端和后端服务器
之间的中间层
,接收客户端请求并将其转发到后端服务器,然后将响应返回给客户端
。这种反向代理的架构可以提供安全性、负载均衡和缓存
等功能。 -
静态文件服务
:Nginx
可以高效地提供静态文件(如HTML、CSS、JavaScript和图像文件)的服务。
它可以处理大量的并发静态请求
,并使用内存缓存
来提高性能。 -
SSL/TLS支持
:Nginx
内置了对SSL和TLS的支持,可以提供加密的HTTPS连接
。它可以用作安全的Web服务器,通过配置SSL证书和密码学参数来保护传输的数据。 -
可扩展性
:Nginx
具有模块化的架构,允许用户根据需要添加自定义功能和第三方模块
。这使得Nginx可以根据特定的需求进行灵活的定制和扩展。 -
可靠性和稳定性
:Nginx
以其出色的稳定性而著名
。它能够处理大量的并发连接,而且即使在高负载下也能保持良好的响应速度
。此外,Nginx还具有热部署功能
,可以在不中断服务的情况下进行配置更改和软件更新。
以下是Nginx官网的部分截图:
使用Nginx的理由:
架构图:
Nginx生态图(解决方案图):
这图我自己看了都晕,没事过两眼就行了,现在我们来具体了解下上面说到的异步事件驱动以及反向代理
的概念,这二者也是Nginx的核心。
异步事件驱动
上文的Nginx
的高性能中提到了异步事件驱动(Asynchronous Event-Driven)
,大家可能会觉得陌生,再在引入这个概念之前,我们可以先了解一下与之相对的,传统Web服务器(这里以老版本的Apache
举例)使用的同步事件驱动(Synchronous Event-Driven)
。
稍微提一句这里的事件
是指:程序中某种特定情况的发生
,例如用户输入、网络连接就绪、定时器到期等。
需要注意的是一个请求并不代表一个事件
,一般来说在Nginx中,一个请求会被拆分为多个事件
,例如以下几种常见的事件类型
:
连接事件
:当一个客户端发起一个TCP连接
时,Nginx会触发一个连接事件
,负责建立和管理该连接。读事件
:当一个客户端发送一个HTTP请求时
,Nginx会触发一个读事件
,负责读取和解析该请求。写事件
:当Nginx需要向客户端发送一个HTTP响应时
,Nginx会触发一个写事件
,负责生成和发送该响应。定时器事件
:当Nginx需要对某些连接或请求进行超时处理时
,Nginx会触发一个定时器事件
,负责关闭或重试该连接或请求。
同步事件驱动
在传统的Apache
的同步事件驱动
中,每个客户端连接都会被分配给一个独立的线程或进程来处理
。当一个请求到达时,服务器会为其单独分配一个线程或进程
,并在处理完
该请求后再转向下一个请求
,但这种方法使得服务器在处理请求期间
是同步阻塞
的。
同步阻塞是一种编程模型
当发起一个请求时,
程序会等待请求的结果返回,期间不能执行其他操作,(注意这句)
直到结果返回后才能继续后续的操作。为了大家对与异步、同步、阻塞、非阻塞的区别,这里我再贴一个异步阻塞的解释异步阻塞:
当发起一个请求时,
程序不会立即等待请求的结果返回,而是安排一个以后的时间再去获取结果。(注意这句)
当到了那个时间点时,程序会等待请求的结果返回,
期间不能执行其他操作,直到结果返回后才能继续后续的操作。例如,设置一个定时器,在一定时间后再去读取文件,当定时器触发时,调用一个阻塞的read函数来读取文件,
程序会一直等待文件内容返回,不能做其他事情。
下面我举个通俗的例子来更好地解释同步事件驱动,把传统服务器如Apache的同步事件模型
可以类比为一个餐厅
,每个请求对应餐厅的一个顾客
。
每个客人进来餐厅都会让一个服务员为他们全程服务,从点菜到离开
这个过程中需要这个服务员全程服务
,该服务员在等待客人点菜、等待菜品做好、等待客人吃完等过程中是无法同时为其他客人提供服务的
。
同步事件驱动的问题
发现问题所在没有?比如有的客人进来,服务员来了,他说:我还没想好吃什么,让我想一下。
于是服务员只能在一旁等着
,即使这个时候有别的客人来了
,服务员也无法给他服务
。
这种同步事件驱动
的缺点是在面对大量并发连接时
,需要创建和管理大量的线程或进程,这会消耗大量的系统资源,并且可能导致性能下降
。类比到餐厅,假如有太多的客人同时到来,但是服务员有限,而且还会遇到很多那种没想好吃什么的客人,此时服务员只能等没想好的客人,而准备好吃什么的客人也只等待。
在这种同步事件驱动模型
中,每个请求都会占用一个线程或进程
,而且在请求处理期间,该线程或进程会一直被阻塞,无法同时处理其他请求
。当并发连接数增加时,创建和管理大量的线程或进程
会带来额外的开销,并且系统的性能可能受到限制。
异步事件驱动
了解完同步事件阻塞,我们就比较容易理解异步事件驱动了。
在Nginx
中,异步事件驱动
是一种处理并发连接的方法
,通过异步非阻塞
的方式处理并发连接。下面将介绍该方法中涉及到的一些名词解释。
异步非阻塞与同步非阻塞
异步非阻塞:当发起一个请求时,
程序不会立即等待请求的结果返回,也不会安排一个以后的时间再去获取结果。
而是在发起请求后,继续执行其他操作,当请求的结果返回时,
程序会通过某种方式(如回调函数、信号量等)来通知程序,(注意这句)
程序再根据结果进行后续的操作。例如,调用一个非阻塞的read函数来读取文件,程序不会等待文件内容返回,也不会定时去检查文件是否读取完毕,
而是在文件内容返回时,触发一个回调函数来处理文件内容,程序可以做其他事情。同步非阻塞:
当发起一个请求时,
程序不会等待请求的结果返回,而是继续执行其他操作
但是程序会周期性地去检查请求的结果是否返回,如果返回了,就进行后续的操作,(注意这句)
如果没有返回,就继续检查。例如,调用一个非阻塞的read函数来读取文件,程序不会等待文件内容返回,而是在执行其他操作的同时,
不断地去询问文件是否读取完毕(注意这句)
如果读取完毕,就处理文件内容,如果没有读取完毕,就继续询问。
可以理解为:
它使用了少量的固定的工作进程 - Woker Process
(通常只有几个,用于处理事件,可以理解为上图中的服务员),每个工作进程可以同时处理多个并发连接(或者说事件也可以)
。
并发和并行
这里注意并发和并行的区别,并发:一个处理器短时间内交替执行多个任务并行:多个处理器同时执行自己任务
异步事件驱动
的核心之一是Nginx
使用了操作系统提供的I/O多路复用机制
(如epoll、kqueue
等),通过监听事件的发生
来实现高效的并发处理。
I/O多路复用
I/O多路复用机制:一种用于提高系统性能和效率的编程模型或机制。它允许一个进程同时监视多个I/O事件(如套接字的读写操作)的发生 (注意这一句)
并在事件就绪时进行处理,而不需要为每个事件创建一个独立的线程或进程。 (注意这一句)使用该机制可以在一个线程中同时监视多个I/O事件的状态,并在事件就绪时进行处理,
从而避免了线程阻塞等待。
Nginx请求处理的基本流程
现在我们可以通过Nginx处理一个请求的流程
来更加深入的了解以下异步事件驱动,Nginx接收到请求的处理如下:
当有连接请求到达时,Nginx不会
立即为其创建一个专门的线程或进程,而是将其拆分为多个事件添加到队列(不一定是同一个队列)中等待处理
。
再往下讲之前我们先了解一些基本概念:
-
事件循环
:负责监听队列中的事件。 -
协程
:一种轻量级的线程(后面会详细解释) -
事件处理器
:用于解析和处理事件
一个事件的处理过程
可以简述为以下阶段:
处理
阶段:工作进程通过事件循环不断地检查事件
,当发现一个可执行的事件
,会从协程池
将其分配给一个可用的协程来执行。协程会使用事件处理器来执行相应的处理逻辑
,并根据需要进行挂起和恢复
操作。
完成
阶段:当协程执行完毕后,它会将处理结果写入到适当的输出缓冲区中
,如发送响应给客户端的缓冲区。同时,协程会通知工作进程
该事件已经处理完毕,并准备接收下一个事件。
回收
阶段:工作进程定期检查各个协程的状态
,包括检查输出缓冲区是否有待发送的数据。如果发现协程处理完毕并准备好通知
,工作进程会相应地处理,例如将数据发送给客户端。完成后,事件处理器会回收事件,释放相关资源
。
以下图片以读事件为例:
以下内容是对上述事件处理过程一些更加细致的说明
。
协作式切换
说到协程,就不免说一下协作式切换
,因为在Nginx中工作进程执行事件的方式
默认就是协作式切换
。当然还有别的执行方式,比如比较出名的时间片轮转
。
协作式切换:如果事件是协程化的,即可以被挂起和恢复,
那么工作进程可以使用协作式切换的方式来执行各个事件,
即每个事件在遇到阻塞时,主动让出CPU给其他事件,以提高并发性和资源利用率。什么叫协程化呢?可以简单理解为使用协程去执行事件。
进程?线程?协程?
那什么叫协程
呢?是不是看着眼熟?跟进程和线程
很像啊。它们之间确实有联系,我们简单区分下。
-
进程(Process)
:进程是操作系统中资源分配的基本单位
,由操作系统控制
。每个进程都有独立的内存空间和系统资源
,可以通过进程间通信(IPC)进行数据交换。进程之间相互独立
,每个进程有自己的地址空间,需要操作系统的支持进行进程切换,切换代价很大
。 -
线程(Thread)
:线程是在进程内部执行的轻量级执行单位
,由操作系统控制
。一个进程
可以包含多个线程
,用于执行不同的任务,它们共享进程的资源
,如内存空间和打开的文件。线程之间的切换相对较快
,不需要像进程切换那样昂贵的上下文切换开销。 -
协程(Coroutine)
:协程是一种用户级的轻量级线程
,也被称为协作式多任务
,由程序控制
。与线程不同,协程的调度和切换是由程序显式控制的
,而不是由操作系统进行抢占式调度。协程之间合作协作执行,需要在适当的时机显式地让出执行权。协程的切换开销较小
,可以更高效地利用系统资源。
简单来说,进程是资源分配的基本单位,线程是执行的基本单位,协程是比线程更轻量级的执行单位,具有更小的切换开销和更高的灵活性
。
由于Nginx默认是使用协作式切换的方式来执行事件
的,所以当一个事件被激活
时,事件循环会将该事件分配给一个空闲的协程来执行
,而自己继续监听新的事件
。
当一个协程遇到阻塞时,它会主动让出CPU给其他协程继续执行
。
当阻塞解除时,它会恢复原来的状态,继续执行。这样可以避免阻塞和等待,提高并发性能和资源利用率。
这里的阻塞可能不一定是资源未准备好,比如I/O操作
,也可能是事件执行时间较长或者有更重要的事件需要执行
等情况。
这种方式的好处是,Nginx能够高效地处理大量的连接请求,因为它不需要为每个请求都创建一个新的线程或进程
。相反,它使用少量的工作进程来处理多个请求,提高了系统的性能和资源利用率
。
反向代理
了解完异步事件驱动
,我们再来聊聊反向代理
。当然了,在此之前我们照例先说说什么是正向代理
,以方便了解。
正向代理
在追查黑客IP的电影中,追踪的最后经常可以听到这样的台词:是通过代理服务器连接的 无法追踪地址。这里的代理服务器就是
正向代理。
如果你之前了解过代理服务器
,就比较好理解。
在上图的客户请求先发送到代理服务器
,然后再由代理服务器转发到具体的服务器
,相应的话也是先返回给代理服务器,然后再由代理服务器返回给客户。这种代理方式,我们称之为正向代理
。
这里正向是指代理服务器代表用户
去与服务器端交互。
在这一过程中,客户知道代理服务器的存在
,而服务器却不知道客户的信息
,只知道代理服务器的信息。所以这种访问方式(正向代理
)可以隐藏客户端的真实IP地址和标识,使目标服务器无法直接识别请求的源
。当然了,正向代理的特点不仅仅只能隐藏用户信息,还可以优化性能,使用缓存以更快速的响应等功能
,这里我就不展开说了。
反向代理
在上面的正向代理的描述中我们说到代理服务器代表的是用户
,那么反向就好理解了:代理服务器代表的是服务器
。
也就是上面的图变成以下这样:
可以看到,用户的请求先是给到了代理服务器
,然后由代理服务器决定转发给哪个服务器
。在此过程中,用户并不知道他的请求是先发送给代理服务器再发送给服务器处理的,也就是在这过程中,用户并不知道代理服务器的存在
。
而转发给哪个服务器这个过程也就是我们上面提到的:Nginx使用反向代理实现负载均衡
。当然了,用户请求具体转发给那个服务器,还是由具体的算法及服务器信息决定
。
其他的核心特性
当然除了上面讲到异步事件驱动和反向代理
,Nginx还有别的核心特性,如负载均衡、缓存加速、API网关
等等,这里就不一一展开说了,感兴趣的自行了解,相信了解完上面的两个特性,其他的理解起来就比较容易了。
删删减减又写了快一万字,也不知道有没有写错,如果上文中有错误的地方,大家可以评论区留言,十分感谢。