leberknecht/amqp-rpc-transporter-bundle
Symfony bundle that wires AMQP into your RPC transport layer, letting services send and receive remote procedure calls over RabbitMQ-style message queues with minimal setup.
This is a Symfony Messenger transport, not a Laravel package. There is no service provider, no config/queue.php driver, and no Illuminate\Queue integration. If you are working in a Laravel codebase, the first decision is whether you actually need this bundle or whether you should solve the problem with php-amqplib/enqueue directly inside Laravel. Use this bundle only if you are (a) in a Symfony app, or (b) deliberately booting a standalone Messenger bus inside a Laravel service provider.
Assuming a Symfony host app:
composer require leberknecht/amqp-rpc-transporter-bundle
pecl install amqp # ext-amqp is required; this bundle is built on the C extension
Register the bundle in config/bundles.php (Symfony 4+):
return [
// ...
Leberknecht\AmqpRpcTransporterBundle\LeberknechtAmqpRpcTransporterBundle::class => ['all' => true],
];
Configure a transport in config/packages/messenger.yaml using whatever DSN scheme the bundle's TransportFactory::supports() registers (open that file first — the scheme is the single most important thing to confirm, likely amqp-rpc://):
framework:
messenger:
transports:
rpc:
dsn: '%env(RPC_DSN)%'
options:
queue: rpc_requests
routing:
'App\Message\InventoryCheck': rpc
Where to look first, in order:
TransportFactory — confirms the DSN scheme and which options are parsed.Transport class — check that it implements both TransportInterface (send) and ReceiverInterface (get). RPC over AMQP is fundamentally a two-way transport; if it only sends, it is not RPC.correlation_id and reply_to — this is where the actual RPC semantics live.call() helper vs. manual envelope handling).First use case: a synchronous request/response hop between two services over RabbitMQ where an HTTP call is unacceptable — e.g. an order service asking a pricing service for a quote and needing the answer in the same request lifecycle. That is the shape this bundle targets. If your use case is "dispatch and process later," use the stock amqp:// transport instead; this bundle buys you nothing.
Caller side — treat RPC as a blocking dependency, isolate it behind a client class. Do not let Messenger envelopes leak into controllers.
final class PricingRpcClient
{
public function __construct(private MessageBusInterface $bus) {}
public function quote(QuoteRequest $request, int $timeoutMs = 2000): QuoteResponse
{
// Confirm the exact return contract of the transport in the package source:
// some implementations return Envelope, some require a separate await() call.
$envelope = $this->bus->dispatch($request);
// ...unwrap QuoteResponse from $envelope here
}
}
Wrap this in a decorator that adds a hard deadline, so a missing consumer fails fast instead of pinning a PHP-FPM worker:
public function quote(QuoteRequest $request, int $timeoutMs = 2000): QuoteResponse
{
$start = microtime(true);
try {
return $this->inner->quote($request);
} finally {
if ((microtime(true) - $start) * 1000 > $timeoutMs) {
// log slow RPC, emit a metric
}
}
}
Consumer side — run a dedicated worker for the RPC queue, not the shared one. RPC replies are latency-sensitive, and mixing them with batch jobs means your caller times out behind a long-running email job.
bin/console messenger:consume rpc --time-limit=3600 --memory-limit=128M
Contracts, not shared classes, across services. Define request/response DTOs in a small shared library with explicit serializer groups. Symfony's PhpSerializer is the default and is a trap for cross-language callers (a Node or Python service cannot read it). Force JSON:
framework:
messenger:
serializer:
default_serializer: messenger.transport.symfony_serializer
Integration tips that actually matter day-to-day:
amq.rabbitmq.reply-to) if the bundle supports it — no reply queue to declare, cleanly scoped to the channel. If it declares a named reply queue instead, ensure one queue per caller process or you get cross-talk.messenger.yaml so RPC traffic never accidentally lands on the async transport. One misrouted class = silent hang.RpcQuoteClient) so you can swap in a fake in tests. Never call the real RPC transport from a unit test.TransportFactory, MessageBus, serializer) and expose a single RpcClient binding. Do not try to make Messenger's ConsumeMessagesCommand play nice with queue:work — run the Symfony command as a separate supervisor program.ext-amqp. Not installable on many shared/managed hosts, and the extension and the pure-PHP php-amqplib are not interoperable at the object level. Check php -m | grep amqp before you plan anything.send() contract has no notion of a return value. Any RPC transport has to bolt request/response on top, which means the caller must block. Audit the bundle's Transport for how it reconciles this — that's where the interesting (and most fragile) code is.x-message-ttl on the request queue and a client-side timeout, otherwise a missing pricing service takes down your checkout.PhpSerializer vs. JSON produces an empty or unhandled envelope that Messenger may just nack. Pin the serializer explicitly and write one integration test that round-trips a real DTO.messenger:stop-workers (or a Supervisor restart) is required; long-lived AMQP connections in workers will otherwise keep consuming stale code. Watch AMQP heartbeat settings on idle connections.Transport.php and TransportFactory.php end-to-end before adopting — likely a few hundred lines, entirely auditable in 20 minutes. Pin the version in composer.json, and be prepared to vendor/fork it. It is more valuable as a reference implementation of direct reply-to in Messenger than as a long-term dependency.reply_to/correlation_id inspection, rabbitmqctl list_queues name messages consumers, and a Messenger middleware that logs every dispatched envelope plus timing. If a call hangs, check consumers on the request queue first — it will be 0.MessageBusInterface to add retries, circuit breaking, and metrics rather than patching the transport. If you need connection pooling or multi-broker failover, a custom TransportFactory delegating to the bundle's transport is cleaner than subclassing it.vladimir-yuldashev/laravel-queue-rabbitmq for async, or php-amqplib with an explicit correlation_id/reply_to implementation for RPC — roughly 60 lines, fully under your control, and no Symfony Messenger coupling. This bundle earns its place only when a Symfony service is already on the other end of the wire.How can I help you explore Laravel packages today?