簡體   English   中英

排隊的 Laravel 通知卡在 AWS SQS 上

[英]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 方面遇到了各種麻煩。 但我沒有得到任何有用的錯誤。

所有實現代碼似乎都很好,因為前幾個通知按預期發出,如果我根本不排隊,所有通知都會立即發送。

排隊的通知最終會生成兩個不同的異常: MaxAttemptsExceededExceptionOut 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_timememory_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並且用戶收到通知。

導致解決方案的一些線索:

  • 我們注意到我們通常在第一次通知時遇到問題。
  • 如果我們同時創建 10 篇文章,我們就會錯過每篇文章的第一個通知。
  • 我們將 Worker 中的 HTTP Max Connections 設置為 1。當同時創建 10 篇文章時,我們注意到只有第一篇文章錯過了第一個通知。
  • 我們沒有從 Worker 那里得到任何有用的錯誤消息,所以我們決定設置我們自己的 EC2 並手動運行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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM