API gateway: PHP, Node, Go, Rust — gRPC і RabbitMQ

У мікросервісній архітектурі «API gateway» зазвичай означає публічний периметр: TLS, маршрутизація, автентифікація, обмеження швидкості й іноді BFF, який збирає відповідь для вебу чи мобільного клієнта. За цим шаром сервіси спілкуються інакше — часто через gRPC (синхронний RPC) або брокери повідомлень, зокрема RabbitMQ (асинхронна доставка). Жоден варіант не є «єдино правильним стеком»: відрізняються вартість експлуатації, навички команди та сценарії відмов.

Пов’язані матеріали: PHP на сервері — FPM, Swoole, воркери · Sail: RabbitMQ і черги

Зміст


Що насправді робить шлюз

Типові обов’язки:

  1. Вхід — HTTP/HTTPS з інтернету; HTTP/3 часто закінчується на балансувальнику.
  2. Політики — перевірка JWT, API-ключі, списки IP, інтеграція з WAF.
  3. Форма трафіку — rate limiting, обмеження розміру тіла, таймаути.
  4. Маршрутизація — префікси шляхів до кластерів (/billing/* → billing).
  5. Агрегація (за потреби) — 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 затримки шлюзу.

Додаткові матеріали