核心要点
本文是关于构建示例应用(多图博客)以进行性能基准测试和优化的系列文章的一部分。 (在此处查看代码库)
在之前的文章中,我们添加了按需图片缩放功能。图片在首次请求时进行缩放并缓存以供以后使用。这样做虽然方便,但也增加了首次加载的开销;系统必须动态渲染缩略图,并在完成图片渲染之前“阻塞”第一个用户的页面渲染。
优化的方案是在创建图库后渲染缩略图。您可能会想:“好吧,但这样会阻塞创建图库的用户?”这不仅会带来糟糕的用户体验,而且也不是一个可扩展的解决方案。用户会对较长的加载时间感到困惑,或者更糟糕的是,如果图片过大而无法处理,则会遇到超时和/或错误。最佳解决方案是将这些繁重的任务移至后台处理。
后台任务
后台任务是处理任何繁重任务的最佳方式。我们可以立即通知用户我们已收到他们的请求并安排其进行处理。 YouTube 上传视频的方式也是如此:上传后视频并不可访问。用户需要等到视频完全处理完毕才能预览或分享。
处理或生成文件、发送电子邮件或任何其他非关键任务都应在后台进行。
后台处理的工作原理
后台处理方法有两个关键组件:任务队列和工作进程。应用程序创建需要处理的任务,而工作进程则等待并一次从队列中获取一个任务。
您可以创建多个工作进程(进程)来加快处理速度,将大型任务分解成较小的块并同时处理它们。您可以根据自己的需要组织和管理后台处理,但请注意,并行处理并非一项简单的任务:您应该注意潜在的竞争条件并优雅地处理失败的任务。
我们的技术栈
我们使用Beanstalkd任务队列来存储任务,使用Symfony Console组件将工作进程实现为控制台命令,并使用Supervisor来管理工作进程。
如果您使用Homestead Improved,则Beanstalkd和Supervisor已安装,因此您可以跳过下面的安装说明。
安装Beanstalkd
Beanstalkd是一个快速的作业队列,具有通用的接口,最初设计用于通过异步运行耗时任务来减少高流量 Web 应用程序中页面浏览的延迟。
您可以使用许多可用的客户端库。在我们的项目中,我们使用的是Pheanstalk。
要在您的Ubuntu或Debian服务器上安装Beanstalkd,只需运行sudo apt-get install beanstalkd
。查看官方下载页面,了解如何在其他操作系统上安装Beanstalkd。
安装后,Beanstalkd作为守护进程启动,等待客户端连接并创建(或处理)作业:
<code>/etc/init.d/beanstalkd Usage: /etc/init.d/beanstalkd {start|stop|force-stop|restart|force-reload|status}</code>
通过运行composer require pda/pheanstalk
安装Pheanstalk作为依赖项。
队列将用于创建和获取作业,因此我们将作业创建集中在一个工厂服务JobQueueFactory
中:
<code class="language-php"><?php namespace App\Service; use Pheanstalk\Pheanstalk; class JobQueueFactory { private $host = 'localhost'; private $port = '11300'; const QUEUE_IMAGE_RESIZE = 'resize'; public function createQueue(): Pheanstalk { return new Pheanstalk($this->host, $this->port); } }</code>
现在,我们可以根据需要注入工厂服务来与Beanstalkd队列进行交互。我们将队列名称定义为常量,并在将作业放入队列或在工作进程中监视队列时引用它。
安装Supervisor
根据官方页面,Supervisor是一个客户端/服务器系统,允许其用户监视和控制类 Unix 操作系统上的许多进程。
我们将使用它来启动、重启、扩展和监视工作进程。
通过运行sudo apt-get install supervisor
在您的Ubuntu/Debian服务器上安装Supervisor。安装后,Supervisor将作为守护进程在后台运行。使用supervisorctl
来控制Supervisor进程:
<code>$ sudo supervisorctl help default commands (type help <topic>): </topic>===================================== add exit open reload restart start tail avail fg pid remove shutdown status update clear maintail quit reread signal stop version</code>
要使用Supervisor控制进程,我们首先必须编写一个配置文件并描述我们希望如何控制我们的进程。配置存储在/etc/supervisor/conf.d/
中。用于调整大小的工作进程的简单Supervisor配置如下所示:
<code>[program:resize-worker] process_name=%(program_name)s_%(process_num)02d command=php PATH-TO-YOUR-APP/bin/console app:resize-image-worker autostart=true autorestart=true numprocs=5 stderr_logfile = PATH-TO-YOUR-APP/var/log/resize-worker-stderr.log stdout_logfile = PATH-TO-YOUR-APP/var/log/resize-worker-stdout.log</code>
我们告诉Supervisor如何命名生成的进程、要运行的命令的路径、自动启动和重启进程、我们想要多少进程以及在哪里记录输出。在此处了解更多关于Supervisor配置的信息。
在后台调整图片大小
一旦我们的基础设施设置完毕(即Beanstalkd和Supervisor安装完毕),我们就可以修改我们的应用程序以在创建图库后在后台调整图片大小。为此,我们需要:
ImageController
中的图片服务逻辑更新图片服务逻辑
到目前为止,我们一直在首次请求时调整图片大小:如果请求大小的图片文件不存在,则会动态创建它。
我们现在将修改ImageController
,仅当调整大小的图片文件存在(即图片已调整大小)时才返回请求大小的图片响应。
如果不存在,应用程序将返回一个通用的占位符图片响应,说明图片正在调整大小。请注意,占位符图片响应具有不同的缓存控制标头,因为我们不想缓存占位符图片;我们希望在调整大小过程完成后立即渲染图片。
我们将创建一个名为GalleryCreatedEvent
的简单事件,其有效负载为图库 ID。在成功创建图库后,此事件将在UploadController
中分派:
<code>/etc/init.d/beanstalkd Usage: /etc/init.d/beanstalkd {start|stop|force-stop|restart|force-reload|status}</code>
此外,我们将使用“Images are now being processed.”更新闪存消息,以便用户知道在我们准备好之前,我们还需要对他们的图片进行一些处理。
我们将创建GalleryEventSubscriber
事件订阅者,它将对GalleryCreatedEvent
做出反应,并为新创建的图库中的每个图片请求调整大小作业:
<code class="language-php"><?php namespace App\Service; use Pheanstalk\Pheanstalk; class JobQueueFactory { private $host = 'localhost'; private $port = '11300'; const QUEUE_IMAGE_RESIZE = 'resize'; public function createQueue(): Pheanstalk { return new Pheanstalk($this->host, $this->port); } }</code>
现在,当用户成功创建图库时,应用程序将呈现图库页面,但某些图片的缩略图仍未准备好,因此不会显示:
一旦工作进程完成调整大小,下次刷新应该会呈现完整的图库页面。
将调整大小的工作进程实现为控制台命令
工作进程是一个简单的进程,它为从队列中获取的每个作业执行相同的作业。工作进程的执行在$queue->reserve()
调用处被阻塞,直到该工作进程保留作业,或者发生超时。
只有一个工作进程可以获取和处理作业。作业通常包含有效负载,例如字符串或序列化数组/对象。在我们的例子中,它将是已创建的图库的 UUID。
一个简单的工人如下所示:
<code>/etc/init.d/beanstalkd Usage: /etc/init.d/beanstalkd {start|stop|force-stop|restart|force-reload|status}</code>
您可能已经注意到,工作进程将在定义的超时后或处理作业后退出。我们可以将工作进程逻辑包装在一个无限循环中并使其无限期地重复其作业,但这可能会导致一些问题,例如长时间不活动后数据库连接超时,并使部署更加困难。为了防止这种情况,我们的工作进程生命周期将在完成单个任务后结束。然后,Supervisor 将重新启动工作进程作为新进程。
查看ResizeImageWorkerCommand
以了解 Worker 命令的结构。以这种方式实现的工作进程也可以手动启动为 Symfony 控制台命令:./bin/console app:resize-image-worker
。
创建Supervisor配置
我们希望我们的工作进程自动启动,因此我们将在配置中设置autostart=true
指令。由于必须在超时或成功处理任务后重新启动工作进程,因此我们还将设置autorestart=true
指令。
后台处理的最佳部分是并行处理的简易性。我们可以设置numprocs=5
指令,Supervisor 将生成五个工作进程实例。它们将等待作业并独立处理它们,从而使我们能够轻松扩展系统。随着系统的发展,您可能需要增加进程数量。由于我们将有多个进程运行,因此我们需要定义进程名称的结构,因此我们设置了process_name=%(program_name)s_%(process_num)02d
指令。
最后但并非最不重要的是,我们希望存储工作进程的输出,以便在出现问题时可以分析和调试它们。我们将定义stderr_logfile
和stdout_logfile
路径。
我们的调整大小工作进程的完整Supervisor配置如下所示:
<code class="language-php"><?php namespace App\Service; use Pheanstalk\Pheanstalk; class JobQueueFactory { private $host = 'localhost'; private $port = '11300'; const QUEUE_IMAGE_RESIZE = 'resize'; public function createQueue(): Pheanstalk { return new Pheanstalk($this->host, $this->port); } }</code>
创建(或更新)位于/etc/supervisor/conf.d/
目录中的配置文件后,您必须告诉Supervisor重新读取并更新其配置,方法是执行以下命令:
<code>$ sudo supervisorctl help default commands (type help <topic>): </topic>===================================== add exit open reload restart start tail avail fg pid remove shutdown status update clear maintail quit reread signal stop version</code>
如果您使用Homestead Improved(您应该使用!),您可以使用scripts/setup-supervisor.sh
为该项目生成Supervisor配置:sudo ./scripts/setup-supervisor.sh
。
更新装置
图片缩略图将不再在第一次请求时呈现,因此当我们在LoadGalleriesData
装置类中加载装置时,我们需要显式地请求为每个图片呈现:
<code>[program:resize-worker] process_name=%(program_name)s_%(process_num)02d command=php PATH-TO-YOUR-APP/bin/console app:resize-image-worker autostart=true autorestart=true numprocs=5 stderr_logfile = PATH-TO-YOUR-APP/var/log/resize-worker-stderr.log stdout_logfile = PATH-TO-YOUR-APP/var/log/resize-worker-stdout.log</code>
现在您应该感觉到装置加载速度变慢了,这就是为什么我们将它移到后台,而不是强迫用户等待它完成!
提示和技巧
工作进程在后台运行,因此即使您部署了新版本的应用程序,在第一次重新启动之前,您仍然会有过时的工作进程运行。
在我们的例子中,我们必须等待所有工作进程完成其任务或超时(5分钟),直到我们确定所有工作进程都已更新。在创建部署过程时请注意这一点!
关于使用后台处理来加快页面加载时间的常见问题解答 (FAQ)
后台处理在加快页面加载速度方面起什么作用?
后台处理在提高页面加载速度方面起着至关重要的作用。它允许在后台执行某些任务,从而释放主线程资源,专注于页面加载。这意味着用户不必等待这些任务完成才能加载页面,从而带来更快、更流畅的浏览体验。
Symfony Process 组件如何辅助后台处理?
Symfony Process 组件是一个强大的工具,允许您在子进程中执行命令。它为运行系统命令和管理其输出提供了一个简单面向对象的 API。这对于后台处理特别有用,因为它允许您在单独的进程中运行任务,而不会阻塞主线程。
后台处理的一些常见用例是什么?
后台处理通常用于可以在与主线程无关的情况下执行任务的情况。这包括发送电子邮件、处理图片、运行复杂的计算等等任务。通过在后台运行这些任务,您可以提高应用程序的性能并提供更好的用户体验。
如何在 PHP 中执行后台进程?
可以使用 exec()
函数在 PHP 中执行后台进程。此函数允许您在子进程中运行命令,然后继续执行脚本的其余部分,而无需等待命令完成。这是一个简单的示例:
exec("php background_task.php > /dev/null &");
在此示例中,background_task.php
是您想要在后台运行的脚本。
什么是 Symfony Messenger 组件,它与后台处理有什么关系?
Symfony Messenger 组件是一个消息总线系统,可用于异步地将消息分派给处理程序。这意味着您可以向总线发送消息,然后继续执行脚本,而无需等待处理消息。这是一种后台处理的形式,因为消息的处理可以在单独的进程中完成。
如何使用后台处理来提高网站的性能?
通过将任务卸载到后台,您可以释放主线程资源,专注于加载页面。这可以显著提高网站的性能,尤其是在您有耗时或资源密集型任务的情况下。一些可以卸载到后台的常见任务包括发送电子邮件、处理图片和运行复杂的计算。
后台处理有哪些潜在的挑战,如何减轻这些挑战?
后台处理的主要挑战之一是确保任务成功且按正确的顺序完成。这可以通过使用任务队列来减轻,任务队列确保任务按添加的顺序执行。另一个挑战是处理后台任务中的错误。这可以通过实现健壮的错误处理和日志记录机制来解决。
后台处理可以与其他性能优化技术结合使用吗?
是的,后台处理可以与其他性能优化技术结合使用。例如,您可以使用缓存来存储昂贵操作的结果,然后使用后台处理定期更新缓存。这允许您提供最新的数据,而不会减慢应用程序的速度。
如何监视后台任务的进度?
可以使用各种工具和技术来监视后台任务的进度。一种常见的方法是使用日志记录来记录每个任务的状态。您还可以使用 Symfony 的 Messenger 组件之类的工具,该组件提供对监视和调试后台任务的内置支持。
使用后台处理时是否存在任何安全注意事项?
是的,使用后台处理时存在一些安全注意事项。例如,您需要确保后台任务不会泄露敏感信息,并且不会受到注入攻击。您还应该确保您的任务在安全的环境中运行,并且它们没有比完成其工作所需的更多权限。
以上是使用背景处理来加快页面加载时间的详细内容。更多信息请关注PHP中文网其他相关文章!