@socket.io/redis-adapter
Advanced tools
Comparing version 8.1.0 to 8.2.0
@@ -59,2 +59,3 @@ import { Adapter, BroadcastOptions, Room } from "socket.io-adapter"; | ||
private redisListeners; | ||
private readonly friendlyErrorHandler; | ||
/** | ||
@@ -119,11 +120,5 @@ * Adapter constructor. | ||
private serverSideEmitWithAck; | ||
/** | ||
* Get the number of subscribers of the request channel | ||
* | ||
* @private | ||
*/ | ||
private getNumSub; | ||
serverCount(): Promise<number>; | ||
close(): Promise<void> | void; | ||
} | ||
export {}; | ||
export { createShardedAdapter } from "./sharded-adapter"; |
@@ -14,6 +14,7 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.RedisAdapter = exports.createAdapter = void 0; | ||
exports.createShardedAdapter = exports.RedisAdapter = exports.createAdapter = void 0; | ||
const uid2 = require("uid2"); | ||
const msgpack = require("notepack.io"); | ||
const socket_io_adapter_1 = require("socket.io-adapter"); | ||
const util_1 = require("./util"); | ||
const debug = require("debug")("socket.io-redis"); | ||
@@ -73,3 +74,4 @@ module.exports = exports = createAdapter; | ||
this.requestsTimeout = opts.requestsTimeout || 5000; | ||
this.publishOnSpecificResponseChannel = !!opts.publishOnSpecificResponseChannel; | ||
this.publishOnSpecificResponseChannel = | ||
!!opts.publishOnSpecificResponseChannel; | ||
this.parser = opts.parser || msgpack; | ||
@@ -108,11 +110,9 @@ const prefix = opts.key || "socket.io"; | ||
} | ||
const registerFriendlyErrorHandler = (redisClient) => { | ||
redisClient.on("error", () => { | ||
if (redisClient.listenerCount("error") === 1) { | ||
console.warn("missing 'error' handler on this Redis client"); | ||
} | ||
}); | ||
this.friendlyErrorHandler = function () { | ||
if (this.listenerCount("error") === 1) { | ||
console.warn("missing 'error' handler on this Redis client"); | ||
} | ||
}; | ||
registerFriendlyErrorHandler(this.pubClient); | ||
registerFriendlyErrorHandler(this.subClient); | ||
this.pubClient.on("error", this.friendlyErrorHandler); | ||
this.subClient.on("error", this.friendlyErrorHandler); | ||
} | ||
@@ -518,3 +518,3 @@ /** | ||
const localRooms = new Set(this.rooms.keys()); | ||
const numSub = await this.getNumSub(); | ||
const numSub = await this.serverCount(); | ||
debug('waiting for %d responses to "allRooms" request', numSub); | ||
@@ -554,3 +554,3 @@ if (numSub <= 1) { | ||
} | ||
const numSub = await this.getNumSub(); | ||
const numSub = await this.serverCount(); | ||
debug('waiting for %d responses to "fetchSockets" request', numSub); | ||
@@ -653,3 +653,3 @@ if (numSub <= 1) { | ||
const ack = packet.pop(); | ||
const numSub = (await this.getNumSub()) - 1; // ignore self | ||
const numSub = (await this.serverCount()) - 1; // ignore self | ||
debug('waiting for %d responses to "serverSideEmit" request', numSub); | ||
@@ -682,27 +682,30 @@ if (numSub <= 0) { | ||
} | ||
/** | ||
* Get the number of subscribers of the request channel | ||
* | ||
* @private | ||
*/ | ||
getNumSub() { | ||
serverCount() { | ||
if (this.pubClient.constructor.name === "Cluster" || | ||
this.pubClient.isCluster) { | ||
// Cluster | ||
// ioredis cluster | ||
const nodes = this.pubClient.nodes(); | ||
return Promise.all(nodes.map((node) => node.send_command("pubsub", ["numsub", this.requestChannel]))).then((values) => { | ||
let numSub = 0; | ||
values.map((value) => { | ||
numSub += parseInt(value[1], 10); | ||
}); | ||
return numSub; | ||
}); | ||
return Promise.all(nodes.map((node) => node | ||
.send_command("pubsub", ["numsub", this.requestChannel]) | ||
.then(util_1.parseNumSubResponse))).then(util_1.sumValues); | ||
} | ||
else if (typeof this.pubClient.pSubscribe === "function") { | ||
return this.pubClient | ||
.sendCommand(["pubsub", "numsub", this.requestChannel]) | ||
.then((res) => parseInt(res[1], 10)); | ||
// node-redis client | ||
const isCluster = Array.isArray(this.pubClient.masters); | ||
if (isCluster) { | ||
const nodes = this.pubClient.masters; | ||
return Promise.all(nodes.map((node) => { | ||
return node.client | ||
.sendCommand(["pubsub", "numsub", this.requestChannel]) | ||
.then(util_1.parseNumSubResponse); | ||
})).then(util_1.sumValues); | ||
} | ||
else { | ||
return this.pubClient | ||
.sendCommand(["pubsub", "numsub", this.requestChannel]) | ||
.then(util_1.parseNumSubResponse); | ||
} | ||
} | ||
else { | ||
// RedisClient or Redis | ||
// ioredis or node-redis v3 client | ||
return new Promise((resolve, reject) => { | ||
@@ -712,3 +715,3 @@ this.pubClient.send_command("pubsub", ["numsub", this.requestChannel], (err, numSub) => { | ||
return reject(err); | ||
resolve(parseInt(numSub[1], 10)); | ||
resolve((0, util_1.parseNumSubResponse)(numSub)); | ||
}); | ||
@@ -718,5 +721,2 @@ }); | ||
} | ||
serverCount() { | ||
return this.getNumSub(); | ||
} | ||
close() { | ||
@@ -742,4 +742,8 @@ const isRedisV4 = typeof this.pubClient.pSubscribe === "function"; | ||
} | ||
this.pubClient.off("error", this.friendlyErrorHandler); | ||
this.subClient.off("error", this.friendlyErrorHandler); | ||
} | ||
} | ||
exports.RedisAdapter = RedisAdapter; | ||
var sharded_adapter_1 = require("./sharded-adapter"); | ||
Object.defineProperty(exports, "createShardedAdapter", { enumerable: true, get: function () { return sharded_adapter_1.createShardedAdapter; } }); |
{ | ||
"name": "@socket.io/redis-adapter", | ||
"version": "8.1.0", | ||
"version": "8.2.0", | ||
"description": "The Socket.IO Redis adapter, allowing to broadcast events between several Socket.IO servers", | ||
@@ -16,7 +16,8 @@ "license": "MIT", | ||
"scripts": { | ||
"test": "npm run format:check && tsc && npm run test:redis-v4 && npm run test:redis-v4-specific-channel && npm run test:redis-v3 && npm run test:ioredis", | ||
"test:redis-v4": "nyc mocha --bail --require ts-node/register test/index.ts", | ||
"test:redis-v4-specific-channel": "SPECIFIC_CHANNEL=1 nyc mocha --bail --require ts-node/register test/index.ts", | ||
"test:redis-v3": "REDIS_CLIENT=redis-v3 nyc mocha --bail --require ts-node/register test/index.ts", | ||
"test:ioredis": "REDIS_CLIENT=ioredis nyc mocha --bail --require ts-node/register test/index.ts", | ||
"test": "npm run format:check && tsc && npm run test:default && npm run test:redis-v4-specific-channel && npm run test:redis-v3 && npm run test:ioredis && npm run test:sharded", | ||
"test:default": "nyc mocha --bail --require ts-node/register test/*.ts", | ||
"test:redis-v4-specific-channel": "SPECIFIC_CHANNEL=1 npm run test:default", | ||
"test:redis-v3": "REDIS_CLIENT=redis-v3 npm run test:default", | ||
"test:ioredis": "REDIS_CLIENT=ioredis npm run test:default", | ||
"test:sharded": "SHARDED=1 npm run test:default", | ||
"format:check": "prettier --parser typescript --check 'lib/**/*.ts' 'test/**/*.ts'", | ||
@@ -42,9 +43,9 @@ "format:fix": "prettier --parser typescript --write 'lib/**/*.ts' 'test/**/*.ts'", | ||
"nyc": "^15.1.0", | ||
"prettier": "^2.1.2", | ||
"redis": "^4.0.0", | ||
"prettier": "^2.8.7", | ||
"redis": "^4.6.6", | ||
"redis-v3": "npm:redis@^3.1.2", | ||
"socket.io": "^4.5.0", | ||
"socket.io": "^4.6.1", | ||
"socket.io-client": "^4.1.1", | ||
"ts-node": "^9.1.1", | ||
"typescript": "^4.0.5" | ||
"ts-node": "^10.9.1", | ||
"typescript": "^4.9.5" | ||
}, | ||
@@ -51,0 +52,0 @@ "engines": { |
@@ -12,2 +12,3 @@ # socket.io-redis | ||
- [TypeScript](#typescript) | ||
- [Sharded Redis Pub/Sub](#sharded-redis-pubsub) | ||
- [Compatibility table](#compatibility-table) | ||
@@ -18,7 +19,3 @@ - [How does it work under the hood?](#how-does-it-work-under-the-hood) | ||
- [RedisAdapter](#redisadapter) | ||
- [RedisAdapter#sockets(rooms: Set<String>)](#redisadaptersocketsrooms-setstring) | ||
- [RedisAdapter#allRooms()](#redisadapterallrooms) | ||
- [RedisAdapter#remoteJoin(id:String, room:String)](#redisadapterremotejoinidstring-roomstring) | ||
- [RedisAdapter#remoteLeave(id:String, room:String)](#redisadapterremoteleaveidstring-roomstring) | ||
- [RedisAdapter#remoteDisconnect(id:String, close:Boolean)](#redisadapterremotedisconnectidstring-closeboolean) | ||
- [With ioredis client](#with-ioredis-client) | ||
@@ -127,2 +124,36 @@ - [Cluster example](#cluster-example) | ||
### Sharded Redis Pub/Sub | ||
Sharded Pub/Sub was introduced in Redis 7.0 in order to help scaling the usage of Pub/Sub in cluster mode. | ||
Reference: https://redis.io/docs/manual/pubsub/#sharded-pubsub | ||
A dedicated adapter can be created with the `createShardedAdapter()` method: | ||
```js | ||
import { Server } from 'socket.io'; | ||
import { createClient } from 'redis'; | ||
import { createShardedAdapter } from '@socket.io/redis-adapter'; | ||
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); | ||
``` | ||
Minimum requirements: | ||
- Redis 7.0 | ||
- [`redis@4.6.0`](https://github.com/redis/node-redis/commit/3b1bad229674b421b2bc6424155b20d4d3e45bd1) | ||
## Compatibility table | ||
@@ -214,18 +245,2 @@ | ||
### RedisAdapter#sockets(rooms: Set<String>) | ||
Returns the list of socket IDs connected to `rooms` across all nodes. See [Namespace#allSockets()](https://socket.io/docs/v3/server-api/#namespace-allSockets) | ||
```js | ||
const sockets = await io.of('/').adapter.sockets(new Set()); | ||
console.log(sockets); // a Set containing all the connected socket ids | ||
const sockets = await io.of('/').adapter.sockets(new Set(['room1', 'room2'])); | ||
console.log(sockets); // a Set containing the socket ids in 'room1' or in 'room2' | ||
// this method is also exposed by the Server instance | ||
const sockets = await io.in('room3').allSockets(); | ||
console.log(sockets); // a Set containing the socket ids in 'room3' | ||
``` | ||
### RedisAdapter#allRooms() | ||
@@ -240,38 +255,2 @@ | ||
### RedisAdapter#remoteJoin(id:String, room:String) | ||
Makes the socket with the given id join the room. | ||
```js | ||
try { | ||
await io.of('/').adapter.remoteJoin('<my-id>', 'room1'); | ||
} catch (e) { | ||
// the socket was not found | ||
} | ||
``` | ||
### RedisAdapter#remoteLeave(id:String, room:String) | ||
Makes the socket with the given id leave the room. | ||
```js | ||
try { | ||
await io.of('/').adapter.remoteLeave('<my-id>', 'room1'); | ||
} catch (e) { | ||
// the socket was not found | ||
} | ||
``` | ||
### RedisAdapter#remoteDisconnect(id:String, close:Boolean) | ||
Makes the socket with the given id to get disconnected. If `close` is set to true, it also closes the underlying socket. | ||
```js | ||
try { | ||
await io.of('/').adapter.remoteDisconnect('<my-id>', true); | ||
} catch (e) { | ||
// the socket was not found | ||
} | ||
``` | ||
## With ioredis client | ||
@@ -278,0 +257,0 @@ |
78973
13
1713
352