核心要點
本文是關於構建示例應用(多圖博客)以進行性能基準測試和優化的系列文章的一部分。 (在此處查看代碼庫)
在之前的文章中,我們添加了按需圖片縮放功能。圖片在首次請求時進行縮放並緩存以供以後使用。這樣做雖然方便,但也增加了首次加載的開銷;系統必須動態渲染縮略圖,並在完成圖片渲染之前“阻塞”第一個用戶的頁面渲染。
優化的方案是在創建圖庫後渲染縮略圖。您可能會想:“好吧,但這樣會阻塞創建圖庫的用戶?”這不僅會帶來糟糕的用戶體驗,而且也不是一個可擴展的解決方案。用戶會對較長的加載時間感到困惑,或者更糟糕的是,如果圖片過大而無法處理,則會遇到超時和/或錯誤。最佳解決方案是將這些繁重的任務移至後台處理。
後台任務
後台任務是處理任何繁重任務的最佳方式。我們可以立即通知用戶我們已收到他們的請求並安排其進行處理。 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
中:
<?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); } }
現在,我們可以根據需要注入工廠服務來與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
做出反應,並為新創建的圖庫中的每個圖片請求調整大小作業:
<?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); } }
現在,當用戶成功創建圖庫時,應用程序將呈現圖庫頁面,但某些圖片的縮略圖仍未準備好,因此不會顯示:
一旦工作進程完成調整大小,下次刷新應該會呈現完整的圖庫頁面。
將調整大小的工作進程實現為控制台命令
工作進程是一個簡單的進程,它為從隊列中獲取的每個作業執行相同的作業。工作進程的執行在$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配置如下所示:
<?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); } }
創建(或更新)位於/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中文網其他相關文章!