Как можно сохранить последовательность выполнения задач в очереди?

У меня возникла проблема с порядком обработки заданий в очереди. Я добавил три задания в очередь с интервалом 2-3 секунды: 

1. Первое задание выполняется 30 секунд и записывает в лог цифру 1:
   ```php
   public function handle()
   {
       sleep(30);
       file_put_contents(storage_path('/logs/journal.log'), 1, FILE_APPEND);
   }
   ```

2. Второе задание выполняется 20 секунд и записывает в лог цифру 2:
   ```php
   public function handle()
   {
       sleep(20);
       file_put_contents(storage_path('/logs/journal.log'), 2, FILE_APPEND);
   }
   ```

3. Третье задание выполняется 2 секунды и записывает в лог цифру 3:
   ```php
   public function handle()
   {
       sleep(2);
       file_put_contents(storage_path('/logs/journal.log'), 3, FILE_APPEND);
   }
   ```

Таким образом, при запуске нескольких воркеров в журнале появляется запись в порядке 321, тогда как я хотел бы, чтобы выводился порядок 123. Как я могу обеспечить соблюдение очередности выполнения заданий, чтобы задания, добавленные позже, но имеющие меньшее время выполнения, не опережали предыдущие?
  • 24 октября 2024 г. 15:42
Ответы на вопрос 2
Для того чтобы обеспечить последовательное выполнение задач в очереди и избежать ситуации, когда более короткие задачи опережают более длительные, можно использовать несколько подходов. Вот некоторые из них:

### 1. Использование однотипного воркера

Запустите только один воркер, чтобы гарантировать, что задания выполняются по очереди. Это самый простой и эффективный способ для обеспечения последовательности:

```bash
php artisan queue:work
```

### 2. Приоритеты очередей

В Laravel можно настроить приоритеты для очередей. Вы можете создать несколько очередей для задач разной сложности и использовать более приоритетные очереди для длительных задач. Задания с более высоким приоритетом будут обрабатываться раньше:

```php
dispatch(new LongTask())->onQueue('high'); // Для долгих задач
dispatch(new ShortTask())->onQueue('low'); // Для коротких задач
```

А затем запускаете воркеры для каждой очереди:

```bash
php artisan queue:work --queue=high
php artisan queue:work --queue=low
```

### 3. Использование `waitUntil` или других механизмов синхронизации

Вы можете контролировать выполнение заданий самостоятельно, добавляя в них логику, которая будет запретить запуск следующих задач, пока не завершилась предыдущая. 

## Пример кода с использованием блокировок:

```php
use Illuminate\Support\Facades\Lock;

class LongTask implements ShouldQueue
{
    public function handle()
    {
        $lock = Lock::if(!Lock::get('long_task_lock', 0)) {
            Lock::put('long_task_lock', 1);
            sleep(30);
            file_put_contents(storage_path('/logs/journal.log'), 1, FILE_APPEND);
            Lock::forget('long_task_lock');
        });
    }
}

class ShortTask implements ShouldQueue
{
    public function handle()
    {
        $lock = Lock::if(!Lock::get('long_task_lock', 0)) {
            Lock::put('long_task_lock', 1);
            sleep(20);
            file_put_contents(storage_path('/logs/journal.log'), 2, FILE_APPEND);
            Lock::forget('long_task_lock');
        });
    }
}
```

Обратите внимание, что этот метод может вызвать задержки при обработке задач, и вам нужно учитывать возможность блокировки.

### 4. Добавление временной метки или порядка в очередь

При добавлении задания в очередь, 기록ите в базе данных временную метку или последовательный номер задач, а затем, когда воркер достает задание, он может проверять этот порядок и обрабатывать их соответственно. Это потребует дополнительной логики по управлению очередями.

### Итог

Наиболее простое решение — это использование одного воркера или ограничения на количество одновременно выполняемых задач. При использовании нескольких воркеров для более требования к производительности, вам нужно будет более серьёзно подойти к управлению задачами, чтобы избежать проблем с порядком выполнения.
Надо начать читать документацию и не изобретать велосипедов с БД и прочим.
Похожие вопросы