Продвинутое

Фоновые задачи (threads)

Всё, что выполняется вне HTTP-запроса — разовые задачи, форкающиеся воркеры, демоны, WebSocket-серверы — в Winter описывается единой моделью Dispatchable. Вы наследуете нужный стереотип, кладёте код в resolution() и запускаете его на переднем плане или в фоне.

Стереотипы Job · Process · Daemon · WebSocketЗапуск dispatch() / start()Требует ext-pcntl

Что такое фоновые задачи и зачем

Фоновая задача — работа, выполняемая отдельно от обработки запроса.

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

Решение. Winter форкает такую работу в отдельный процесс через модель Dispatchable: разовые джобы, форкающиеся процессы, демоны-синглтоны, WebSocket. Об этом и раздел.

Четыре стереотипа

Стереотип Форк Блокировка-синглтон Для чего
Job нет нет Разовая задача (письмо, обработка)
Process да нет Форкающийся пул воркеров
Daemon да кластерная блокировка Долгоживущий демон-синглтон
WebSocket одиночный цикл WebSocket-сервер

Все живут в Flytachi\Winter\K2\Stereotype. Скаффолд — флагами make: -J Job, -P Process, -N Daemon, -W WebSocket.

Контракт Dispatchable

php
public static function dispatch(mixed $data = null): int;   // фон → PID потомка
public static function start(mixed $data = null): void;      // передний план, блокирует
public function resolution(mixed $data = null): void;        // ВАШ код
  • start() — выполняет в текущем процессе и блокирует.
  • dispatch() — форкает в фон, возвращает PID потомка. Данные передаются через внутреннее файловое хранилище (потомок читает их деструктивно).
  • resolution() — метод, который вы реализуете.

Пример: Job

main/Threads/SendInvoice.php
<?php

namespace Main\Threads;

use Flytachi\Winter\DI\Attribute\Autowired;
use Flytachi\Winter\K2\Stereotype\Job;

class SendInvoice extends Job
{
  #[Autowired] private MailService $mail;

  public function resolution(mixed $data = null): void
  {
      $this->mail->sendInvoice($data['orderId']);
  }
}
php
SendInvoice::dispatch(['orderId' => 42]);   // фон, вернёт PID
SendInvoice::start(['orderId' => 42]);       // передний план, блокирует

DI в потомке

В форкнутом потомке зависимости разрешаются заново через контейнер (Container::make) — конструктор и #[Autowired] работают. Но живые ресурсы (открытые соединения) в потомок не наследуются — джоба сама достаёт, что нужно.

Демоны

Daemon — долгоживущий процесс с кластерной блокировкой (один экземпляр на машину) и потоковой обработкой с ограничением параллелизма:

php
use Flytachi\Winter\K2\Stereotype\Daemon;

class Cleanup extends Daemon
{
  public function resolution(mixed $data = null): void
  {
      $this->prepare(streamRps: 20);              // до 20 параллельных форков
      $this->streaming(function () {
          $this->fork(fn () => $this->cleanOne());
      });
  }
}

Управление — командой thread: start / stop / status (см. ниже).

Запуск из консоли

bash
php call thread main.threads.SendInvoice       # передний план
php call thread main.threads.SendInvoice -d    # фон
php call thread main.threads.Cleanup start     # демон в фоне
php call thread main.threads.Cleanup status -v # состояние демона
php call thread list                           # все Dispatchable-классы

Полностью про команду — на странице CLI → thread.

Дальше