Життєвий цикл запиту
Як JsonRpcBundle обробляє запит від моменту отримання до відповіді — для кожної точки входу. Знадобиться, щоб писати лістенери подій і розширення.
HTTP POST
Маршрути бандла оголошені як GET (для документації); POST-запити на ту саму адресу перехоплюються на kernel.request ще до роутингу й контролера.
kernel.request, пріоритет 240 —SymfonyFlowListener::initHttpRequest- визначає групу API з маршруту й записує в
ApiGroupContext; OPTIONS(CORS preflight) отримує порожню відповідь;- для POST створює carrier
RpcFromHttp(розбір JSON; тіло, що починається з[, — batch), токен-холдерHttpTokenHolderі перевіряє токен (якщоprotected_api: true) — валідатором групи або базовим.
- визначає групу API з маршруту й записує в
kernel.request, пріоритет 230 —RpcRequestHandler::handle()- batch — перевірка
batch.enabledі порожнього batch, вибір стратегії (IBatchStrategy) і виконання елементів; - одиночний запит — перевірка
jsonrpc: "2.0", подіяrpc.request, далі одна з гілок:- запит з
$rpc.callback— миттєва відповідь-заглушка йrpc.pre_response, виконання відкладається; - notification — заглушка й
rpc.pre_response, виконання відкладається; - звичайний запит —
RpcServer::handle().
- запит з
- batch — перевірка
RpcServer::handle()- пошук методу в карті сервісів (з урахуванням групи й версії); невідомий метод — помилка
-32601; - кеш: для методів з
#[RPC\Cache](і без$rpc.ignoreCache) готова відповідь повертається одразу, без подій виконання; - подія
rpc.pre_execute, на якій послідовно: формування аргументів (1002), валідація (1001), конвертація (1000),#[RPC\Lock](-100), виклик методу (-1000); - успіх —
rpc.pre_response, потімrpc.post_execute; помилка —rpc.error, потімrpc.pre_response; - збереження відповіді в кеш (для
#[RPC\Cache]).
- пошук методу в карті сервісів (з урахуванням групи й версії); невідомий метод — помилка
- Відповідь — JSON;
204без тіла, якщо відповідати нічого (notification, batch лише з notifications). Роутер і контролер Symfony для POST не запускаються. kernel.terminate— післяfastcgi_finish_request()(PHP-FPM) для кожного успішно виконаного запиту генеруєтьсяrpc.post_response: інвалідація#[RPC\CacheRelation]і виконання notifications / callback. ПотімApiGroupContextскидається.
Помилки, що виникли до виконання методу (недійсний JSON, токен, невідомий метод тощо), перетворює на відповідь SymfonyErrorListener на kernel.exception. HTTP-статус: 401 — токен не передано, 403 — недійсний, 500 — недійсний JSON, інакше 200.
HTTP GET (документація)
- Пріоритет 240 — визначення групи; якщо
protected_doc: true— перевірка токена. - Пріоритет 8 — вибір мови з
Accept-Language(RpcLocaleNegotiator). - Пріоритет 4 — поза debug-режимом відповідь з кешу:
304дляIf-None-Matchабо збережений документ. ApiController::openrpc()/postman()будує документ для версії та групи.kernel.response—Content-Language,Vary: Accept-Language,ETag, збереження в кеш (лише для200).
RPC-події на цьому шляху не генеруються.
Консоль: ufo:rpc:process
Команда не має власної логіки — запит обробляє лістенер console.command (пріоритет 999999):
- читання JSON (аргумент, stdin
-або зміннаUFO_RPC_REQUEST); - група з
--api-group, версія з--ver; - carrier
RpcFromCli, токен з опції--<token_name>або змінноїUFO_RPC_TOKEN; RpcRequestHandler::handle()— далі як у HTTP;- вивід JSON у stdout;
console.terminate—rpc.post_response.
Помилки обробляються на console.error і проходять через rpc.error.
Symfony Messenger
RpcAsyncProcessor — обробник повідомлень RpcAsyncRequest:
- група з
$message->apiGroup, токен з$message->token, перевірка токена; - виклик
RpcServer::handle()напряму.
Тож для запитів з черги немає rpc.request і rpc.post_response; rpc.pre_execute, rpc.pre_response, rpc.post_execute і rpc.error генеруються. Помилки токена й невідомого методу стають помилками обробки повідомлення Messenger (повторні спроби, failure transport).
Batch
- Процесний режим (
batch.process: true) — кожен готовий елемент запускається в дочірньому процесіufo:rpc:process -(запит через stdin, токен через змінну оточення, група й версія — опціями), не більшеbatch.process_countодночасно. Кожен дочірній процес — повний консольний цикл зі своїми подіями. Батьківський процес на виводі кожного генеруєrpc.async_output; коли розв'язується залежність@FROM, запускає наступні елементи. - Inline-режим (
batch.process: false) — елементи по черзі в поточному процесі черезRpcExecutor, кожен з власним пулом подій;rpc.post_response— одразу після елемента. - Елементи, залежність яких не виконалась, завершуються помилкою в батьківському процесі.
Notifications і $rpc.callback
- При прийомі —
rpc.requestіrpc.pre_responseіз заглушкою; клієнт отримує204(notification) або{"async": true, "callback": …}. - На
rpc.post_response(пріоритет 10)AsyncServerEventListenerвиконує метод — повний циклrpc.pre_execute→rpc.pre_response→rpc.post_execute/rpc.error. - Для callback результат надсилається POST-запитом на вказаний URL.
З notification.process: true одиночний HTTP-запит передається від'єднаному процесу ufo:rpc:process @env: воркер чекає підтвердження до 3 секунд, інакше виконує запит сам. Кількість від'єднаних процесів обмежує notification.process_count.
RpcExecutor (адаптери)
Ufo\JsonRpcBundle\Server\RpcExecutor::execute(IRpcRequestCarrier $carrier): RpcResponse — точка входу для розширень, що приймають запити не через маршрут бандла (REST, MCP):
- встановлює carrier, генерує
rpc.requestі далі як в одиночному запиті; - будь-який виняток проходить через
rpc.errorі повертається як відповідь з помилкою; rpc.post_response— наkernel.terminateосновного HTTP-запиту;- токен не перевіряє — це робить адаптер.
Де що зберігається під час запиту
| Сервіс | Що містить |
|---|---|
RequestCarrier |
Поточний carrier (RpcFromHttp, RpcFromCli, carrier адаптера) і об'єкти запиту |
ApiGroupContext |
Група API поточного запиту |
IRpcSecurity |
Токен-холдер поточного запиту |
RpcEventFactory |
Усі події, згенеровані для запиту (getEvent(), getEvents()) |
PostResponseQueue |
Запити, для яких буде згенеровано rpc.post_response |
У довгоживучих воркерах ці сервіси скидаються між запитами (ResetInterface).