RedisMPX
RedisMPX is a Redis Pub/Sub multiplexer library written in multiple languages and live coded on Twitch.
Abstract
When bridging multiple application instances through Redis Pub/Sub it's easy to end up needing support for multiplexing. RedisMPX streamlines this process in a consistent way across multiple languages by offering a consistent set of features that cover the most common use cases.
The library works under the assumption that you are going to create separate subscriptions for each client connected to your service (e.g. WebSockets clients):
- ChannelSubscription allows you to add and remove individual Redis PubSub channels similarly to how a multi-room chat application would need to.
- PatternSubscription allows you to subscribe to a single Redis Pub/Sub pattern.
- PromiseSubscription allows you to create a networked promise system.
Installation
npm install redis-mpx
Features
- Simple channel subscriptions
- Pattern subscriptions
- Networked promise system
- Automatic reconnection with exponetial backoff + jitter
Networked Promise System
A Promise represents a timed, uninterrupted, single-message subscription to a Redis Pub/Sub channel. If network connectivity gets lost, thus causing an interruption, the Promise will be failed (unless already fullfilled). See Multiplexer.createPromiseSubscription()
for more info.
Documentation
Usage
const Multiplexer = ; // Pass to Multiplexer the same connection options that// node-redis' redis.createClient() would accept.// For more info: https://github.com/NodeRedis/node-redis#rediscreateclientlet mpx = ; // onMessage is a callback (can be async)// that accepts a channel name and a message.// Bott channel name and message will be given to you// as Buffer instances, use `.toString()` to decode their // contents if necessary.async funcion { console;} // onDisconnect is a callback (can be async) // that that accepts no parameters.{ console;} // onActivation is a callback (can be async)// that accepts the name of the channel or pattern// whose subscription just became active (depends// on whether it's attached to a ChannelSubscription// or a PatternSubscription). The function will receive// `name` as a Buffer. Use `.toString()` to decode it if// necessary.{ console;} // Both `onDisconnected` and `onActivation` are optional. // Use `mpx` to create new subscriptions:let channelSub = mpx;let patternSub = mpx;let promiseSub = mpx; // Close the multiplexer once you're done with it.mpx;
ChannelSubscription
// Create the ChannelSubscription.let channelSub = mpx; // Add channelschannelSub;channelSub;channelSub; // Remove a channelchannelSub; // Clear the subscription (remove all channels)channelSubclear; // Close the subscriptionchannelSub;
PatternSubscription
// Create the PatternSubscription.// Note how it also requires the pattern.let patternSub = mpx; // PatternSubscriptions can only be closedpatternSub
PromiseSubscription
// Create the subscription. // Note how it doesn't accept any callback.let promiseSub = mpx; // When first created (and after a network error that causes // a reconnection), a PromiseSubscription is not immediately // able to create new promises as it first needs the underlying// PatternSubscription to become active. This async function// waits for that event.await promiseSub; // Create a new promise. It might fail if the subscription is // not active.var promise = null;try promise = promiseSub; // 10000ms = 10s // The provided suffix will be composed with the subscription's // prefix to create the final Redis Pub/Sub channel from which // the message is expected to come. In this example, to fullfill // the promise you could send, using redis-cli (or any other client): // // > PUBLISH hello-world "your-promise-payload" // catch e if e instanceof redismpxSubscriptionInactiveError // Wait and then Retry? Return an error to the user? Up to you. // A way of creating a promise that ensures no SubscriptionInactiveError// will trigger.promise = promiseSub; // 10000ms = 10s // A Promise represents a timed, uninterrupted, single-message // subscription to a Redis Pub/Sub channel. If network // connectivity gets lost, thus causing an interruption, // the Promise will be failed (unless already fullfilled). // Resolve the promisetry let result = await promise console // prints your-promise-payload catch e if e instanceof redismpxPromiseTimeoutError // The promise timed out. else if e instanceof redismpxSubscriptionInactiveError // The subscription became inactive while the promise was // still pending. else if e instanceof redismpxSubscriptionClosedError // The subscription was closed while the promise was // still pending. // Clear the subscription (will reject all outstanding// promises and unlock all `waitFor*` waiters)promiseSubclear; // Close the subscription (will call clear()).promiseSub;
WebSocket Example
This is a more realistic example of how to use RedisMPX.
Code
This code is also available in examples/channel.js.
# channeljs const Multiplexer = ;const redis = ;const WebSocket = ; let publish = redis;let mpx = ; let server = port: 8080 ;server;
Interacting with the example
The application works like a simple WebSocket chat application that expects commands from the user.
- Sending
+hello
will subscribe you to channelhello
, while-hello
will do the opposite. - Sending
!hello
will broadcast the next message you send tohello
. - You can use whatever channel name you like.
To send those commands you can use a browser:
// To create a websocket connection to localhost// you will need to deal with the browser's security// policies. Opening a file on the local filesystem// and typing these commands in the console should// do the trick.let ws = "ws://localhost:8000/ws"ws consolewswsws
A more handy way of interacting with websockets are command-line clients: