(Примечание: я новичок в использовании AWS SQS для очередей). У меня есть функция, в которой я хотел бы вставить десятки тысяч записей в Excel, сохранить Excel в AWS S3 и отобразить в таблице данных внешнего интерфейса. Эта функция выполняется с использованием очереди AWS SQS с супервизором в качестве рабочего в веб-приложении Laravel 9.
Ошибка, которую я получаю:
- Job\SomeJob выполнялся слишком много раз или выполнялся слишком долго. задание могло быть ранее тайм-аут. {"исключение":"[объект] (Illuminate\Queue\MaxAttemptsExceededException (код: 0)
- (Symfony\Component\ErrorHandler\Error\FatalError(код: 0): максимум время выполнения 60 секунд превышено в /var/app/current/vendor/laravel/framework/src/Illuminate/Collections/Arr.php:314)
Я понятия не имею, почему я получаю эту ошибку, но работа на самом деле успешна. Эта ошибка будет отображаться в таблице failed_jobs, и у меня есть функция, в которой, если есть какие-либо failed_jobs, запустить скрипт для отправки электронной почты менеджеру, и я думаю, вы, ребята, знаете, что происходит после этого.
Что я пробовал, так это Log::info() каждую строку до и после процесса, чтобы узнать, что вызывает ошибку.
Моя настройка Supervisor для SQS:
[program:sqs-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/app/current/artisan queue:work sqs --sleep=3 --tries=1 --timeout=1800
autostart=true
autorestart=true
user=webapp
numprocs=1
redirect_stderr=true
stdout_logfile=/var/www/html/worker.log
Как я отправляю работу:
class SomeOtherController extends Controller{
public function show(){
dispatch(new SomeJob($id));
return 'job run';
}
}
Содержание работы такое:
Class SomeJob implements ShouldQueue{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $timeout = 1800;
public $id;
public function __construct($id){
$this->id = $id;
}
public function handle(){
Log::info('start job');
$apps = Application::where('client_id', $id)->get(); //15000+ records
Log::info('start foreach');
$count = 0; //to count each 100 records, do Log::info()
foreach($apps as $key => $app){
if ($count == 100){
Log::info('This is the '.$key.' record');
$count = 0;
}
//the actual job is much more lenghty and complicated
$list = new ApplicationTable();
$list->client_id = $app->client_id;
$list->name = $app->name;
$list->booking = $app->booking->name;
$list->price = $app->price + $app->discount + $app->gst;
$list->save();
$count++;
}
Log::info('end foreach');
//Some process to generate and store excel to S3
$lists = ApplicationTable::where('client_id','=',$id)->get();
(new ReportExport($lists))->store('application_report');
$s3 = Storage::disk('s3');
$s3_path = 'ApplicationReport';
$s3->put($s3_path, file_get_contents('application_report'));
//unlink
unlink($path);
$user_email = $apps->first()->user->email;
if (isset($user_email)){
\Mail::to($user_email)->send(new ApplicationReportMail($id));
}
log::info('end job');
return true;
}
}
Я ожидаю, что в журнале будут отображаться все процессы и заканчиваться «завершением задания» без каких-либо ошибок. Но что я получаю:
[20XX-XX-XX 12:56:34] start job
[20XX-XX-XX 12:56:36] start foreach
[20XX-XX-XX 12:56:41] This is the 100 record
[20XX-XX-XX 12:56:47] This is the 200 record
[20XX-XX-XX 12:56:52] This is the 300 record
[20XX-XX-XX 12:56:57] This is the 400 record
[20XX-XX-XX 12:57:04] local.ERROR: App\Jobs\SomeJob has been attempted too many times or run too long. The job may have previously timed out. {"exception":"[object] (Illuminate\\Queue\\MaxAttemptsExceededException(code: 0): App\\Jobs\\SomeJob has been attempted too many times or run too long. The job may have previously timed out. at /var/app/current/vendor/laravel/framework/src/Illuminate/Queue/Worker.php:746)"
[20XX-XX-XX 12:57:06] This is the 500 record
[20XX-XX-XX 12:57:10] This is the 600 record
...
[20XX-XX-XX 13:09:46] This is the 11400 record
[20XX-XX-XX 13:09:52] This is the 11500 record
[20XX-XX-XX 13:09:53] Maximum execution time of 60 seconds exceeded {"userId":144,"exception":"[object] (Symfony\\Component\\ErrorHandler\\Error\\FatalError(code: 0): Maximum execution time of 60 seconds exceeded at /var/app/current/vendor/laravel/framework/src/Illuminate/Collections/Arr.php:314)"
[20XX-XX-XX 13:16:20] local.INFO: end foreach
[20XX-XX-XX 13:16:23] local.INFO: end job
Как видно из журналов, задание выполнялось, и примерно через 30-60 секунд Laravel выдает исключение MaxAttemptsExceededException. Затем в 13:09:53 получите еще одно исключение FatalError, в котором говорится, что превышено время ожидания 60 секунд, и журнал остановлен. Он продолжается после 13:16:20, чтобы завершить процесс...
Для всех, кому интересно, какая конфигурация для очереди внутри таблицы failed_jobs:
...,"maxTries":null,"maxExceptions":null,"failOnTimeout":false,"backoff":null,"timeout":1800,"retryUntil":null,...
Очень признателен за любой вклад и разъяснения по этому вопросу. У меня есть поиск решения, но безуспешно.
Да, у Laravel Jobs есть максимальная попытка и максимальное время повторной попытки. Но для своей конфигурации я не указывал, и не уверен, есть для этого дефолт или нет. В любом случае, я получаю это исключение, но задание все еще выполняется до завершения. Это не остановилось. См. представленные журналы. Вот в этом вопросе я не знаю.
Возможно, этот ответ поможет: stackoverflow.com/a/34487604/9636400
Спасибо за ответ @geertjanknapen, но после поиска «retry_after» и прослушивания объяснения retry_after эта конфигурация предназначена для тех случаев, когда задание laravel не завершилось неудачно или успешно через 60 секунд, а затем повторите задание. Однако в моем случае это задание фактически обрабатывается, и его выполнение заняло около 20 минут. Таким образом, исключение 60 секунд на самом деле является ложным отрицательным результатом. Как запретить laravel проверять статус задания через 60 секунд для этого конкретного задания, не затрагивая другие задания?
Я думаю, что вы сталкиваетесь с timeout (сбой задания через x секунд), но вы хотите, чтобы это влияло только на это конкретное задание. Так что на работе вы могли бы сделать public $timeout = 500;, где 500 — это время в секундах. (500 = 3 минуты, 1200 = 20 минут).
Я пытался использовать public $timeout = 1800; = 30 минут в задании, но иногда после 2 минут обработки я получаю ошибку тайм-аута 60 секунд. Что тоже неправильно, потому что 2 минуты уже прошли как 60 секунд. Это очень расстраивает и сбивает с толку.
Можете ли вы обновить вопрос с кодом для полного задания и с тем, как вы выполняете эти задания? (app\Console\Kernel.php:schedule())
Я не использую расписание ядра, но вызываю метод dispatch(new SomeJob($id)); для выполнения задания.
Пожалуйста, поделитесь своими настройками конфигурации Supervisor . Кроме того, ваша работа имеет характеристики по умолчанию (use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;) и не реализует интерфейс ShouldQueue. Пожалуйста, обратитесь к Создание классов работы
Я включил конфигурацию своего супервайзера и черты для работы.
@NazhanNasir У вас все еще возникают проблемы после добавления необходимых черт и внедрения интерфейса ShouldQueue в вашу работу?
@steven7mwesigwa Да, по-прежнему возникают проблемы (ложноотрицательные ошибки), если задание выполняется более 2 минут.
Отвечает ли это на ваш вопрос? Фатальная ошибка: Превышено максимальное время выполнения 30 секунд
Я только что начал задание в [2023-02-07 17:50:57] и получил это после добавления set_time_limit(1800);: [2023-02-07 17:51:29] local.ERROR: App\Jobs\SomeJob has been attempted too many times or run too long. The job may have previously timed out. {"exception":"[object] (Illuminate\\Queue\\MaxAttemptsExceededException(code: 0)..." Я получаю это сейчас, но задание все еще обрабатывается и закончилось в [2023-02-07 17:53:35] local.INFO: end job
На основании Истечения срока действия и времени ожидания и Задание выполнялось слишком много раз или выполнялось слишком долго, вам необходимо увеличить значение retry_after до максимального количества секунд, которое разумно требуется вашим заданиям для завершения обработки.
Amazon SQS повторит задание на основе Тайм-аута видимости по умолчанию, который управляется в консоли AWS. К сожалению, у меня нет опыта работы с SQS, чтобы помочь вам увеличить «Время ожидания видимости по умолчанию». Наконец, убедитесь, что вы действительно используете правильное соединение с очередью, проверив, установлен ли QUEUE_CONNECTION=sqs в вашем файле .env.
@steven7mwesigwa Тайм-аут видимости по умолчанию для SQS сработал! Мой SQS был установлен на 30 секунд, поэтому каждые 120 секунд (4 повторения) laravel отправляет ошибку MaxAttemptsExceededException. Я перешел на 1800 и больше не вижу ошибки. Большое спасибо! Вы можете отправить свой ответ, чтобы я мог отметить его как решение.






- (Symfony\Component\ErrorHandler\Error\FatalError(код: 0): максимум время выполнения 60 секунд превышено в /var/app/current/vendor/laravel/framework/src/Illuminate/Collections/Arr.php:314)
Во-первых, что касается вышеуказанной ошибки, вам необходимо увеличить конфигурацию PHP «максимальное время выполнения».
Фатальная ошибка: Превышено максимальное время выполнения 30 секунд
Это можно решить, добавив set_time_limit(1800); в начало тела метода SomeJob::handle(...) задания.
- Job\SomeJob выполнялся слишком много раз или выполнялся слишком долго. задание могло быть ранее тайм-аут. {"исключение":"[объект] (Illuminate\Queue\MaxAttemptsExceededException (код: 0)
Во-вторых, что касается вышеуказанной ошибки,
На основании Истечения срока действия и времени ожидания и Задание выполнялось слишком много раз или выполнялось слишком долго, вам необходимо увеличить значение retry_after до максимального количества секунд, которое разумно требуется вашим заданиям для завершения обработки.
Amazon SQS повторит задание на основе Тайм-аута видимости по умолчанию, который управляется в консоли AWS. Время ожидания видимости сообщения по умолчанию составляет 30 секунд. Минимум 0 секунд. Максимум 12 часов.
Увеличение значения «Тайм-аут видимости Amazon SQS по умолчанию» до более высокого значения на пару секунд больше, чем ваша конфигурация тайм-аута очереди, должна решить эту проблему. Если ваш параметр --timeout длиннее вашего retry_after или значения конфигурации «Тайм-аут видимости Amazon SQS по умолчанию», ваши задания могут быть обработаны дважды.
См.: Настройка параметров очереди (консоль) для получения информации о настройке «тайм-аута видимости Amazon SQS» для очереди с помощью консоли.
Наконец, убедитесь, что вы действительно используете правильное соединение с очередью, проверив, установлен ли QUEUE_CONNECTION=sqs в вашем файле .env.
Примечание для других, столкнувшихся с той же проблемой: если вы установите для параметра супервизора значение 1800 секунд, то время ожидания видимости по умолчанию в SQS также должно быть равно 1800 секундам.
Задания Laravel имеют максимальное количество попыток и/или максимальное время повтора, Laravel docs для получения дополнительной информации.