关键要点
几年前,Apache基金会的Web服务器(简称为“Apache”)非常普遍,以至于它成为“Web服务器”一词的同义词。它在Linux系统上的守护进程名为httpd(仅表示http进程),并且预装在主要的Linux发行版中。
它最初于1995年发布,引用维基百科的话说,“它在万维网的初期发展中发挥了关键作用”。根据W3techs的数据,它仍然是最常用的Web服务器软件。然而,根据那些显示过去十年的一些趋势以及与其他解决方案的比较的报告,其市场份额正在下降。Netcraft和Builtwith提供的报告略有不同,但都同意Apache的市场份额呈下降趋势,而Nginx的市场份额正在增长。
Nginx(发音为engine x)由Igor Sysoev于2004年发布,其明确目标是超越Apache。Nginx的网站有一篇值得阅读的文章,比较了这两种技术。起初,它主要用作Apache的补充,主要用于提供静态文件,但随着它不断发展以处理各种Web服务器任务,它一直在稳步增长。
它通常用作反向代理、负载均衡器和HTTP缓存。CDN和视频流提供商使用它来构建其性能至关重要的内容交付系统。
Apache已经存在很长时间了,并且有很多模块可以选择。Apache服务器的管理众所周知的是用户友好的。动态模块加载允许编译不同的模块并添加到Apache堆栈中,而无需重新编译主服务器二进制文件。通常,模块将在Linux发行版存储库中,安装它们后,可以通过系统包管理器优雅地使用类似a2enmod的命令将它们添加到堆栈中。Nginx尚未看到这种灵活性。当我们查看设置Nginx以支持HTTP/2的指南时,模块是Nginx需要构建的内容——在构建时配置的。
另一个促成Apache市场统治地位的特性是.htaccess文件。它是Apache的杀手锏,使其成为共享托管环境的首选解决方案,因为它允许在目录级别控制服务器配置。Apache提供的服务器上的每个目录都可以有自己的.htaccess文件。
Nginx不仅没有等效的解决方案,而且由于性能影响而阻止这种用法。
1995-2005年服务器厂商市场份额。数据来自Netcraft
LiteSpeed或LSWS是一个服务器竞争者,其灵活性水平可与Apache相比,同时又不牺牲性能。它支持Apache风格的.htaccess、mod_security和mod_rewrite,并且值得考虑用于共享设置。它被计划作为Apache的直接替代品,并且可以与cPanel和Plesk一起使用。自2015年以来,它一直支持HTTP/2。
LiteSpeed有三个许可层级:OpenLiteSpeed、LSWS Standard和LSWS Enterprise。Standard和Enterprise附带一个可选的缓存解决方案,可与Varnish媲美,即LSCache,它内置于服务器本身,并且可以使用.htaccess文件(每个目录)中的重写规则进行控制。它还内置了一些DDOS缓解“电池”。这加上其事件驱动架构,使其成为一个强大的竞争者,主要针对注重性能的托管提供商,但即使对于较小的服务器或网站,它也值得设置。
优化系统时,我们不能过分强调对硬件设置给予足够的关注。无论我们为设置选择哪种解决方案,拥有足够的RAM都至关重要。当Web服务器进程或像PHP这样的解释器没有足够的RAM时,它们就会开始交换,而交换实际上意味着使用硬盘来补充RAM内存。这样做的效果是每次访问此内存时都会增加延迟。这让我们想到了第二点——硬盘空间。使用快速的SSD存储是网站速度的另一个关键因素。我们还需要注意CPU可用性以及服务器数据中心与目标受众的物理距离。
要更深入地了解性能调整的硬件方面,Dropbox有一篇不错的文章。
详细监控当前服务器堆栈性能的一种实用方法是htop,它适用于Linux、Unix和macOS,并为我们的进程提供彩色概述。
其他监控工具包括New Relic(一个具有全套工具的高级解决方案)和Netdata(一个开源解决方案,提供出色的可扩展性、细粒度的指标和可定制的Web仪表板,适用于小型VPS系统和服务器网络监控)。它可以通过电子邮件、Slack、pushbullet、Telegram、Twilio等发送任何应用程序或系统进程的警报。
Monit是另一个无头开源工具,可以监控系统,并且可以配置为在满足某些条件时向我们发出警报,或重新启动某些进程,或重新启动系统。
AB(Apache Benchmark)是Apache基金会的一个简单的负载测试工具,Siege是另一个负载测试程序。本文解释了如何设置它们,这里有一些关于AB的更高级技巧,而对Siege的深入了解可以在这里找到。
如果您更喜欢Web界面,那么Locust是一个基于Python的工具,它对于测试网站性能非常方便。
安装Locust后,我们需要在我们将从中启动它的目录中创建一个locustfile:
<code>from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): @task(1) def index(self): self.client.get("/") @task(2) def shop(self): self.client.get("/?page_id=5") @task(3) def page(self): self.client.get("/?page_id=2") class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 300 max_wait = 3000 </code>
然后我们只需从命令行启动它:
<code>locust --host=https://my-website.com </code>
关于这些负载测试工具的一个警告:它们具有DDoS攻击的效果,因此建议您将测试限制在您自己的网站上。
Apache可以追溯到1995年和互联网的早期,当时服务器运行的一种可接受方式是在每个传入的TCP连接上生成一个新进程并对其进行回复。如果更多连接进来,则会创建更多工作进程来处理它们。生成新进程的成本很高,Apache开发人员设计了一种prefork模式,其中预先生成了一定数量的进程。每个进程中嵌入的动态语言解释器(如mod_php)仍然成本很高,并且Apache的默认设置会导致服务器崩溃。每个进程只能处理一个传入连接。
此模型在Apache的MPM(多进程模块)系统中称为mpm_prefork_module。根据Apache的网站,此模式几乎不需要配置,因为它可以自我调节,并且最重要的是MaxRequestWorkers指令足够大,可以处理您期望接收的尽可能多的同时请求,但又足够小,以确保所有进程都有足够的物理RAM。
一个小型Locust负载测试,显示生成大量Apache进程以处理传入流量。
我们可以补充一点,这种模式可能是Apache声名狼藉的主要原因。它可能会资源效率低下。
Apache的2.0版本带来了另外两个试图解决prefork模式问题的MPM。它们是worker模块或mpm_worker_module和event模块。
Worker模块不再基于进程;它是一种混合的基于进程线程的操作模式。引用Apache的网站:
单个控制进程(父进程)负责启动子进程。每个子进程根据ThreadsPerChild指令中指定的数量创建固定数量的服务器线程,以及一个侦听线程,该线程侦听连接并在连接到达时将它们传递给服务器线程进行处理。
此模式更节省资源。
Apache的2.4版本为我们带来了第三个MPM——event模块。它基于worker MPM,并添加了一个单独的侦听线程,该线程在HTTP请求完成后管理休眠的keepalive连接。它是一种非阻塞异步模式,内存占用更小。更多关于2.4版本改进的信息在这里。
我们在虚拟服务器上加载了一个大约有1200个帖子的测试WooCommerce安装程序,并在Apache 2.4上使用默认的prefork模式和mod_php对其进行了测试。
首先,我们在https://tools.pingdom.com上使用libapache2-mod-php7和mpm_prefork_module对其进行了测试:
然后,我们对event MPM模块进行了测试。
我们必须将multiverse添加到我们的/etc/apt/sources.list中:
<code>from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): @task(1) def index(self): self.client.get("/") @task(2) def shop(self): self.client.get("/?page_id=5") @task(3) def page(self): self.client.get("/?page_id=2") class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 300 max_wait = 3000 </code>
然后我们执行sudo apt-get update并安装libapache2-mod-fastcgi和php-fpm:
<code>locust --host=https://my-website.com </code>
由于php-fpm是与Apache分离的服务,因此需要重新启动:
<code>deb http://archive.ubuntu.com/ubuntu xenial main restricted universe multiverse deb http://archive.ubuntu.com/ubuntu xenial-updates main restricted universe multiverse deb http://security.ubuntu.com/ubuntu xenial-security main restricted universe multiverse deb http://archive.canonical.com/ubuntu xenial partner</code>
然后我们禁用了prefork模块,并启用了event模式和proxy_fcgi:
<code>sudo apt-get install libapache2-mod-fastcgi php7.0-fpm </code>
我们将此代码段添加到我们的Apache虚拟主机中:
<code>sudo service start php7.0-fpm </code>
此端口需要与/etc/php/7.0/fpm/pool.d/www.conf中的php-fpm配置一致。更多关于php-fpm设置的信息在这里。
然后我们在/etc/apache2/mods-available/mpm_event.conf中调整了mpm_event配置,记住我们对此测试的迷你VPS资源受到限制——因此我们只是减少了一些默认数字。Apache网站上每个指令的详细信息,以及此处关于event mpm的技巧。请记住,启动的服务器会消耗一定量的内存,无论它们有多忙。MaxRequestWorkers指令设置允许的同时请求的数量:将MaxConnectionsPerChild设置为非零值很重要,因为它可以防止可能的内存泄漏。
<code>from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): @task(1) def index(self): self.client.get("/") @task(2) def shop(self): self.client.get("/?page_id=5") @task(3) def page(self): self.client.get("/?page_id=2") class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 300 max_wait = 3000 </code>
然后我们使用sudo service apache2 restart重新启动服务器(如果我们更改某些指令,例如ThreadLimit,则需要显式停止和启动服务,使用sudo service apache2 stop; sudo service apache2 start)。
我们现在在Pingdom上的测试显示页面加载时间减少了一半以上:
禁用.htaccess:htaccess允许设置服务器根目录中每个目录的特定配置,而无需重新启动。因此,在每次请求时遍历所有目录以查找.htaccess文件会产生性能损失。
引自Apache文档:
一般来说,只有在您无权访问主服务器配置文件时才应使用.htaccess文件。…一般来说,应尽可能避免使用.htaccess文件。您可以像在主服务器配置文件中使用
部分一样有效地进行任何您认为要放在.htaccess文件中的配置。
解决方案是在/etc/apache2/apache2.conf中禁用它:
<code>locust --host=https://my-website.com </code>
如果我们需要它用于特定目录,那么我们可以在虚拟主机文件中的
<code>deb http://archive.ubuntu.com/ubuntu xenial main restricted universe multiverse deb http://archive.ubuntu.com/ubuntu xenial-updates main restricted universe multiverse deb http://security.ubuntu.com/ubuntu xenial-security main restricted universe multiverse deb http://archive.canonical.com/ubuntu xenial partner</code>
其他技巧包括:
Nginx是一个事件驱动和非阻塞的Web服务器。引用Hacker News上的一位发帖人:
与事件循环相比,分叉进程非常昂贵。基于事件的HTTP服务器最终获胜。
这个说法在Hacker News上引发了一场相当激烈的辩论,但根据我们的经验,仅仅从mpm_prefork Apache切换到Nginx通常就意味着可以防止网站崩溃。简单地切换到Nginx通常本身就是一种解决方法。
对Nginx架构更全面的视觉解释可以在这里找到。
Nginx建议将worker的数量固定为PC核心的数量(就像我们对Apache的mpm_event配置所做的那样),通过在/etc/nginx/nginx.conf中将worker_processes设置为auto(默认为1)。
worker_connections设置每个worker进程可以处理的连接数。默认为512,但通常可以增加。
Keepalive连接是影响性能的服务器方面,这在基准测试中通常不可见。
根据Nginx网站:
HTTP keepalive连接是必要的性能特性,可以减少延迟并加快网页加载速度。
建立新的TCP连接可能代价高昂——更不用说涉及HTTPS加密的情况了。HTTP/2协议通过其多路复用功能减轻了这种情况。重用现有连接可以减少请求时间。
Apache的mpm_prefork和mpm_worker存在并发限制,这与keepalive事件循环形成对比。这在Apache 2.4的mpm_event模块中得到一定程度的修复,并且在Nginx中作为唯一默认的操作模式出现。Nginx worker可以同时处理数千个传入连接,如果它用作反向代理或负载均衡器,则Nginx使用本地keepalive连接池,无需TCP连接开销。
keepalive_requests是一个设置,它调节客户端可以通过单个keepalive连接进行的请求数量。keepalive_timeout设置空闲keepalive连接保持打开的时间。
keepalive是一个与Nginx到上游服务器的连接相关的设置——当它充当代理或负载均衡器时。这意味着每个worker进程的空闲keepalive上游连接的数量。
启用上游keepalive连接需要将这些指令放入Nginx主配置中:
<code>from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): @task(1) def index(self): self.client.get("/") @task(2) def shop(self): self.client.get("/?page_id=5") @task(3) def page(self): self.client.get("/?page_id=2") class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 300 max_wait = 3000 </code>
Nginx上游连接由ngx_http_upstream_module管理。
如果我们的前端应用程序不断轮询我们的后端应用程序以获取更新,则增加keepalive_requests和keepalive_timeout将限制需要建立的连接数量。keepalive指令不应过大,以允许其他连接到达我们的上游服务器。
这些设置的调整是根据具体情况进行的,需要进行测试。这也许是keepalive没有默认设置的原因之一。
默认情况下,Nginx使用一个单独的PHP进程来转发PHP文件请求。在此,它充当代理(就像我们使用php7.0-fpm设置Apache时一样)。
通常,我们使用Nginx的虚拟主机设置如下所示:
<code>locust --host=https://my-website.com </code>
由于FastCGI与HTTP是不同的协议,因此前两行将一些参数和标头转发给php-fpm,而第三行指定了代理请求的方式——通过本地网络套接字。
这对于多服务器设置来说是实用的,因为我们还可以指定要将请求代理到的远程服务器。
但是,如果我们在单个系统上托管整个设置,则应使用Unix套接字连接到侦听的php进程:
<code>deb http://archive.ubuntu.com/ubuntu xenial main restricted universe multiverse deb http://archive.ubuntu.com/ubuntu xenial-updates main restricted universe multiverse deb http://security.ubuntu.com/ubuntu xenial-security main restricted universe multiverse deb http://archive.canonical.com/ubuntu xenial partner</code>
Unix套接字被认为比TCP具有更好的性能,并且此设置被认为更安全。您可以在Rackspace的这篇文章中找到有关此设置的更多详细信息。
关于Unix套接字的此技巧也适用于Apache。更多详细信息在这里。
gzip_static:关于Web服务器性能的普遍观点是压缩我们的静态资源。这通常意味着我们将尝试妥协,并尝试仅压缩超过某个阈值的文 件,因为在每次请求时动态压缩资源可能代价高昂。Nginx有一个gzip_static指令,允许我们提供文件的gzip版本(扩展名为.gz),而不是常规资源:
<code>from locust import HttpLocust, TaskSet, task class UserBehavior(TaskSet): @task(1) def index(self): self.client.get("/") @task(2) def shop(self): self.client.get("/?page_id=5") @task(3) def page(self): self.client.get("/?page_id=2") class WebsiteUser(HttpLocust): task_set = UserBehavior min_wait = 300 max_wait = 3000 </code>
这样,Nginx将尝试提供style.css.gz而不是style.css(在这种情况下,我们需要自己处理gzipping)。
这样,CPU周期就不会浪费在每次请求的动态压缩上。
关于Nginx的故事如果没有提到如何缓存内容就不完整。Nginx缓存非常高效,以至于许多系统管理员认为单独的HTTP缓存层(如Varnish)没有多大意义。“简单就是一种特性”。启用Nginx缓存非常简单。
<code>locust --host=https://my-website.com </code>
这是我们在虚拟主机文件中放置的指令,位于服务器块之外。proxy_cache_path参数可以是我们要存储缓存的任何路径。levels指定Nginx应该在多少个目录级别中存储缓存的内容。出于性能原因,两个级别通常是可以的。遍历目录可能会很费时。keys_zone参数是用于存储缓存键的共享内存区域的名称,而10m是这些键在内存中的空间(10MB通常就足够了;这不是实际缓存内容的空间)。max_size是可选的,它设置缓存内容的上限——这里是10GB。如果没有指定,它将占用所有可用空间。inactive指定内容在未被请求之前可以在缓存中停留多长时间,然后被Nginx删除。
设置好之后,我们将添加以下一行,其中包含内存区域的名称到服务器或位置块:
<code>deb http://archive.ubuntu.com/ubuntu xenial main restricted universe multiverse deb http://archive.ubuntu.com/ubuntu xenial-updates main restricted universe multiverse deb http://security.ubuntu.com/ubuntu xenial-security main restricted universe multiverse deb http://archive.canonical.com/ubuntu xenial partner</code>
通过告诉Nginx在遇到源服务器、上游服务器出错或服务器宕机时从缓存中提供项目,可以实现Nginx的额外容错层:
<code>sudo apt-get install libapache2-mod-fastcgi php7.0-fpm </code>
有关服务器或位置块指令的更多详细信息,以进一步调整Nginx缓存,请参见此处。
proxycache指令用于静态资源,但我们通常希望缓存Web应用程序的动态输出——无论是CMS还是其他内容。在这种情况下,我们将使用fastcgicache指令而不是proxycache*:
<code>sudo service start php7.0-fpm </code>
上面的最后一行将设置响应标头以告知我们内容是从缓存中传递还是没有。
然后,在我们的服务器或位置块中,我们可以设置一些缓存例外——例如,当请求URL中存在查询字符串时:
<code>sudo a2dismod php7.0 mpm_prefork sudo a2enmod mpm_event proxy_fcgi </code>
此外,在PHP的情况下,在服务器中的.php块中,我们将添加类似以下内容:
<code><filesmatch> SetHandler "proxy:fcgi://127.0.0.1:9000/" </filesmatch></code>
在上面,fastcgi_cache*行和fastcgi_no_cache调节缓存和排除。所有这些指令的详细参考可以在Nginx文档网站上找到。
要了解更多信息,Nginx的工作人员为此主题提供了一个免费网络研讨会,并且有很多电子书可用。
我们试图介绍一些有助于提高Web服务器性能的技术以及这些技术背后的理论。但这个主题绝非穷尽:我们仍然没有涵盖由Apache和Nginx组成的反向代理设置或多服务器设置。在这两台服务器上取得最佳结果取决于测试和分析具体的实际案例。这是一个永无止境的话题。
Apache和Nginx都是功能强大的Web服务器,但在性能和优化能力方面存在显着差异。 Apache是一个较旧的服务器,它使用进程驱动模型,为每个请求创建一个新线程。在处理多个并发连接时,这可能会导致大量的内存使用。另一方面,Nginx使用事件驱动架构,允许它同时处理数千个连接,而内存使用量极少。这使得Nginx更高效、更快,尤其是在静态内容交付和反向代理场景中。
有多种方法可以优化Nginx以获得更好的性能。首先,您可以调整worker进程和worker连接。 worker进程应设置为CPU或核心的数量,而worker连接应设置为最大打开文件限制。其次,您可以启用gzip压缩以减小Nginx发送给客户端的数据大小。第三,您可以使用缓存将频繁访问的数据存储在内存中,减少磁盘I/O操作。最后,您可以使用负载均衡将网络流量分布到多台服务器上,从而提高响应时间和整体性能。
可以通过多种方法优化Apache。首先,您可以调整MaxClients指令以控制最大并发连接数。其次,您可以启用mod_deflate在将数据发送给客户端之前对其进行压缩,从而减少带宽使用量。第三,您可以使用mod_cache进行缓存以将频繁访问的数据存储在内存中,减少磁盘I/O操作。最后,您可以使用mod_proxy_balancer进行负载均衡以将网络流量分布到多台服务器上,从而提高响应时间和整体性能。
是的,您可以在反向代理设置中同时使用Apache和Nginx。在此配置中,Nginx充当处理客户端请求的前端服务器,而Apache充当处理这些请求的后端服务器。此设置结合了两个服务器的优势,Nginx有效地处理静态内容,而Apache提供动态内容处理。
由于其事件驱动架构,Nginx擅长提供静态内容,这使其能够同时处理数千个并发连接,而内存使用量极少。对于动态内容,Nginx可以将请求传递给应用程序服务器(如PHP-FPM)或将其代理到Apache服务器。但是,Nginx不像Apache使用其mod_php模块那样本地处理动态内容。
Apache可以提供静态内容和动态内容。对于静态内容,Apache使用其核心模块。对于动态内容,Apache使用mod_php等附加模块来处理PHP脚本。但是,Apache的进程驱动模型在处理多个并发连接时可能会导致大量的内存使用,这使其在静态内容交付方面不如Nginx高效。
服务器优化可以显着提高网站性能。它可以减少服务器响应时间,增加服务器可以处理的并发连接数,并减少带宽使用量。这可以导致更快的页面加载时间、更好的用户体验和改进的SEO排名。
在Apache和Nginx之间进行选择取决于您的具体需求。如果您需要一个可以高效处理大量并发连接的服务器,或者您主要提供静态内容,那么Nginx可能是更好的选择。如果您需要一个对动态内容处理具有强大支持的服务器,或者您依赖.htaccess文件进行配置,那么Apache可能更合适。
Apache的常见性能问题包括在处理多个并发连接时内存使用量高和响应时间慢。对于Nginx,常见问题包括worker进程和连接的配置不当以及缺乏动态内容处理能力。
您可以使用各种工具监控Apache和Nginx的性能。对于Apache,您可以使用mod_status模块来提供服务器状态信息。对于Nginx,您可以使用stub_status模块。此外,您可以使用New Relic、Datadog或Nagios等第三方监控工具来获取更详细的性能指标和警报。
以上是Apache vs nginx性能:优化技术的详细内容。更多信息请关注PHP中文网其他相关文章!