Фоновые задачи (threads)
Всё, что выполняется вне HTTP-запроса — разовые задачи, форкающиеся воркеры,
демоны, WebSocket-серверы — в Winter описывается единой моделью Dispatchable.
Вы наследуете нужный стереотип, кладёте код в resolution() и запускаете его на
переднем плане или в фоне.
Что такое фоновые задачи и зачем
Фоновая задача — работа, выполняемая отдельно от обработки запроса.
Проблема. Отправка письма, обработка загруженного файла, периодическая уборка — если делать это внутри запроса, пользователь ждёт, а таймауты рвут операцию. Некоторым задачам нужен вообще отдельный долгоживущий процесс.
Решение. Winter форкает такую работу в отдельный процесс через модель
Dispatchable: разовые джобы, форкающиеся процессы, демоны-синглтоны, WebSocket. Об
этом и раздел.
Четыре стереотипа
| Стереотип | Форк | Блокировка-синглтон | Для чего |
|---|---|---|---|
Job |
нет | нет | Разовая задача (письмо, обработка) |
Process |
да | нет | Форкающийся пул воркеров |
Daemon |
да | кластерная блокировка | Долгоживущий демон-синглтон |
WebSocket |
одиночный цикл | — | WebSocket-сервер |
Все живут в Flytachi\Winter\K2\Stereotype. Скаффолд — флагами make: -J Job,
-P Process, -N Daemon, -W WebSocket.
Контракт Dispatchable
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
<?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']);
}
}SendInvoice::dispatch(['orderId' => 42]); // фон, вернёт PID
SendInvoice::start(['orderId' => 42]); // передний план, блокируетDI в потомке
В форкнутом потомке зависимости разрешаются заново через контейнер
(Container::make) — конструктор и #[Autowired] работают. Но живые ресурсы
(открытые соединения) в потомок не наследуются — джоба сама достаёт, что нужно.
Демоны
Daemon — долгоживущий процесс с кластерной блокировкой (один экземпляр на машину)
и потоковой обработкой с ограничением параллелизма:
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 (см. ниже).
Запуск из консоли
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.
Дальше
- Джобы — паттерн разовой задачи подробно
- CLI → thread — запуск и управление
- Thread — пакет — модель процессов в глубину