Skip to content

Redis and Python ​

layers/redis is a Redis pub/sub channel layer, wire-compatible with RedisPubSubChannelLayer from Python's channels_redis and fast-channels, and with @chanx-js/layers. Go, Python and Node processes on one Redis share channels and groups: a Go worker can push to sockets a Django server holds, and a FastAPI process can broadcast to a room some of whose members are connected to Go.

bash
go get go.chanx.dev/layers/redis
go
func NewEngine() (*chanx.Engine, error) {
	layer, err := redis.NewPubSub(redis.PubSubOptions{
		Hosts: []string{os.Getenv("REDIS_URL")}, // "redis://localhost:6379"
	})
	if err != nil {
		return nil, err
	}
	return chanx.New(chanx.Config{Layers: layers.Set{"default": layer}}), nil
}

Sharing Redis with Python ​

Use the same settings on both sides:

go
// Settings a Python process using channels_redis or fast-channels shares.
func SharedWithPython() (layers.Layer, error) {
	return redis.NewPubSub(redis.PubSubOptions{
		Hosts:                   []string{"redis://redis-a:6379", "redis://redis-b:6379"}, // same order everywhere
		Prefix:                  "asgi",
		SerializerFormat:        "msgpack",
		SymmetricEncryptionKeys: []string{os.Getenv("CHANNEL_LAYER_KEY")},
	})
}
GoPythonDefault
Hosts, in the same orderhostsredis://localhost:6379
Prefixprefixasgi
SerializerFormatserializer_formatmsgpack
SymmetricEncryptionKeyssymmetric_encryption_keysnone
python
# settings.py, the same layer on the Django side
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.pubsub.RedisPubSubChannelLayer",
        "CONFIG": {
            "hosts": ["redis://redis-a:6379", "redis://redis-b:6379"],
            "prefix": "asgi",
            "symmetric_encryption_keys": [os.environ["CHANNEL_LAYER_KEY"]],
        },
    },
}

What has to match, and is tested to:

  • The shard ring. With several hosts, a channel's shard is a CRC-32 of its name over 4096 slots, as in channels_redis. Every process must list the hosts in the same order.
  • The encoding. msgpack (default) or JSON, with ints and floats kept apart, so 2.0 stays a float in Python.
  • Encryption. Fernet, keyed as channels_redis keys it; tokens are tested byte for byte against Python's.
  • Group names. protocol.SafeGroupName and topic groups compute names as chanx does.
  • The messages chanx puts on the layer: handle_channel_event, handle_group_message, handle_topic_event, with the same fields.

Options ​

OptionDoes
Clientsready go-redis clients instead of Hosts: Sentinel (goredis.NewFailoverClient) or custom settings. Not closed by Close
ShardedRedis Cluster sharded pub/sub, SSUBSCRIBE and SPUBLISH; only processes with this option read these messages
BufferSizeeach local channel's buffer, 1024 by default; an overflow drops the message
OnOverflowcalled with the channel that dropped a message, for a metric
OnReconnectcalled when a shard's connection comes back
SubscribeTimeouthow long a subscribe waits for Redis's acknowledgement, 5s
Serializerreplaces SerializerFormat and the keys

GroupAdd and NewChannel return once Redis has acknowledged the subscription, so "join, then publish" never loses the message.

What pub/sub does not do ​

Pub/sub keeps nothing. A message published while a process is disconnected from Redis is lost to it, and a slow reader past BufferSize loses messages. That suits real-time traffic, where a late update is replaced by the next. For delivery that must survive a restart, keep the state in your database and send what changed when a client reconnects.

The queue-based RedisChannelLayer of channels_redis has no Go version yet.

Next: Clients.

MIT licensed