spiral/roadrunner-tcp
TCP transport for Spiral RoadRunner applications. Provides a simple PHP TCP server/worker communication layer and helpers to read/write payloads over sockets, enabling custom RPC-style messaging and integration where HTTP isn’t required.
Install the package:
composer require spiral/roadrunner-tcp
Ensure you’re using a compatible version of RoadRunner (v2+), as this package extends the core RoadRunner framework.
Start with the TCP server:
In your .rr.yaml (or .rr.json), configure a TCP worker pool targeting your PHP worker script:
servers:
tcp:
address: "127.0.0.1:6001"
transport: "tcp"
pool:
limit: 10
workers:
command: "php worker.php"
pool: { num_workers: 4, ttl: 0 }
Your worker.php should be a long-running process that uses Spiral\RoadRunner\TCP\Worker to handle requests.
First use case: Build a simple binary RPC service.
In your worker, instantiate the TCP worker and loop on incoming requests:
use Spiral\RoadRunner\TCP\Worker;
use Relay\Relay;
$worker = new Worker();
$relay = new Relay();
while ($relay->accept($worker)) {
$request = $worker->read(); // Read raw TCP message
// Decode, process, encode...
$response = 'OK';
$worker->write($response);
}
Request/Response over TCP: Leverage the Worker class for bidirectional communication. Use read() and write() for message framing—ensure your client and worker agree on a framing protocol (e.g., length-prefixed, newline-delimited, or custom binary headers).
Binary protocol support: Encode/decode custom payloads (e.g., protobuf, msgpack, or application-specific structs). Since this is raw TCP, framing is your responsibility—common patterns include:
Worker orchestration: Combine with RoadRunner’s stateless workers for throughput. Assign TCP worker pools dedicated to specific tasks (e.g., tcp:redis-proxy, tcp:cache-warmup) with pool limits and TTL to prevent memory bloat.
Client-side usage: For internal CLI tools or other services, use Spiral\RoadRunner\TCP\Client to connect:
$client = new Spiral\RoadRunner\TCP\Client('127.0.0.1:6001');
$client->write('ping');
$response = $client->read();
Integration with Laravel/Spiral apps: Wrap TCP logic in a command or service class. Register it as a RoadRunner worker via config or CLI (e.g., rr serve -c .rr.tcp.yaml).
Framing is manual: Unlike HTTP, TCP doesn’t define message boundaries. You must implement (or adapt) a framing strategy. Use a dedicated protocol encoder/decoder to avoid silent corruption. Consider libraries like symfony/serializer + custom encoders, or low-level pack()/unpack().
Long-lived workers = memory vigilance: RoadRunner reuses workers—but PHP memory leaks compound over time. Use gc_collect_cycles(), avoid static caches without eviction, and set ttl or max_jobs to restart workers proactively.
Error handling & timeouts: TCP read()/write() can block. Use stream_set_timeout() on worker sockets if available, or implement async timeouts using RoadRunner’s event loop (if combined with spiral/roadrunner-stream or ReactPHP).
No built-in discovery or load balancing: This package is transport-only. Pair with external tools (e.g., nginx/tcp, HAProxy, or consul-template) for failover. For internal discovery, use Redis or a local config file.
Extension points: You can hook into RoadRunner’s worker lifecycle (e.g., via spiral/roadrunner's WorkerEvents) to flush buffers or release resources on shutdown.
Debugging tip: Log raw bytes during development using bin2hex() or unpack('C*', ...). Use nc -v localhost 6001 to manually send test requests while prototyping protocols.
How can I help you explore Laravel packages today?