Skip to content

Commit 00a4713

Browse files
feat: 添加 ioredis Cluster Sharded Pub/Sub 支持
- 在 util.ts 中添加 isIoRedisCluster 和 hasShardedSubscribers 輔助函數 - 在 sharded-adapter.ts 中添加 ioredis Cluster 配置驗證 - 啟用 [sharded] ioredis cluster 測試(需要 shardedSubscribers: true) - 更新 package.json 中 ioredis 版本要求到 ^5.9.0 - 更新 README.md 添加 ioredis Cluster 與 sharded pub/sub 的使用說明 - 添加 ioredis Cluster 的 natMap 配置以解決 Docker 環境問題 參考: redis/ioredis#1956
1 parent cdb5535 commit 00a4713

6 files changed

Lines changed: 129 additions & 26 deletions

File tree

README.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ The `@socket.io/redis-adapter` package allows broadcasting packets between multi
1818
- [With the `ioredis` package](#with-the-ioredis-package)
1919
- [With the `ioredis` package and a Redis cluster](#with-the-ioredis-package-and-a-redis-cluster)
2020
- [With Redis sharded Pub/Sub](#with-redis-sharded-pubsub)
21+
- [With the `ioredis` package and a Redis cluster](#with-the-ioredis-package-and-a-redis-cluster-1)
2122
- [Options](#options)
2223
- [Default adapter](#default-adapter)
2324
- [Sharded adapter](#sharded-adapter)
@@ -186,7 +187,44 @@ Minimum requirements:
186187
- Redis 7.0
187188
- [`redis@4.6.0`](https://github.com/redis/node-redis/commit/3b1bad229674b421b2bc6424155b20d4d3e45bd1)
188189

189-
Note: it is not currently possible to use the sharded adapter with the `ioredis` package and a Redis cluster ([reference](https://github.com/luin/ioredis/issues/1759)).
190+
#### With the `ioredis` package and a Redis cluster
191+
192+
Starting with `ioredis@5.9.0`, you can use the sharded adapter with an ioredis Cluster by enabling the `shardedSubscribers` option:
193+
194+
```js
195+
import { Cluster } from "ioredis";
196+
import { Server } from "socket.io";
197+
import { createShardedAdapter } from "@socket.io/redis-adapter";
198+
199+
const pubClient = new Cluster(
200+
[
201+
{
202+
host: "localhost",
203+
port: 7000,
204+
},
205+
{
206+
host: "localhost",
207+
port: 7001,
208+
},
209+
{
210+
host: "localhost",
211+
port: 7002,
212+
},
213+
],
214+
{
215+
shardedSubscribers: true,
216+
}
217+
);
218+
const subClient = pubClient.duplicate();
219+
220+
const io = new Server({
221+
adapter: createShardedAdapter(pubClient, subClient)
222+
});
223+
224+
io.listen(3000);
225+
```
226+
227+
Reference: https://github.com/redis/ioredis/pull/1956
190228

191229
## Options
192230

lib/sharded-adapter.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,15 @@ import {
66
Offset,
77
} from "socket.io-adapter";
88
import { decode, encode } from "notepack.io";
9-
import { hasBinary, PUBSUB, SPUBLISH, SSUBSCRIBE, SUNSUBSCRIBE } from "./util";
9+
import {
10+
hasBinary,
11+
hasShardedSubscribers,
12+
isIoRedisCluster,
13+
PUBSUB,
14+
SPUBLISH,
15+
SSUBSCRIBE,
16+
SUNSUBSCRIBE,
17+
} from "./util";
1018
import debugModule from "debug";
1119

1220
const debug = debugModule("socket.io-redis");
@@ -85,6 +93,15 @@ class ShardedRedisAdapter extends ClusterAdapter {
8593
opts
8694
);
8795

96+
// Validate ioredis Cluster configuration
97+
if (isIoRedisCluster(subClient) && !hasShardedSubscribers(subClient)) {
98+
throw new Error(
99+
"When using the sharded adapter with an ioredis Cluster, " +
100+
"you must enable the 'shardedSubscribers' option. " +
101+
"See https://github.com/redis/ioredis/pull/1956"
102+
);
103+
}
104+
88105
this.channel = `${this.opts.channelPrefix}#${nsp.name}#`;
89106
this.responseChannel = `${this.opts.channelPrefix}#${nsp.name}#${this.uid}#`;
90107

lib/util.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,29 @@ function isRedisV4Client(redisClient: any) {
5252
return typeof redisClient.sSubscribe === "function";
5353
}
5454

55+
/**
56+
* Whether the client is an ioredis Cluster instance
57+
*
58+
* @param redisClient
59+
*/
60+
export function isIoRedisCluster(redisClient: any) {
61+
return redisClient.constructor.name === "Cluster" || redisClient.isCluster;
62+
}
63+
64+
/**
65+
* Whether the ioredis Cluster has shardedSubscribers enabled
66+
*
67+
* @param redisClient
68+
*
69+
* @see https://github.com/redis/ioredis/pull/1956
70+
*/
71+
export function hasShardedSubscribers(redisClient: any) {
72+
return (
73+
isIoRedisCluster(redisClient) &&
74+
redisClient.options?.shardedSubscribers === true
75+
);
76+
}
77+
5578
const kHandlers = Symbol("handlers");
5679

5780
export function SSUBSCRIBE(

package-lock.json

Lines changed: 29 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"@types/mocha": "^8.2.1",
3333
"@types/node": "^14.14.7",
3434
"expect.js": "0.3.1",
35-
"ioredis": "^5.3.2",
35+
"ioredis": "^5.9.1",
3636
"mocha": "^10.1.0",
3737
"nyc": "^15.1.0",
3838
"prettier": "^2.8.7",

test/test-runner.ts

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ const clusterNodes = [
3838
},
3939
];
4040

41+
// NAT mapping for ioredis Cluster (Docker container returns internal IP)
42+
const ioredisNatMap = {
43+
"172.20.0.3:7000": { host: "localhost", port: 7000 },
44+
"172.20.0.3:7001": { host: "localhost", port: 7001 },
45+
"172.20.0.3:7002": { host: "localhost", port: 7002 },
46+
"172.20.0.3:7003": { host: "localhost", port: 7003 },
47+
"172.20.0.3:7004": { host: "localhost", port: 7004 },
48+
"172.20.0.3:7005": { host: "localhost", port: 7005 },
49+
};
50+
4151
function testSuite(
4252
createAdapter: any,
4353
redisPackage: string = "redis@4",
@@ -139,7 +149,9 @@ describe("@socket.io/redis-adapter", () => {
139149

140150
describe("ioredis cluster", () =>
141151
testSuite(async () => {
142-
const pubClient = new Cluster(clusterNodes);
152+
const pubClient = new Cluster(clusterNodes, {
153+
natMap: ioredisNatMap,
154+
});
143155
const subClient = pubClient.duplicate();
144156

145157
return [
@@ -259,11 +271,14 @@ describe("@socket.io/redis-adapter", () => {
259271
true
260272
));
261273

262-
// FIXME see https://github.com/luin/ioredis/issues/1759
263-
describe.skip("[sharded] ioredis cluster", () =>
274+
// Fixed in ioredis 5.9.0, see https://github.com/redis/ioredis/pull/1956
275+
describe("[sharded] ioredis cluster", () =>
264276
testSuite(
265277
async () => {
266-
const pubClient = new Cluster(clusterNodes);
278+
const pubClient = new Cluster(clusterNodes, {
279+
shardedSubscribers: true,
280+
natMap: ioredisNatMap,
281+
});
267282
const subClient = pubClient.duplicate();
268283

269284
return [

0 commit comments

Comments
 (0)