php - 当我使用 queue:listen 时,Laravel 队列作业永远不会仅以超时结束
问题描述
我对 Laravel 队列有疑问。它将开始处理作业,但随后它会挂在中间某处(基于对日志的自定义回显)并且仅以超时结束。问题是作业应该不超过 1 分钟,但队列中的作业运行超过 10 分钟,没有任何结果也没有任何错误 - 除了标准超时错误。
工作
应该在队列中处理的作业包含标准的 Eloquent 选择和一个应该更新另一个模型属性的更新方法。
// app\Listeners\CountReceivedTextAnswers
// There follows the listener's handle method. Nothing else is inside the
// Listener, it also implements ShouldQueue interface and InteractsWithQueue trait.
public function handle($event)
{
$questions = $this->question->whereTarget(['gl', 'povinn', 'ucit'], $event->evaluation->id, 'text');
$this->evaluation->updateOptions(
$event->evaluation->id,
'number_of_answers_to_text_questions',
$this->answer->countAnswersToManyQuestions($questions)
);
}
// app\Repositories\Answers\AnswersEloquentRepository
// This is the method that is called inside the listener. It passes
// collection of questions to the following method which should count
// answers on them.
public function countAnswersToManyQuestions(Collection $questions): int
{
$result = 0;
foreach ($questions as $question) {
$result += $this->countAnswersToQuestion($question);
}
return $result;
}
// This is the count method, it accepts Question model and count
// number of answers received on that question.
public function countAnswersToQuestion(Question $question): int
{
$select = [
'id',
'import_id',
'question_id',
'content',
'value',
'hidden',
'hidden_by',
'signed_by',
];
return Answer::select($select)
->whereDoesntHave('answered')
->where('question_id', '=', $question->id)
// Remove unwanted answers e.g. empty.
->when($question->form === 'text', function (Builder $query) {
$query->whereNotNull('content');
})
->when($question->form === 'slider', function (Builder $query) {
$query->whereNotNull('value');
})
->count();
}
// app\Repositories\Evaluation\EvaluationEloquentRepository
// This is the update method that is called to update the value
// inside the listener.
public function updateOptions($id, $field, $value)
{
$evaluation = $this->find($id);
$options = json_decode($evaluation->options, true);
$options[$field] = $value;
return $this->update($id, [
'options' => $options
]);
}
当我在 Tinker 中手动从侦听器调用相同的方法时,大约需要 30 秒才能完成。因此我猜这个问题不应该与方法本身有关,而是其他的,可能是配置?
我的设置
我正在使用带有五个容器的 docker,其中两个基于我的docker镜像(.dockerfile),该镜像基于官方 php:7.3-fpm 镜像,并安装了 oci8 和一些其他扩展。容器的启动脚本基于本教程,因此我可以同时使用一个容器 - 队列和应用程序。其余容器基于其官方 docker 镜像 - httpd:2.4-alpine、mysql:8.0 和 redis:5-alpine。我还应该注意我正在使用Laravel 5.5
php.ini
我在 php.ini 配置中更改了这些值。休息应该是默认的。设置得比较大方,因为一开始我以为这个错误是和php config有关的,但好像不是,因为php的错误日志中没有错误。
date.timezone=UTC
display_errors=on
log_errors=On
error_log=/var/www/storage/logs/php.log
opcache.enable=1
opcache.enable_cli=1
memory_limit = 512M
upload_max_filesize = 128M
post_max_size = 64M
max_execution_time=900
max_input_time=900
default_socket_timeout=60
启动文件
#!/usr/bin/env bash
set -e
role=${CONTAINER_ROLE:-app}
env=${APP_ENV:-production}
if [[ "$env" != "local" ]]; then
echo "Caching configuration..."
(cd /var/www && php artisan config:cache && php artisan route:cache && php artisan view:cache)
fi
if [[ "$role" = "app" ]]; then
exec php-fpm
elif [[ "$role" = "queue" ]]; then
echo "Running the queue..."
php /var/www/artisan queue:listen --verbose --tries=10 --sleep=0 --timeout=800 --memory=512
elif [[ "$role" = "scheduler" ]]; then
while [[ true ]]
do
php /var/www/artisan schedule:run --verbose --no-interaction &
sleep 60
done
else
echo "Could not match the container role \"$role\""
exit 1
fi
唯一的错误
我可以在 laravel.log 中找到唯一的错误,但是我不认为实际问题出在作业的长度上,因为在 Tinker 中运行它所花费的时间远少于设置的超时时间是。
[2019-05-22 16:06:39] local.ERROR: The process "'/usr/local/bin/php' 'artisan' queue:work 'redis' --once --queue='default' -- delay=0 --memory=512 --sleep=0 --tries=10" 超过了 800 秒的超时时间。{"exception":"[object] (Symfony\Component\Process\Exception\ProcessTimedOutException(code: 0): 进程\"'/usr/local/bin/php' 'artisan' queue:work 'redis' --曾经 --queue='default' --delay=0 --memory=512 --sleep=0 --tries=10\" 超过了 800 秒的超时时间。在 /var/www/vendor/symfony/process/Process .php:1335) [stacktrace] /var/www/vendor/symfony/process/Process.php(424): Symfony\Component\Process\Process->checkTimeout() /var/www/vendor/symfony/process/Process .php(212): Symfony\Component\Process\Process->
我可能已经尝试了我在互联网上找到的所有可能的建议,我已经更改了php artisan queue:listen
命令中的所有值,php.ini
但它总是以相同的结果结束。我也尝试定位redis日志,但没有成功,因此我将队列移至数据库,结果始终相同。队列侦听器开始了这项工作,但随后不知何故挂起,没有任何进一步的信息或错误。我还应该说,拥有所有这些侦听器和工作的工作人员在 docker 映像之外工作得很好。
我将非常感谢任何建议或提示!另外,如果您想查看更多信息,请告诉我,我会添加它们。
解决方案
最后我发现队列工作者真的超过了超时。这是由于在某些数据迁移过程中删除了所有外键和索引,因此从表中加载关系花费了太长时间。重新定义数据库中的关系使队列工作器显着更快并且错误消失了。
推荐阅读
- sql - SQL Server 查找类先决条件循环
- arrays - 检查空格的数组公式
- html - 链接被识别为链接,但无法点击一页网站内的锚点
- python - 如何将填充应用于数组列表?
- ios - CoreBluetooth 中是否存在 didLostPeripheral (或类似方法)
- php - 以逗号分隔字符串而不将其放入数组中
- java - 为什么在 Headless 模式下调用 Quit 时 ChromeDriver 不存储缓存?
- django - django url 连接。我不想连接
- c# - 将 C# 转换为 Powershell 和 Regex 不起作用还是我做错了?
- php - 即使安装了教义/教义捆绑包,教义命令也不会出现在 Symfony 3.4 控制台中