# socket.io-redis [![Build Status](https://travis-ci.org/socketio/socket.io-redis.svg?branch=master)](https://travis-ci.org/socketio/socket.io-redis) [![NPM version](https://badge.fury.io/js/socket.io-redis.svg)](http://badge.fury.io/js/socket.io-redis) ## How to use ```js const io = require('socket.io')(3000); const redisAdapter = require('socket.io-redis'); io.adapter(redisAdapter({ host: 'localhost', port: 6379 })); ``` By running socket.io with the `socket.io-redis` adapter you can run multiple socket.io instances in different processes or servers that can all broadcast and emit events to and from each other. So any of the following commands: ```js io.emit('hello', 'to all clients'); io.to('room42').emit('hello', "to all clients in 'room42' room"); io.on('connection', (socket) => { socket.broadcast.emit('hello', 'to all clients except sender'); socket.to('room42').emit('hello', "to all clients in 'room42' room except sender"); }); ``` will properly be broadcast to the clients through the Redis Pub/Sub mechanism. If you need to emit events to socket.io instances from a non-socket.io process, you should use [socket.io-emitter](https://github.com/socketio/socket.io-emitter). ## 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) 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 that a regular `Adapter` does not - `uid` - `prefix` - `pubClient` - `subClient` - `requestsTimeout` ### RedisAdapter#clients(rooms:Array, fn:Function) Returns the list of client IDs connected to `rooms` across all nodes. See [Namespace#clients(fn:Function)](https://github.com/socketio/socket.io#namespaceclientsfnfunction) ```js io.of('/').adapter.clients((err, clients) => { console.log(clients); // an array containing all connected socket ids }); io.of('/').adapter.clients(['room1', 'room2'], (err, clients) => { console.log(clients); // an array containing socket ids in 'room1' and/or 'room2' }); // you can also use io.in('room3').clients((err, clients) => { console.log(clients); // an array containing socket ids in 'room3' }); ``` ### RedisAdapter#clientRooms(id:String, fn:Function) Returns the list of rooms the client with the given ID has joined (even on another node). ```js io.of('/').adapter.clientRooms('', (err, rooms) => { if (err) { /* unknown id */ } console.log(rooms); // an array containing every room a given id has joined. }); ``` ### RedisAdapter#allRooms(fn:Function) Returns the list of all rooms. ```js io.of('/').adapter.allRooms((err, rooms) => { console.log(rooms); // an array containing all rooms (accross every node) }); ``` ### RedisAdapter#remoteJoin(id:String, room:String, fn:Function) Makes the socket with the given id join the room. The callback will be called once the socket has joined the room, or with an `err` argument if the socket was not found. ```js io.of('/').adapter.remoteJoin('', 'room1', (err) => { if (err) { /* unknown id */ } // success }); ``` ### RedisAdapter#remoteLeave(id:String, room:String, fn:Function) Makes the socket with the given id leave the room. The callback will be called once the socket has left the room, or with an `err` argument if the socket was not found. ```js io.of('/').adapter.remoteLeave('', 'room1', (err) => { if (err) { /* unknown id */ } // success }); ``` ### RedisAdapter#remoteDisconnect(id:String, close:Boolean, fn:Function) Makes the socket with the given id to get disconnected. If `close` is set to true, it also closes the underlying socket. The callback will be called once the socket was disconnected, or with an `err` argument if the socket was not found. ```js io.of('/').adapter.remoteDisconnect('', true, (err) => { if (err) { /* unknown id */ } // success }); ``` ### RedisAdapter#customRequest(data:Object, fn:Function) Sends a request to every nodes, that will respond through the `customHook` method. ```js // on every node io.of('/').adapter.customHook = (data, cb) => { cb('hello ' + data); } // then io.of('/').adapter.customRequest('john', function(err, replies){ console.log(replies); // an array ['hello john', ...] with one element per node }); ``` ## 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')('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'); 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'); const pub = redis.createClient(port, host, { auth_pass: "pwd" }); const sub = redis.createClient(port, host, { auth_pass: "pwd" }); io.adapter(redisAdapter({ pubClient: pub, subClient: sub })); ``` ## With [ioredis](https://github.com/luin/ioredis) client ### Cluster example ```js const io = require('socket.io')(3000); const redisAdapter = require('socket.io-redis'); const Redis = require('ioredis'); const startupNodes = [ { port: 6380, host: '127.0.0.1' }, { port: 6381, host: '127.0.0.1' } ]; io.adapter(redisAdapter({ pubClient: new Redis.Cluster(startupNodes), subClient: new Redis.Cluster(startupNodes) })); ``` ### Sentinel Example ```js const io = require('socket.io')(3000); const redisAdapter = require('socket.io-redis'); const Redis = require('ioredis'); const options = { sentinels: [ { host: 'somehost1', port: 26379 }, { host: 'somehost2', port: 26379 } ], name: 'master01' }; io.adapter(redisAdapter({ pubClient: new Redis(options), subClient: new Redis(options) })); ``` ## Protocol The `socket.io-redis` adapter broadcasts and receives messages on particularly named Redis channels. For global broadcasts the channel name is: ``` prefix + '#' + namespace + '#' ``` In broadcasting to a single room the channel name is: ``` prefix + '#' + namespace + '#' + room + '#' ``` - `prefix`: The base channel name. Default value is `socket.io`. Changed by setting `opts.key` in `adapter(opts)` constructor - `namespace`: See https://github.com/socketio/socket.io#namespace. - `room` : Used if targeting a specific room. A number of other libraries adopt this protocol including: - [socket.io-emitter](https://github.com/socketio/socket.io-emitter) - [socket.io-python-emitter](https://github.com/GameXG/socket.io-python-emitter) - [socket.io-emitter-go](https://github.com/stackcats/socket.io-emitter-go) ## License MIT