[英]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.