Membangun gRPC service di atas RoadRunner: mendefinisikan API dengan file .proto, menjalankan server gRPC PHP memakai spiral/roadrunner-grpc dan protoc-gen-php-grpc, mengimplementasikan service, lalu menguji dengan client gRPC.

Setelah di episode 15 kita mengamankan HTTP, pada episode kali ini kita membuka jalur komunikasi kedua: gRPC. RoadRunner bisa menjadi gRPC server dengan worker PHP yang mengimplementasikan service — pola yang sangat berguna untuk komunikasi antar-service (microservice) yang membutuhkan performa dan kontrak API yang tegas.
Mengapa episode ini penting? Karena gRPC dan RoadRunner saling melengkapi: gRPC membutuhkan server yang mampu menangani banyak koneksi long-lived dan streaming, persis kekuatan Go — sementara implementasi service tetap ditulis dalam PHP yang kalian kuasai. Ini membuka RoadRunner untuk pola microservice.
.proto — bahasa kontrak yang sama untuk semua bahasa.Komponen yang dibutuhkan:
| Komponen | Peran |
|---|---|
spiral/roadrunner-grpc | Server gRPC PHP (worker) |
protoc-gen-php-grpc | Generator kode PHP dari .proto |
google/protobuf | Runtime protobuf PHP |
Plugin grpc RoadRunner | Server Go yang menerima koneksi gRPC |
.protoBuat file api/proto/echo.proto:
syntax = "proto3";
package echo.v1;
option php_namespace = "App\\Grpc";
option php_metadata_namespace = "App\\Grpc\\GPBMetadata";
service EchoService {
rpc Ping (PingRequest) returns (PingResponse);
}
message PingRequest {
string message = 1;
}
message PingResponse {
string message = 1;
int32 pid = 2;
}option php_namespace menentukan namespace kelas PHP yang di-generate. Ini bagian dari kontrak: client dan server berbicara dengan skema yang sama persis.
Generate kelas service dan message dari .proto:
composer require google/protobuf spiral/roadrunner-grpc
mkdir -p generated
protoc --php_out=generated --php-grpc_out=generated \
-I api/proto api/proto/echo.protoHasilnya: kelas message (PingRequest, PingResponse) dan kelas interface service (EchoServiceInterface) yang harus kalian implementasikan. File .proto baru memerlukan regenerasi.
Implementasikan interface yang di-generate:
<?php
declare(strict_types=1);
use App\Grpc\EchoServiceInterface;
use Spiral\RoadRunner\GRPC\ContextInterface;
use Spiral\RoadRunner\GRPC\Server;
use Spiral\RoadRunner\Worker;
require __DIR__ . '/vendor/autoload.php';
class EchoService implements EchoServiceInterface
{
public function Ping(ContextInterface $ctx, PingRequest $in): PingResponse
{
$response = new PingResponse();
$response->setMessage($in->getMessage());
$response->setPid(getmypid());
return $response;
}
}
$server = new Server();
$server->registerService(EchoServiceInterface::class, new EchoService());
$server->serve(Worker::create());Catatan penting: Server::serve() masuk ke loop internal — worker gRPC tidak memakai pola while (waitRequest()) manual. Service di-register sekali, dan plugin gRPC menyerahkan request masuk ke worker yang sedang idle.
grpc:
listen: tcp://0.0.0.0:9001
proto:
- "api/proto/echo.proto"
pool:
num_workers: 4
max_worker_memory: 128
max_msg_size: 10485760
max_send_msg_size: 10485760| Key | Fungsi |
|---|---|
listen | Alamat listen gRPC (port terpisah dari HTTP) |
proto | Daftar file .proto yang dimuat server |
pool | Worker pool untuk gRPC (terpisah dari pool HTTP) |
max_msg_size | Limit ukuran pesan masuk (byte) |
max_send_msg_size | Limit ukuran pesan keluar (byte) |
Worker untuk gRPC dijalankan dengan server.command yang sama? Tidak — gRPC membutuhkan command sendiri. Karena worker gRPC dan HTTP berbeda file, definisikan pool worker yang berbeda:
server:
command: "php worker-http.php" # pool default
relay: pipesUntuk membedakan worker, plugin gRPC memakai key grpc.pool yang membutuhkan command khusus di blok masing-masing pool (lihat rr config:info). Contoh lengkapnya di dokumentasi spiral/roadrunner-grpc.
Test dengan grpcurl (tool CLI gRPC) atau client PHP:
grpcurl -plaintext -d '{"message": "hello"}' \
127.0.0.1:9001 echo.v1.EchoService/PingClient PHP memakai library grpc:
$client = new EchoServiceClient('127.0.0.1:9001', ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
$request = new PingRequest();
$request->setMessage('ping dari client');
[$response, $status] = $client->Ping($request);
echo $response->getMessage(); // echo dari serverTip
Verifikasi port dan proto yang aktif: ./rr grpc:list menampilkan service gRPC yang terdaftar. Ini cara cepat memastikan file .proto terbaca benar sebelum menulis client.
| Aspek | gRPC | HTTP REST |
|---|---|---|
| Protokol | HTTP/2 + protobuf | HTTP/1.1-2 + JSON |
| Kontrak | Wajib .proto | Opsional (OpenAPI) |
| Performa | Lebih cepat, payload kecil | Lebih lambat, JSON verbosa |
| Browser | Tidak langsung | Bisa |
| Cocok untuk | Microservice-to-microservice | API publik/browser |
RoadRunner melayani keduanya sekaligus di satu binary — HTTP di satu port, gRPC di port lain. Kalian bisa memakai gRPC untuk internal dan REST untuk publik.
Pada episode 16 ini, kalian telah membangun gRPC service di atas RoadRunner.
Inti yang harus dibawa pulang:
.proto.grpc menerima koneksi; worker PHP mengimplementasikan interface hasil generate.protoc --php-grpc_out menghasilkan kelas service dan message.grpcurl dan ./rr grpc:list.Di episode 17 selanjutnya, kita belajar keamanan & hardening — membatasi worker, request timeouts, rate limiting, dan proteksi API endpoint dengan pola autentikasi yang benar. Sampai jumpa di episode 17!