[英]Queued Laravel Notifications get stuck on AWS SQS
我在 AWS 上有一個工作人員處理排隊的 Laravel 通知。 一些通知被發送出去,但其他通知被困在隊列中,我不知道為什么。
我查看了 Beanstalk 中的日志,看到了三種不同類型的錯誤:
2020/11/03 09:22:34 [emerg] 10932#0: *30 malloc(4096) failed (12: Cannot allocate memory) while reading upstream, client: 127.0.0.1, server: , request: "POST /worker/queue HTTP/1.1", upstream: "fastcgi://unix:/run/php-fpm/www.sock:", host: "localhost"
我也在 Bugsnag 上看到內存不足問題,但沒有任何堆棧跟蹤。
另一個錯誤是這個:
2020/11/02 14:50:07 [error] 10241#0: *2623 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 127.0.0.1, server: , request: "POST /worker/queue HTTP/1.1", upstream: "fastcgi://unix:/run/php-fpm/www.sock", host: "localhost"
這是最后一個:
2020/11/02 15:00:24 [error] 10241#0: *2698 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: 127.0.0.1, server: , request: "POST /worker/queue HTTP/1.1", upstream: "fastcgi://unix:/run/php-fpm/www.sock:", host: "localhost"
我真的不明白我能做些什么來解決這些錯誤。 這只是一個基本的 Laravel / EBS / SQS 設置,隊列唯一要做的就是處理通知。 有時一次幾十個。 我正在運行t2.micro
,並假設這足以發送幾封電子郵件? 我已將環境升級到t2.large
但無濟於事。
我注意到消息最終在隊列中,然后獲得狀態“正在傳輸的消息”,但隨后在 Laravel 方面遇到了各種麻煩。 但我沒有得到任何有用的錯誤。
所有實現代碼似乎都很好,因為前幾個通知按預期發出,如果我根本不排隊,所有通知都會立即發送。
排隊的通知最終會生成兩個不同的異常: MaxAttemptsExceededException
和Out of Memory FatalError
,但兩者都不MaxAttemptsExceededException
我引向實際的潛在問題。
我在哪里可以進一步調試?
更新
請參閱我對問題和解決方案的回答。 在工作人員嘗試為仍必須創建的對象發送通知之前,數據庫事務尚未完成。
當前分配給 PHP 的 memory_limit 是多少? 您可以通過運行以下命令來確定這一點:
php -i | grep memory_limit
您可以通過運行以下內容來增加它:
sed -i -e 's/memory_limit = [current-limit]/memory_limit = [new-limit]/g' [full-path-to-php-ini]
只需將[current-limit]替換為第一個命令中顯示的值,並將[new-limit]替換為一個新的合理值。 這可能需要反復試驗。 將[full-path-to-php-ini]替換為失敗進程使用的 php.ini 的完整路徑。 要找到它,請運行:
php -i | grep php.ini
首先確保你增加了max_execution_time
和memory_limit
還要確保您設置了--timeout選項
然后確保按照 laravel 文檔中的Amazon SQS說明進行操作
唯一不包含 retry_after 值的隊列連接是 Amazon SQS。 SQS 將根據在 AWS 控制台內管理的默認可見性超時重試作業。
如果您確定某些排隊的事件被工作程序 Laravel 正確接收和處理,那么正如其他人所說,這主要是 PHP 內存問題。
在 beanstalk 上,這是我添加到我的 ebextensions 以獲得更大的 PHP 內存的內容(這是針對作曲家的內存問題):
請注意,這是帶有 4go 的 t3.medium EC2 實例,僅適用於 laravel API。
02-environment.config commands: ... option_settings: ... - namespace: aws:elasticbeanstalk:container:php:phpini option_name: memory_limit value: 4096M - namespace: aws:ec2:instances option_name: InstanceTypes value: t3.medium
因此,您可以嘗試增加限制,使用更多可用實例的最大 ram,然后再次部署,以便 beanstalk 重建實例並設置 PHP memory_limit
。
注意:真正的配置當然包含其他配置文件和更多截斷的內容。
正如您所說,您只是在發送電子郵件,所以應該沒問題。 當有大量電子郵件排隊時會發生這種情況嗎? 最后,SQS deadLetterQueue 中有很多事件嗎? 如果是這樣,可能是因為排隊的電子郵件爆發。 因此,SQS 將“淹沒” /worker 路由以執行您的作業。 您可以從 AWS 控制台檢查服務器使用情況,或者像 CLI 工具這樣的htop
進行監控,還可以檢查 SQS 界面以查看是否有許多失敗的作業同時出現(突發)。
編輯:對於彈性豆莖,我使用了dusterio/laravel-aws-worker ,也許你也是,因為你的日志提到了/worker/queue
路線
分配給 PHP 的默認內存量通常非常小。 使用 EBS 時,您希望盡可能多地使用配置文件 - 任何時候您必須通過 SSH 更改服務器上的內容,當您需要重新部署時,您將遇到更多問題。 我已將此添加到我的 EBS 配置/.ebextensions/01-php-settings.config
:
option_settings:
aws:elasticbeanstalk:container:php:phpini:
memory_limit: 256M
當運行t3.micro
來完成我所有的通知和導入處理時,這就足夠了。 對於簡單的處理,它通常不需要比默認更多的內存,但這在很大程度上取決於您的用例以及您對通知進行編程的方式。
正如本答案中已經指出的那樣,SQS 隊列在超時方面的運行方式略有不同。 這是我為幫助解決此問題而編寫的一個小特征:
<?php
namespace App\Jobs\Traits;
trait CanExtendSqsVisibilityTimeout
{
/** NOTE: this needs to map to setting in AWS console */
protected $defaultBackoff = 30;
protected $backoff = 30;
/**
* Extend the time that the job is locked for processing
*
* SQS messages are managed via the default visibility timeout console setting; noted absence of retry_after config
* @see https://laravel.com/docs/7.x/queues#job-expirations-and-timeouts
* AWS recommends to create a "heartbeat" in the consumer process in order to extend processing time:
* @see https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html#configuring-visibility-timeout
*
* @param int $delay Number of seconds to extend the processing time by
*
* @return void
*/
public function extendBackoff($delay = 60)
{
if ($this->job) {
// VisibilityTimeout has a 12 hour (43200s) maximum and will error above that; no extensions if close to it
if ($this->backoff + $delay > 42300) {
return;
}
// add the delay
$this->backoff += $delay;
$sqs = $this->job->getSqs();
$sqsJob = $this->job->getSqsJob();
$sqs->changeMessageVisibility([
'QueueUrl' => $this->job->getQueue(),
'ReceiptHandle' => $sqsJob['ReceiptHandle'],
'VisibilityTimeout' => $this->backoff,
]);
}
}
}
然后對於需要很長時間的排隊作業,我稍微更改了代碼以找出可以插入合理“心跳”的位置。 就我而言,我有一個循環:
class LongRunningJob implements ShouldQueue
{
use CanExtendSqsVisibilityTimeout;
//...
public function handle()
{
// some other processing, no loops involved
// now the code that loops!
$last_extend_at = time();
foreach ($tasks as $task) {
$task->doingSomething();
// make sure the processing doesn't time out, but don't extend time too often
if ($last_extend_at + $this->defaultBackoff - 10 > time()) {
// "heartbeat" to extend visibility timeout
$this->extendBackoff();
$last_extend_at = time();
}
}
}
聽起來您可能需要更詳細地了解您是如何運行工作程序的。
我認為必須讓主管運行以幫助重新啟動您的工人。 否則,如果工作人員停止工作,排隊的消息將在到期時被刪除。 使用 Laravel + EBS 很好地工作有點繁瑣 - 沒有太多好的文檔圍繞它,這可能是為什么不必管理它是 Vapor 的賣點之一!
我們終於找到了問題所在,而不是內存或執行時間。
從一開始我就覺得很奇怪,默認內存或默認執行時間都不足以發送一兩封電子郵件。
我們的用例是:創建一個新Article
並且用戶收到通知。
導致解決方案的一些線索:
php artisan queue
。 然后我們看到的解釋了一切: Illuminate\\Database\\Eloquent\\ModelNotFoundException: No query results for model [App\\Article]
這是我們從未從 EBS Worker / SQS 得到的錯誤,並迅速導致了解決方案:
在文章進入數據庫之前處理通知。
我們為工人添加了延遲,從那時起就沒有問題了。 我們最近在創建文章的過程中添加了一個數據庫事務,並在該事務中創建通知(但在最后)。 我想這就是為什么我們以前沒有這個問題。 我們決定將通知創建留在事務中,只是延遲處理通知。 這意味着我們不必做修補程序來解決這個問題。
感謝所有參與幫助的人!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.