Nginx和Apache有什么不同?
首先
当使用Web服务器时,你经常听到Nginx和Apache这两个词,不是吗?
但是你有没有觉得对它们的区别不是很清楚呢?(如果只是我一个人不知道的话,我很抱歉 笑)
顺便说一下,我对其没有任何根据的印象是“Nginx似乎更受欢迎,更好一些…”。
由于我想更加准确地理解,所以这次我决定将这个区别写成一篇文章。
请务必读完到最后哦!
另外,Nginx和Apache分别读作“エンジンエックス”和“アパッチ”。
首先,Nginx和Apache是什么?
Nginx和Apache是Web服务器软件。
在服务器上安装这些软件,即可具备作为Web服务器的功能。
在2021年12月,Nginx和Apache在Web服务器软件领域拥有绝对的市场份额。

引用:『TECH+ マイナビニュース 12月份网络服务器份额,Apache增加』
結論是,Nginx和Apache之間的差異在哪裡。
Nginx能够更好地处理大量并发访问,相对于Apache来说。
这个问题的关键在于C10K问题。
C10K问题是指能够同时处理1万个连接的难题,如果你搜索一下,会发现有以下的描述。
我们将「即使硬件性能没有问题,但当客户端数量过多时,服务器会因为过载而崩溃的问题」定义为C10K问题。
引用:『技术评论者的博客:C10K问题』
无论是 CPU 还是内存的性能如何,如果客户端数量增多,Web 服务器的处理速度都无法跟得上。为了理解为什么会发生这种情况,我们将深入研究Web服务器的处理过程。
程序的执行方式究竟是怎样的?
由于Web服务器是通过程序实现的,所以首先我会简要介绍程序在服务器上是如何运行的。
当程序开始执行时,首先将其处理内容写入内存。这个处理内容被称为进程。另外,将进程细分为CPU的执行单元称为线程。CPU有一个称为核心的执行处理器,每个核心一次只能处理一个线程(参见下图)。


当进程或线程过多时,会产生哪些问题?
当进程或线程变得过多时,会出现以下问题。
-
- プロセスの上限に達してしまう
-
- ファイルディスクリプタ※の上限の上限に達してしまう
- コンテキストスイッチ※にオーバーヘッドが発生し、負荷が大きくなる
填补
※文件描述符
=> 在程序中操作文件时,为了识别和确定被操作的文件,被分配的编号。
※上下文切换
=> 这是CPU切换处理进程的操作。在处理多个进程时,要保存并恢复CPU的状态(上下文)。在此期间,CPU无法执行任何处理,因此如果上下文切换频繁发生,就会产生额外的开销。
由于像上面提到的问题,就会引发C10K问题。回到Apache和Nginx的问题,Apache会有大量的进程和线程,从而导致C10K问题,但是Nginx巧妙地避免了这个问题。接下来,我们将看一下Apache和Nginx在处理上的区别。
Apache是如何处理访问的?
Apache的处理主要有两种模式,分别是”prefork模式”和”worker模式”。
注意:还有一种称为事件驱动模式的模式,但由于Apache的事件驱动模式与worker模式相似,在此不予赘述。

无论是prefork模型还是worker模型,都有一个主控制进程。主控制进程的子进程用于处理请求。prefork模型会生成多个进程,每个进程只有一个线程。而worker模型会生成多个线程,对应一个进程。

无论是prefork模型还是worker模型,每次收到请求都会创建进程或线程进行处理。
随着访问量的增加,进程或线程的数量也会增加。
这就让人觉得Apache的处理可能会引发C10K问题。

让我们深入了解一下prefork模型和worker模型。
首先,让我们来看看prefork模型的处理。
每个进程只有一个线程,所以会为每个请求生成一个进程。
进程的最大数量※在32位Linux上是32768,在64位Linux上是4M,因此如果有这么多请求,处理进程将会崩溃。
由于CPU需要处理多个进程,因此会频繁发生上下文切换。
关于进程的最大数,所提及的数值是Linux的pid(进程id,具有唯一值)的最大数。由于还有其他进程存在,实际上能容忍的进程数会少于所提及的数值。
当解决方案采用worker模型(多线程)时,上述问题会在一定程度上得到缓解。worker模型允许进程拥有多个线程,这样发起的进程数量就会减少。由于进程数量减少,至少在一定程度上减轻了达到进程上限的压力。此外,同一个进程的线程共享内存空间,这减轻了内存空间切换的负担。
然而,很容易达到文件描述符的上限。
文件描述符的上限是每个进程 1,024。
因此,我们已经了解到,不管是prefork模型还是worker模型,在面临大量访问时,都可能出现C10K问题。
我们已经看了Apache的处理,现在要看Nginx的处理了。
Nginx如何处理访问请求
以下是一个选项:
Nginx的特点是能够在一个进程中处理多个访问(参见下图)。
在主进程下面有几个子进程(工作进程),每个工作进程都处理多个访问。
这是与Apache有明显的不同之处。

能够实现这种处理的机制是非阻塞I/O和事件驱动。
※ Nginx的I/O机制还有另一种异步I/O,但本文只介绍非阻塞I/O。
非阻塞I/O指的是不会阻塞处理的I/O(就是这样,笑)。
如果只有这样的话可能不太理解,所以首先我们来看看阻塞I/O处理时进程的执行流程。
-
- プロセスがブロック状態になる(カーネルからシステムコールの返答が返ってくるまで)
-
- カーネルがI/Oを開始
-
- I/Oが完了したらカーネルは結果を返す
- 処理完了&ブロック解除

在非阻塞I/O的情况下,不会被阻塞。
当向内核发出系统调用时,会转移到其他处理上。
定期查询内核是否完成处理,如果尚未完成,则会产生错误;如果已经完成,则会返回结果(参见下图)。

※ 引用来源:使用异步I/O提升应用性能
通过这样做,可以将内核的输入/输出时间分配给其他处理,并能够执行非常高效的操作。
好的,我们回到Nginx的话题吧!
正如之前提到的,Nginx采用了非阻塞I/O和事件驱动的机制。
非阻塞I/O就是没有阻塞的I/O操作,正如前面所述。
事件驱动是指(在Nginx的情况下)将客户端的访问视为事件,并以此为触发,在进程内执行相应的处理。
假设有两个访问(访问1和访问2)到达。
注意:虽然访问1和访问2几乎同时到达,但访问1稍微早一些到达。
首先收到Access 1时,将在进程内进行处理。
在这种情况下,首先进行accept()操作。
通常情况下,accept()操作会导致阻塞,直到完成,但是Nginx并不会阻塞。
因此,接下来即使有Access 2的accept()操作,也可以开始执行。
read()和write()操作也是同样的道理,可以同时处理Access 1和Access 2而不会阻塞。
根据以上特点,Nginx能够在一个进程中处理多个访问,并且可以应对大量访问。
结束
我們大致比較了Nginx和Apache的區別。
重點在於對於訪問的處理方式不同。
當訪問量增加時,Apache會增加進程數,但Nginx的進程數保持不變。
基於這個特點,Nginx可以處理大量訪問,並且不會引起C10K問題。
僅僅從這一點來看,似乎Nginx更好一些,但Apache也有其優勢。
例如,Apache的prefork模型可以運行PHP。因此,在想要快速啟動環境等情況下,根據個人的需求可能Apache更好。
由於兩者都有優點和缺點,所以我們應根據自己的情況來使用它們!
感谢您阅读到最后!
文献引用
图书
- Nginx実践入門
网络网站
-
- Context Switch(コンテキストスイッチ)とは?
-
- 【Linux】プロセス番号の最大値 Linux カーネルパラメータ
-
- 【必見】ファイルディスクリプタ設定でLinuxをチューニング
-
- いまさら聞けないNode.js
-
- C10K問題
-
- HTTP Serverのプロセス構造(UNIX版)
-
- プロセスの生成
-
- ノンブロッキングI/Oと非同期I/Oの違いを理解する
-
- Boost application performance using asynchronous I/O
-
- Apacheコミッターが見た、Apache vs nginx
-
- 【図解/apache】MPM prefork/worker /event (イベント駆動)の違い~CPUコアの使い方~
-
- Nginxのアーキテクチャを理解する
- Nginxが早い理由について調べた(基礎)