Cross-language: Python → Go over Redis
The whole point of BabelQueue: a message produced in one language is consumed
natively in another, over the broker you already run, using one strict JSON
envelope. This demo proves it end-to-end — a Python or PHP producer and
a Go consumer sharing a Redis queue. The consumer never knows which language
wrote the message; it only sees the canonical envelope and meta.lang.
Every SDK uses the identical reliable-queue pattern (RPUSH to produce, BLMOVE
to a :processing list to reserve, LREM to ack), so they interoperate on the
plain orders Redis list. Full runnable sources:
github.com/BabelQueue/babelqueue-examples.
Producer — Python
# pip install "babelqueue[redis]"
from babelqueue import BabelQueue
app = BabelQueue("redis://localhost:6379/0", queue="orders")
app.publish("urn:babel:orders:created", {"order_id": 1042, "amount": 99.90, "currency": "USD"})
app.publish("urn:babel:catalog:item.indexed", {"sku": "WIDGET-1", "title": "Café Widget ☕"})
Producer — PHP (same queue, no framework)
The framework-less core builds the envelope and the bundled RedisTransport does
a plain RPUSH onto the identical orders list — so the Go consumer below reads
PHP-produced messages without changing a line.
// composer require babelqueue/php-sdk predis/predis
use BabelQueue\Codec\EnvelopeCodec;
use BabelQueue\Transport\RedisTransport;
$transport = new RedisTransport(new Predis\Client("redis://localhost:6379/0"), "orders");
$envelope = EnvelopeCodec::fromJob($job, "orders"); // $job: a PolyglotJob (URN + toPayload())
$transport->publish(EnvelopeCodec::encode($envelope), "orders");
Consumer — Go
// go get github.com/babelqueue/babelqueue-go/redis
import (
babelqueue "github.com/babelqueue/babelqueue-go"
bqredis "github.com/babelqueue/babelqueue-go/redis"
)
tr, _ := bqredis.New("redis://localhost:6379/0")
app := babelqueue.NewApp(tr, babelqueue.WithDefaultQueue("orders"))
app.Handle("urn:babel:orders:created", func(ctx context.Context, env babelqueue.Envelope) error {
fmt.Printf("order %v (produced by %q)\n", env.Data["order_id"], env.Meta.Lang)
return nil
})
app.Consume(context.Background()) // routes by URN, forever
The proof
Run either producer, then the Go consumer. The Go program prints messages it
never produced — produced by "php" (or "python") — with the data,
trace_id and even unicode intact:
[go] order created id=1042 amount=99.9 USD trace=1d3b… (produced by "php")
[go] order created id=1043 amount=12.5 EUR trace=79c3… (produced by "php")
[go] item indexed sku=WIDGET-1 title="Café Widget ☕" (produced by "php")
[go] processed 3 message(s) — same envelope, different language.
The envelope on the wire is the canonical shape every SDK emits:
{
"job": "urn:babel:orders:created",
"trace_id": "…",
"data": { "order_id": 1042, "amount": 99.9, "currency": "USD" },
"meta": { "id": "…", "queue": "orders", "lang": "php", "schema_version": 1, "created_at": 1749132727000 },
"attempts": 0
}
Swap the ends
The queue carries the canonical envelope, so any SDK can be on either side — a Go
producer (app.Publish(...)) and a Python consumer (@app.handler(...) + app.run())
work the same way. PHP (framework-less RedisTransport, or Laravel/Symfony on a
babelqueue-* connection), Node, Java and .NET read and write the identical
envelope on their own transports.