Redis adapter for Socket.IO that enables broadcasting packets between multiple Socket.IO servers through Redis pub/sub functionality
—
Advanced Redis adapter implementation utilizing Redis 7.0+ sharded pub/sub features for improved scalability and performance. Sharded pub/sub helps scale Socket.IO applications by distributing channel load across Redis cluster nodes.
Creates a sharded Redis adapter factory function optimized for Redis 7.0+ sharded pub/sub.
/**
* Creates a sharded Redis adapter factory using Redis 7.0+ sharded pub/sub
* @param pubClient - Redis client used to publish (from the 'redis' package)
* @param subClient - Redis client used to subscribe (from the 'redis' package)
* @param opts - Optional configuration options
* @returns Function that creates ShardedRedisAdapter instances for namespaces
*/
function createShardedAdapter(
pubClient: any,
subClient: any,
opts?: ShardedRedisAdapterOptions
): (nsp: any) => ShardedRedisAdapter;Usage Examples:
import { Server } from "socket.io";
import { createClient } from "redis";
import { createShardedAdapter } from "@socket.io/redis-adapter";
// Basic sharded adapter setup
const pubClient = createClient({ host: "localhost", port: 6379 });
const subClient = pubClient.duplicate();
await Promise.all([
pubClient.connect(),
subClient.connect()
]);
const io = new Server({
adapter: createShardedAdapter(pubClient, subClient)
});
io.listen(3000);
// With custom options
const io2 = new Server({
adapter: createShardedAdapter(pubClient, subClient, {
channelPrefix: "my-app",
subscriptionMode: "dynamic-private"
})
});Main sharded adapter implementation that extends ClusterAdapter with Redis sharded pub/sub functionality.
/**
* Sharded Redis adapter class using Redis 7.0+ sharded pub/sub features
* Extends ClusterAdapter from socket.io-adapter
*/
class ShardedRedisAdapter extends ClusterAdapter {
/**
* Create a new ShardedRedisAdapter instance
* @param nsp - Socket.IO namespace
* @param pubClient - Redis client for publishing
* @param subClient - Redis client for subscribing
* @param opts - Configuration options
*/
constructor(
nsp: any,
pubClient: any,
subClient: any,
opts?: ShardedRedisAdapterOptions
);
}Methods for publishing messages through sharded channels.
/**
* Publishes a cluster message to appropriate sharded channel
* @param message - Message to publish
* @returns Promise resolving to offset (empty string for Redis)
*/
doPublish(message: ClusterMessage): Promise<Offset>;
/**
* Publishes a response to the requesting server's specific channel
* @param requesterUid - Unique ID of the requesting server
* @param response - Response message to send
* @returns Promise that resolves when message is published
*/
doPublishResponse(
requesterUid: string,
response: ClusterResponse
): Promise<void>;Methods for managing server connections and counts.
/**
* Gets the count of servers using sharded pub/sub
* Uses SHARDNUMSUB command for accurate server counting
* @returns Promise resolving to number of connected servers
*/
serverCount(): Promise<number>;Methods for managing the sharded adapter lifecycle.
/**
* Closes the sharded adapter and cleans up all subscriptions
* Unsubscribes from base channels and dynamic room channels
* @returns Promise that resolves when cleanup is complete
*/
close(): Promise<void>;interface ShardedRedisAdapterOptions {
/**
* The prefix for Redis Pub/Sub channels
* @default "socket.io"
*/
channelPrefix?: string;
/**
* Subscription mode that impacts the number of channels used:
*
* - "static": 2 channels per namespace
* Useful with dynamic namespaces
*
* - "dynamic": (2 + 1 per public room) channels per namespace
* Default value, optimal when some rooms have few clients
* Only creates channels for public rooms (not Socket IDs)
*
* - "dynamic-private": Like "dynamic" but includes private rooms
* Creates separate channels for Socket ID rooms
* Useful for lots of 1:1 communication via socket.emit()
*
* @default "dynamic"
*/
subscriptionMode?: "static" | "dynamic" | "dynamic-private";
}import { createClient } from "redis";
import { Server } from "socket.io";
import { createShardedAdapter } from "@socket.io/redis-adapter";
const pubClient = createClient({ url: "redis://localhost:6379" });
const subClient = pubClient.duplicate();
await Promise.all([
pubClient.connect(),
subClient.connect()
]);
const io = new Server({
adapter: createShardedAdapter(pubClient, subClient)
});
// All Socket.IO operations work the same
io.emit("broadcast", "Hello everyone!");
io.to("room1").emit("room-message", "Hello room1!");const io = new Server({
adapter: createShardedAdapter(pubClient, subClient, {
channelPrefix: "my-game",
subscriptionMode: "dynamic-private" // Optimize for 1:1 messaging
})
});
// Efficient for direct socket communication
io.to(socketId).emit("private-message", "Hello specific user!");// With dynamic mode, each room gets its own optimized channel
io.to("lobby").emit("player-joined", { playerId: 123 });
io.to("game-room-1").emit("game-update", gameState);
// Servers not subscribed to these specific rooms won't receive these messagesredis package is supported, not ioredisWhen migrating from the regular Redis adapter to the sharded adapter:
redis package v4.6.0+createAdapter to createShardedAdapter// Before (regular adapter)
import { createAdapter } from "@socket.io/redis-adapter";
const adapter = createAdapter(pubClient, subClient, {
key: "my-app"
});
// After (sharded adapter)
import { createShardedAdapter } from "@socket.io/redis-adapter";
const adapter = createShardedAdapter(pubClient, subClient, {
channelPrefix: "my-app",
subscriptionMode: "dynamic"
});Install with Tessl CLI
npx tessl i tessl/npm-socket-io--redis-adapter