spiral/roadrunner-grpc
Laravel-friendly integration for RoadRunner gRPC: run high-performance PHP gRPC servers/workers, handle protobuf-based services, and communicate with the RoadRunner runtime for fast, long-lived processes and efficient microservices.
Start by installing the package via Composer:
composer require spiral/roadrunner-grpc
Next, ensure you’re using RoadRunner v2.8+ (the package is tightly coupled to RoadRunner’s worker API). Initialize RoadRunner with a minimal config (.rr.yaml) that enables the gRPC worker:
version: '3'
rpc:
listen: 'tcp://127.0.0.1:6001'
server:
command: 'php -S 127.0.0.1:8080 -t public'
grpc:
active: true
workers:
command: 'php public/grpc_server.php' # Your gRPC entrypoint
pool:
numWorkers: 4
maxJobs: 10000
Then define your first gRPC service in a .proto file (e.g., proto/hello.proto), generate PHP stubs using protoc, and implement the generated base service class. Finally, create a minimal public/grpc_server.php bootstrap file that initializes RoadRunner’s gRPC worker loop.
Service Definition → Stub Generation Workflow: Use protoc with the PHP and gRPC plugins (via grpc/grpc and google/protobuf Composer packages) to generate interfaces. Reference the generated *ServiceInterface when implementing your service logic.
Handler Class Structure: Implement each RPC method in a service class that extends the generated abstract base class (e.g., HelloServiceBase). Keep handlers lightweight—inject domain services via constructor injection for separation of concerns.
Streaming RPC Support: Use the ServerStreamInterface and ClientStreamInterface types for bi-directional or server-streaming endpoints. Note that streaming payloads arrive asynchronously; buffer or process as needed using yield or event loops.
RoadRunner Worker Lifecycle: Services run inside persistent PHP workers (not FPM). Use constructor + __invoke() (or handle() if wrapping with middleware) to prepare per-request context. Leverage Spiral\RoadRunner\GRPC\Server::create() to boot your service instance per request.
Integration with Existing Code: Mount gRPC routes alongside HTTP routes using separate entrypoints or shared middleware (e.g., authentication via middleware chain). The package does not unify HTTP/gRPC under one route stack—design boundaries intentionally.
Missing Autoloading in stubs: Generated classes often rely on __DIR__ . '/vendor/autoload.php'. If using custom namespaces or monorepos, manually include your loader early in grpc_server.php.
PHP Worker Crashes silently: RoadRunner restarts failing workers but doesn’t expose the exception in the client. Always wrap RPC handlers in try/catch and log using Psr\Log\LoggerInterface or RR’s Spiral\RoadRunner\Logger\Logger to debug.
Proto Versioning & Breaking Changes: Protobuf doesn’t enforce backward compatibility by default. Use reserved fields and avoid deleting tags. Consider semantic versioning for service contracts and use google.api.http annotations for fallback HTTP mapping if needed.
Performance Tuning: Adjust numWorkers to match CPU cores; increase maxJobs to reduce restart overhead—but only if state is truly immutable or isolated per request. Use rr reset during dev to hot-reload service changes.
Missing Debug Info: The package doesn’t expose gRPC metadata (e.g., timeout, auth headers) by default. Access them via Spiral\RoadRunner\GRPC\Context::getMetadata() inside your handler.
Testing: Write handlers unit-testable without RoadRunner using mock context objects. For integration, use grpcurl or Go’s grpctest with your local RoadRunner endpoint. Avoid relying on PHP’s built-in server—it won’t run gRPC.
How can I help you explore Laravel packages today?