API gateway: PHP, Node, Go, Rust — gRPC і RabbitMQ
У мікросервісній архітектурі «API gateway» зазвичай означає публічний периметр: TLS, маршрутизація, автентифікація, обмеження швидкості й іноді BFF, який збирає відповідь для вебу чи мобільного клієнта. За цим шаром сервіси спілкуються інакше — часто через gRPC (синхронний RPC) або брокери повідомлень, зокрема RabbitMQ (асинхронна доставка). Жоден варіант не є «єдино правильним стеком»: відрізняються вартість експлуатації, навички команди та сценарії відмов.
Пов’язані матеріали: PHP на сервері — FPM, Swoole, воркери · Sail: RabbitMQ і черги
Зміст
- Що насправді робить шлюз
- PHP як шар шлюза
- Node.js на периметрі
- Go для шлюзів і сайдкарів
- Rust, коли важливі мікросекунди
- Внутрішні виклики: gRPC
- Внутрішня робота: RabbitMQ
- Коли поєднувати gRPC і черги
- Зведена таблиця
- Рецепти
Що насправді робить шлюз
Типові обов’язки:
- Вхід — HTTP/HTTPS з інтернету; HTTP/3 часто закінчується на балансувальнику.
- Політики — перевірка JWT, API-ключі, списки IP, інтеграція з WAF.
- Форма трафіку — rate limiting, обмеження розміру тіла, таймаути.
- Маршрутизація — префікси шляхів до кластерів (
/billing/*→ billing). - Агрегація (за потреби) — BFF викликає кілька бекендів і повертає один JSON.
Пункти (1)–(5) можна реалізувати в прикладному коді (PHP, Node, Go, Rust) або віддати Envoy, Traefik, Kong, NGINX чи хмарному API gateway, залишивши в мові лише BFF. У проді часто суміш: nginx знімає TLS, Kong вішає плагіни, невеликий сервіс на Go або PHP додає доменну авторизацію.
PHP як шар шлюза
Коли доречно
- Команда вже веде Laravel або Symfony; хочеться одного контуру для публічного HTTP і частини оркестрації.
- Шлюз — не «глухий проксі на мільйонах RPS», а місце для сесій, OAuth, HTML-помилок або серверних фрагментів UI.
- Підходить модель FPM на запит (або Octane) і горизонтальне масштабування за балансувальником.
Плюси
- Швидкі фічі: авторизація, валідація, переклади, бізнес-правила.
- Екосистема: HTTP-клієнти, OpenAPI, черги для побічних ефектів.
- Простіше знайти людей і робити рев’ю, ніж при повністю поліглотному периметрі.
Мінуси
- У FPM дорожчий старт запиту, ніж у крихітного Go-бінарника (зменшується Opcache, preload, акуратним автозавантаженням).
- Легко «покласти» пул воркерів важкими синхронними викликами в middleware.
- Масовий WebSocket з однієї машини може вести до Swoole/Octane або окремого проксі.
Міні-рецепт (у дусі Laravel)
Групи маршрутів з throttle і auth; назовні — HTTP-клієнт:
<?php
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Route;
Route::middleware(['throttle:api', 'auth:sanctum'])->prefix('v1')->group(function () {
Route::get('/orders/{id}', function (string $id) {
$response = Http::timeout(3)
->withHeaders(['X-Internal-Token' => config('services.billing.token')])
->get(config('services.billing.url')."/orders/{$id}");
abort_unless($response->successful(), $response->status());
return $response->json();
});
});
Пам’ять і стабільність: ставтеся до шлюзу як до високонавантаженого PHP — не кешуйте без меж дані користувача в static, скрізь задавайте таймаути вихідним викликам, використовуйте pm.max_requests (FPM) або перезапуск воркерів (Octane), якщо під навантаженням «повзе» RSS.
Node.js на периметрі
Коли доречно
- Потрібен тонкий BFF із купою паралельних HTTP до апстрімів.
- У шлюз заходять фронтендери; JSON і SSR-інструменти звичні.
- Потрібна велика екосистема npm (OpenTelemetry, GraphQL, WebSocket).
Плюси
- Природна модель для багатьох паралельних вихідних HTTP на
async/await. - Дуже швидкий цикл для збирання API і прототипів.
Мінуси
- Потрібна дисципліна: блокування event loop важким CPU або синхронним I/O б’є по всіх запитах.
- Дерево залежностей і ризики supply-chain без пінів і аудиту.
- Нативні аддони й апгрейди рантайму додають операційну поверхню.
Міні-рецепт (накид Fastify)
npm init -y
npm install fastify @fastify/http-proxy
import Fastify from 'fastify';
import proxy from '@fastify/http-proxy';
const app = Fastify({ logger: true });
app.register(proxy, {
upstream: 'http://billing.internal',
prefix: '/billing',
rewritePrefix: '/v1',
});
await app.listen({ port: 3000, host: '0.0.0.0' });
Go для шлюзів і сайдкарів
Коли доречно
- Потрібен один статичний бінарник, передбачуваний GC, простий образ для Linux.
- Периметр або внутрішній шар говорить gRPC або реалізує власну балансування.
- Платформена команда стандартизує бібліотеки.
Плюси
- Сильні примітиви конкурентності для I/O-bound шлюзів.
- Культура спостережуваності (pprof, OpenTelemetry).
- Зрілі grpc-go та grpc-gateway (HTTP JSON → gRPC).
Мінуси
- Не всім подобаються шаблони помилок і багатослівність порівняно з PHP/Python.
- Codegen з protobuf додає кроки в CI.
Міні-рецепт (grpcurl)
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
grpcurl -plaintext localhost:50051 list
Rust, коли важливі мікросекунди
Коли доречно
- Жорсткі бюджети затримки, важливі алокації або парсинг у критичному для безпеки місці.
- Готові платити часом компіляції і суворим borrow checker.
Плюси
- Передбачувана продуктивність і безпека пам’яті без історії з паузами GC.
- Tonic (gRPC) і Axum (HTTP) — поширений вибір у нових проєктах.
Мінуси
- Онбординг дорожчий, ніж у PHP або Node для типової веб-команди.
- Повільніші ітерації, якщо кожен деплой чекає повної перезбірки в CI.
Типовий компроміс: Rust або Go для одного сервісу політик/автентифікації, PHP — для основного CRUD.
Внутрішні виклики: gRPC
gRPC зазвичай означає HTTP/2, контракти Protobuf і згенеровані заглушки в кожній мові.
Плюси
- Жорсткий контракт — поля й типи явні; ламальні зміни видно на етапі codegen.
- Компактніше JSON на дроті; є стримінг для великих даних.
- Метадані для трейсингу й авторизації на кожен виклик.
Мінуси
- Браузер не говорить «чистий» gRPC — потрібен gRPC-Web і проксі.
- Дебажити складніше, ніж
curlдо JSON, без grpcurl і гарних логів/метрик. - Балансувальники мають коректно маршрутизувати HTTP/2 до gRPC.
PHP: офіційне або community-розширення gRPC + згенеровані класи; явно задавайте таймаути й ліміти ретраїв. Для нових внутрішніх API домовтеся про deadline (grpc-timeout) між сервісами.
Внутрішня робота: RabbitMQ
RabbitMQ — брокер AMQP: видавці шлють у exchange, черги зв’язуються ключами маршрутизації, споживачі роблять ack / nack.
Плюси
- Розв’язка в часі — піки буферизуються в брокері.
- Патерни: черга задач, pub/sub, topic, відкладена доставка (обережно з плагінами).
- Зріла експлуатація: кластер, дзеркалювання (classic), quorum queues у сучасних схемах.
Мінуси
- Це не БД — якщо консьюмери лежать, черги ростуть; потрібні моніторинг і DLQ.
- Рівно один раз наскрізь через усю систему не гарантується; проєктуйте ідемпотентних обробників.
- Розбір «зникло повідомлення» без correlation id і структурних логів перетворюється на квест.
PHP (Laravel): QUEUE_CONNECTION=rabbitmq з пакетом на кшталт vladimir-yuldashev/laravel-queue-rabbitmq; локально див. гайд Sail і черги. Не виставляйте RabbitMQ в інтернет без TLS і облікових записів.
Коли поєднувати gRPC і черги
- Команда: HTTP → шлюз → публікація «OrderPlaced» у RabbitMQ → воркери виконують. Клієнту — 202 + id або схема outbox + опитування статусу.
- Запит: HTTP → шлюз → gRPC у read-сервіс із кешем — низька затримка, синхронна відповідь.
- Саги / компенсації: обмін повідомленнями з ідемпотентними обробниками й явними таймаутами.
Не перетворюйте чергу на прихований RPC без дедлайнів: «надіслали й сподіваємося» погано переживає часткові відмови.
Зведена таблиця
| Шар / інструмент | Добре, коли… | Подумайте двічі, коли… |
|---|---|---|
| PHP-шлюз | Навички команди, багата логіка на периметрі, стек Laravel/Symfony | Потрібен майже голий проксі на екстремальному RPS |
| Node-шлюз | BFF із купою паралельного HTTP, JS-організація | Важкий CPU на гарячому шляху |
| Go-шлюз | Маленький бінарник, gRPC-сітка, платформений стандарт | Немає ресурсу супроводжувати Go |
| Rust-шлюз | Жорсткі SLA за затримкою/пам’яттю | Команда лише на PHP і потрібен швидкий MVP |
| gRPC всередині | Типізовані контракти, стримінг, поліглот | Публічний браузерний клієнт без прослоєк |
| RabbitMQ | Поглинання піків, асинхронні процеси, масштаб консьюмерів | Насправді потрібен синхронний запит-відповідь |
Рецепти
RabbitMQ локально (Docker)
docker run -d --hostname rabbit --name rabbit \
-p 5672:5672 -p 15672:15672 \
-e RABBITMQ_DEFAULT_USER=guest -e RABBITMQ_DEFAULT_PASS=guest \
rabbitmq:4-management
Веб-інтерфейс: http://localhost:15672 (у бою змініть облікові дані).
Оголошення черги через CLI
docker exec rabbit rabbitmqadmin declare queue name=orders durable=true
Мінімальний Protobuf (ілюстрація)
order.proto:
syntax = "proto3";
package billing.v1;
message GetOrderRequest { string id = 1; }
message GetOrderResponse { string id = 1; string status = 2; }
service Orders {
rpc Get(GetOrderRequest) returns (GetOrderResponse);
}
Запускайте protoc із grpc_php_plugin (і плагінами інших мов) у CI; або комітьте згенерований код, або генеруйте в Docker — оберіть одне правило для репозиторію.
Фрагмент .env Laravel для RabbitMQ
QUEUE_CONNECTION=rabbitmq
RABBITMQ_HOST=rabbit
RABBITMQ_PORT=5672
RABBITMQ_USER=guest
RABBITMQ_PASSWORD=guest
RABBITMQ_QUEUE=default
Проти «тихих» відмов
- Просувайте
X-Request-Idвід шлюзу в метадані gRPC і заголовки повідомлень. - Вішайте deadline на gRPC і TTL / DLX на критичні черги.
- В одному дашборді тримайте глибину черг, завантаження консьюмерів і p95 затримки шлюзу.
Додаткові матеріали
- Документація RabbitMQ
- Документація gRPC
- Envoy — коли периметр переважно політика й маршрутизація
- Sail: RabbitMQ і черги — рецепти на цьому сайті
- Черги Laravel (офіційна документація)