Skip to content

Commit

Permalink
feat: remove direct redis dependency
Browse files Browse the repository at this point in the history
BREAKING CHANGE: the library will no longer create Redis clients on
behalf of the user.

Before:

```js
io.adapter(redisAdapter({ host: "localhost", port: 6379 }));
```

After:

```js
const pubClient = createClient({ host: "localhost", port: 6379 });
const subClient = pubClient.duplicate();

io.adapter(redisAdapter(pubClient, subClient));
```

Related:

- #314
- #374
  • Loading branch information
darrachequesne committed May 11, 2021
1 parent 3cac178 commit c68a47c
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 120 deletions.
87 changes: 26 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,41 @@

## How to use

Installation:

```
$ npm install @socket.io/redis-adapter redis
```

### CommonJS

```js
const io = require('socket.io')(3000);
const { createClient } = require('redis');
const redisAdapter = require('@socket.io/redis-adapter');
io.adapter(redisAdapter({ host: 'localhost', port: 6379 }));

const pubClient = createClient({ host: 'localhost', port: 6379 });
const subClient = pubClient.duplicate();
io.adapter(redisAdapter(pubClient, subClient));
```

### ES6 modules

```js
import { Server } from 'socket.io';
import { createClient } from 'redis';
import redisAdapter from '@socket.io/redis-adapter';

const io = new Server(3000);
io.adapter(redisAdapter({ host: 'localhost', port: 6379 }));
const pubClient = createClient({ host: 'localhost', port: 6379 });
const subClient = pubClient.duplicate();
io.adapter(redisAdapter(pubClient, subClient));
```

### TypeScript

```ts
// npm i -D @types/redis
// npm i -D redis @types/redis
import { Server } from 'socket.io';
import { createAdapter } from '@socket.io/redis-adapter';
import { RedisClient } from 'redis';
Expand All @@ -60,7 +73,7 @@ const io = new Server(8080);
const pubClient = new RedisClient({ host: 'localhost', port: 6379 });
const subClient = pubClient.duplicate();

io.adapter(createAdapter({ pubClient, subClient }));
io.adapter(createAdapter(pubClient, subClient));
```

By running Socket.IO with the `@socket.io/redis-adapter` adapter you can run
Expand Down Expand Up @@ -150,26 +163,13 @@ The request and response channels are used in the additional methods exposed by

## API

### adapter(uri[, opts])

`uri` is a string like `localhost:6379` where your redis server
is located. For a list of options see below.

### adapter(opts)
### adapter(pubClient, subClient[, opts])

The following options are allowed:

- `key`: the name of the key to pub/sub events on as prefix (`socket.io`)
- `host`: host to connect to redis on (`localhost`)
- `port`: port to connect to redis on (`6379`)
- `pubClient`: optional, the redis client to publish events on
- `subClient`: optional, the redis client to subscribe to events on
- `requestsTimeout`: optional, after this timeout the adapter will stop waiting from responses to request (`5000ms`)

If you decide to supply `pubClient` and `subClient`, make sure you use
[node_redis](https://github.com/mranney/node_redis) as a client or one
with an equivalent API.

### RedisAdapter

The redis adapter instances expose the following properties
Expand Down Expand Up @@ -242,41 +242,6 @@ try {
}
```

## Client error handling

Access the `pubClient` and `subClient` properties of the
Redis Adapter instance to subscribe to its `error` event:

```js
const adapter = require('@socket.io/redis-adapter')('localhost:6379');
adapter.pubClient.on('error', function(){});
adapter.subClient.on('error', function(){});
```

The errors emitted from `pubClient` and `subClient` will
also be forwarded to the adapter instance:

```js
const io = require('socket.io')(3000);
const redisAdapter = require('@socket.io/redis-adapter');
io.adapter(redisAdapter({ host: 'localhost', port: 6379 }));
io.of('/').adapter.on('error', function(){});
```

## Custom client (eg: with authentication)

If you need to create a redisAdapter to a redis instance
that has a password, use pub/sub options instead of passing
a connection string.

```js
const redis = require('redis');
const redisAdapter = require('@socket.io/redis-adapter');
const pubClient = redis.createClient(port, host, { auth_pass: "pwd" });
const subClient = pubClient.duplicate();
io.adapter(redisAdapter({ pubClient, subClient }));
```

## With ioredis client

### Cluster example
Expand All @@ -297,10 +262,10 @@ const startupNodes = [
}
];

io.adapter(redisAdapter({
pubClient: new Redis.Cluster(startupNodes),
subClient: new Redis.Cluster(startupNodes)
}));
const pubClient = new Redis.Cluster(startupNodes);
const subClient = pubClient.duplicate();

io.adapter(redisAdapter(pubClient, subClient));
```

### Sentinel Example
Expand All @@ -318,10 +283,10 @@ const options = {
name: 'master01'
};

io.adapter(redisAdapter({
pubClient: new Redis(options),
subClient: new Redis(options)
}));
const pubClient = new Redis(options);
const subClient = pubClient.duplicate();

io.adapter(redisAdapter(pubClient, subClient));
```

## Protocol
Expand Down
57 changes: 17 additions & 40 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import uid2 = require("uid2");
import { createClient } from "redis";
import msgpack = require("notepack.io");
import { Adapter, BroadcastOptions, Room, SocketId } from "socket.io-adapter";

Expand Down Expand Up @@ -35,60 +34,34 @@ export interface RedisAdapterOptions {
* @default socket.io
*/
key: string;
/**
* the redis client to publish events on
*/
pubClient: any;
/**
* the redis client to subscribe to events on
*/
subClient: any;
/**
* after this timeout the adapter will stop waiting from responses to request
* @default 5000
*/
requestsTimeout: number;
}

function createRedisClient(uri, opts) {
if (uri) {
// handle uri string
return createClient(uri, opts);
} else {
return createClient(opts);
}
}

/**
* Returns a redis Adapter class.
* Returns a function that will create a RedisAdapter instance.
*
* @param {String} uri - optional, redis uri
* @param {String} opts - redis connection options
* @return {RedisAdapter} adapter
* @param pubClient - a Redis client that will be used to publish messages
* @param subClient - a Redis client that will be used to receive messages (put in subscribed state)
* @param opts - additional options
*
* @public
*/
export function createAdapter(uri: string, opts?: Partial<RedisAdapterOptions>);
export function createAdapter(opts: Partial<RedisAdapterOptions>);
export function createAdapter(
uri?: any,
opts: Partial<RedisAdapterOptions> = {}
pubClient: any,
subClient: any,
opts?: Partial<RedisAdapterOptions>
) {
// handle options only
if (typeof uri === "object") {
opts = uri;
uri = null;
}

return function (nsp) {
return new RedisAdapter(nsp, uri, opts);
return new RedisAdapter(nsp, pubClient, subClient, opts);
};
}

export class RedisAdapter extends Adapter {
public readonly uid;
public readonly pubClient: any;
public readonly subClient: any;
public readonly requestsTimeout: number;

private readonly channel: string;
Expand All @@ -100,17 +73,21 @@ export class RedisAdapter extends Adapter {
* Adapter constructor.
*
* @param nsp - the namespace
* @param uri - the url of the Redis server
* @param opts - the options for both the Redis adapter and the Redis client
* @param pubClient - a Redis client that will be used to publish messages
* @param subClient - a Redis client that will be used to receive messages (put in subscribed state)
* @param opts - additional options
*
* @public
*/
constructor(nsp, uri: string, opts: Partial<RedisAdapterOptions> = {}) {
constructor(
nsp: any,
readonly pubClient: any,
readonly subClient: any,
opts: Partial<RedisAdapterOptions> = {}
) {
super(nsp);

this.uid = uid2(6);
this.pubClient = opts.pubClient || createRedisClient(uri, opts);
this.subClient = opts.subClient || createRedisClient(uri, opts);
this.requestsTimeout = opts.requestsTimeout || 5000;

const prefix = opts.key || "socket.io";
Expand Down
44 changes: 36 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,20 @@
"dependencies": {
"debug": "~4.3.1",
"notepack.io": "~2.2.0",
"redis": "^3.0.0",
"socket.io-adapter": "^2.2.0",
"uid2": "0.0.3"
},
"devDependencies": {
"@types/expect.js": "^0.3.29",
"@types/mocha": "^8.2.1",
"@types/node": "^14.14.7",
"@types/redis": "^2.8.28",
"expect.js": "0.3.1",
"ioredis": "^4.0.0",
"mocha": "^3.4.2",
"nyc": "^15.1.0",
"prettier": "^2.1.2",
"redis": "^3.1.2",
"socket.io": "^4.0.0",
"socket.io-client": "^4.0.0",
"ts-node": "^9.1.1",
Expand Down
Loading

0 comments on commit c68a47c

Please sign in to comment.