繁体   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