Giter VIP home page Giter VIP logo

socket.io-redis-adapter's Introduction

Socket.IO Redis adapter

The @socket.io/redis-adapter package allows broadcasting packets between multiple Socket.IO servers.

Diagram of Socket.IO packets forwarded through Redis

Table of contents

Supported features

Feature socket.io version Support
Socket management 4.0.0 ✅ YES (since version 6.1.0)
Inter-server communication 4.1.0 ✅ YES (since version 7.0.0)
Broadcast with acknowledgements 4.5.0 ✅ YES (since version 7.2.0)
Connection state recovery 4.6.0 ❌ NO

Installation

npm install @socket.io/redis-adapter

Compatibility table

Redis Adapter version Socket.IO server version
4.x 1.x
5.x 2.x
6.0.x 3.x
6.1.x 4.x
7.x and above 4.3.1 and above

Usage

With the redis package

import { createClient } from "redis";
import { Server } from "socket.io";
import { createAdapter } 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: createAdapter(pubClient, subClient)
});

io.listen(3000);

With the redis package and a Redis cluster

import { createCluster } from "redis";
import { Server } from "socket.io";
import { createAdapter } from "@socket.io/redis-adapter";

const pubClient = createCluster({
  rootNodes: [
    {
      url: "redis://localhost:7000",
    },
    {
      url: "redis://localhost:7001",
    },
    {
      url: "redis://localhost:7002",
    },
  ],
});
const subClient = pubClient.duplicate();

await Promise.all([
  pubClient.connect(),
  subClient.connect()
]);

const io = new Server({
  adapter: createAdapter(pubClient, subClient)
});

io.listen(3000);

With the ioredis package

import { Redis } from "ioredis";
import { Server } from "socket.io";
import { createAdapter } from "@socket.io/redis-adapter";

const pubClient = new Redis();
const subClient = pubClient.duplicate();

const io = new Server({
  adapter: createAdapter(pubClient, subClient)
});

io.listen(3000);

With the ioredis package and a Redis cluster

import { Cluster } from "ioredis";
import { Server } from "socket.io";
import { createAdapter } from "@socket.io/redis-adapter";

const pubClient = new Cluster([
  {
    host: "localhost",
    port: 7000,
  },
  {
    host: "localhost",
    port: 7001,
  },
  {
    host: "localhost",
    port: 7002,
  },
]);
const subClient = pubClient.duplicate();

const io = new Server({
  adapter: createAdapter(pubClient, subClient)
});

io.listen(3000);

With Redis sharded 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/interact/pubsub/#sharded-pubsub

A dedicated adapter can be created with the createShardedAdapter() method:

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:

Note: it is not currently possible to use the sharded adapter with the ioredis package and a Redis cluster (reference).

Options

Default adapter

Name Description Default value
key The prefix for the Redis Pub/Sub channels. socket.io
requestsTimeout After this timeout the adapter will stop waiting from responses to request. 5_000
publishOnSpecificResponseChannel Whether to publish a response to the channel specific to the requesting node. false
parser The parser to use for encoding and decoding messages sent to Redis. -

Sharded adapter

Name Description Default value
channelPrefix The prefix for the Redis Pub/Sub channels. socket.io
subscriptionMode The subscription mode impacts the number of Redis Pub/Sub channels used by the adapter. dynamic

License

MIT

socket.io-redis-adapter's People

Contributors

barisusakli avatar beaucollins avatar darrachequesne avatar dazorni avatar dependabot[bot] avatar heiye9 avatar hgcummings avatar kevin-roark avatar kosta-github avatar krinkle avatar laurentp22 avatar martinkolarik avatar masterodin avatar mymomsaysiamspecial avatar naderio avatar nkzawa avatar objectivesee avatar omrilitov avatar overflowz avatar papandreou avatar rase- avatar rauchg avatar reflexfox avatar roccoc avatar ryoqun avatar siedrix avatar stevebaum23 avatar subjunk avatar tomasvidhall avatar williamdasilva avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

socket.io-redis-adapter's Issues

Cannot read property 'bind' of undefined

I have crash error when refresh client: node v4.0, [email protected], [email protected].

/Volumes/working/ibooknail/api/node_modules/socket.io-redis/index.js:213
if (!rooms) return process.nextTick(fn.bind(null, null));
^

TypeError: Cannot read property 'bind' of undefined
at Redis.delAll (/Volumes/working/ibooknail/api/node_modules/socket.io-redis/index.js:213:43)
at Socket.leaveAll (/Volumes/working/ibooknail/api/node_modules/socket.io/lib/socket.js:261:16)
at Socket.onclose (/Volumes/working/ibooknail/api/node_modules/socket.io/lib/socket.js:412:8)
at Client.onclose (/Volumes/working/ibooknail/api/node_modules/socket.io/lib/client.js:230:12)
at emitTwo (events.js:92:20)
at Socket.emit (events.js:172:7)
at Socket.onClose (/Volumes/working/ibooknail/api/node_modules/engine.io/lib/socket.js:248:10)
at WebSocket.g (events.js:260:16)
at emitNone (events.js:67:13)
at WebSocket.emit (events.js:166:7)
at WebSocket.Transport.onClose (/Volumes/working/ibooknail/api/node_modules/engine.io/lib/transport.js:113:8)
at WebSocket.g (events.js:260:16)
at emitTwo (events.js:87:13)
at WebSocket.emit (events.js:172:7)
at WebSocket.cleanupWebsocketResources (/Volumes/working/ibooknail/api/node_modules/ws/lib/WebSocket.js:926:10)
at emitNone (events.js:72:20)
Program node app.js exited with code 1

Next Release?

I see that this repository contains bunch of unreleased changes.
When do you plan to release it?

CPU consumption

I'm developing a project where we have multiple Node.js instances and we use socket.io-redis to broadcast to all clients of all instances. This works but it seems to consume lot's of CPU.
I've done a couple of tests:

  • When I start 4 instances and I broadcast messages in all instances the cpu of all go to 100%.
  • When I start 4 instances and I broadcast messages in 1 instance the cpu of that instance is 1 to 3% and all the other instances use 100%.
  • When I don't use socket.io-redis, start 4 instances and broadcast messages in 1 instance the cpu of that instance is 1 to 3% and all the others are 0%.

I'm using a "stresstest" where I create 100 namespaces with each 2 clients and where one client broadcasts something to the other client, every second (with {state: 'mystate'} as data).
(I'm using PM2 to start multiple instances and it's monitor functionality to monitor the cpu)
I've used the debug module to see what's going on on the other instances when I use socket.io-redis and I see it's basicly lot's of socket.io-redis ignore different namespace.

I think socket.io-redis isn't optimized for "lot's" of namespaces?

Getting a list of everyone in a room?

I'm wondering if there is a common solution to list people in a room when using multi-instance setup?

I have the impression redis only knows how many servers are subscribed to a channel but not how many users?

thank you

Expose redis quit function

I use socket.io-redis with my node js cluster app. Instead of just exiting on SIGTERM and SIGINT, I gracefully exit and close all connections. However, using the socket.io-redis my child processes never exit, problaby redis connection is persisted, leaving a dead process that has to be forced killed using kill -9. Here is a quick reproduction script:

cluster = require('cluster');

if (cluster.isMaster) {
  cluster.fork();
  process.on('SIGTERM', gracefulMasterExit).on('SIGINT', gracefulMasterExit);

  process.on('exit', function () {
    console.log("Successfully shut down");
  });

  function gracefulMasterExit() {
      console.log("Got SIGINT or SIGTERM, gracefully exiting");
  }

} else {
  var server = require('http').Server();
  var io = require('socket.io')(server);
  io.adapter(require('socket.io-redis')('localhost:6379'));
  server.listen(3000);

  process.on('SIGTERM', gracefulExit).on('SIGINT', gracefulExit);

  function gracefulExit() {
      console.log("Worker " + cluster.worker.process.pid + " exiting gracefully");
      cluster.worker.disconnect();
  }
}

After running this you should see Worker <PID> exiting gracefully, but if you look at your process manager you will still see that worker running with the master process dead. In this particular case sending SIGTERM or SIGINT will trigger a channel closed error but in my actual code the program will just return leaving the worker running in the background. Commenting out the redis adapter line will resolve the problem.

Thanks for taking a look at this!

Cannot read split of undefined, and trailing bytes

I have the following package versions:

{
    "redis": "2.0.1",
    "socket.io": "1.3.7",
    "socket.io-emitter": "0.2.0",
    "socket.io-redis": "0.1.4",
}

My redis version is 2.8.21.

When I instantiate socket.io-redis with return_buffers set to true for the clients, I get the following error:

channel.split("#") <!-- cannot read split of undefined

If I monkey patch the code in index.js for socket.io-redis so that the channel is a string, I get a trailing bytes error. Additionally, when I downgrade redis to 1.0.0, I get a trailing bytes error.

I can deal with downgrading redis to 1.0.0. However, no permutation of socket.io-redis, socket.io-emitter, and redis seems to ever solve the trailing bytes error. This is a very critical issue for me. Is there an initiative to fix it in the very near future?

Rooms with multiple node processes

How do I get the list of connected clients in a particular room across nodes.

io.sockets.adapter.rooms seems to have the list of clients only in a specific room.

Tests hang

The test suite just hangs when called, it doesn't even time out. This is very strange. I narrowed it down to the 'join' event ack not getting emitted, but I can't understand why it doesn't timeout and instead hangs indefinitely. I'm trying to figure it out.

Feeling confused about the api

Following he docs

var redis = require('socket.io-redis');
var adapter = redis('localhost:6379');
adapter.pubClient.on('error', function(){});
adapter.subClient.on('error', function(){});

Error

adapter.pubClient.on('error', function () {});
                 ^

TypeError: Cannot read property 'on' of undefined

Has the api changed ?

Handling client messages in other nodes

Hey everyone,

I am sorry but I cannot get my head wrapped around it.

What I want to do is to have one node (or one class of nodes) A that is handling client to server connections. All messages that clients send to node A should be kinda "dumped" in to the redis database where node (class) B is listening on message of type "X" and if it sees any, does stuff.

Does anyone have a basic example on how to achieve this? Especially a way to create a Socket.IO service that is not listening to the outside world but only to redis and gets messages from other nodes is what I cannot achieve to implement.

Thanks in advance

Levin

some bugs

I use socket.io-redis for a game chat.

and daily active users are more than millions.

but we have recently found some bugs.

firstly we changed join function like the following code..

Socket.prototype.join = function(room, fn){
debug('joining room %s', room);
var self = this;
if (~this.rooms.indexOf(room)){
fn && fn();
return this;
}
this.adapter.add(this.id, room, function(err){
if (err) return fn && fn(err);
debug('joined room %s', room);
if (self.rooms.indexOf(room) == -1) {
self.rooms.push(room);
}
fn && fn(null);
});
return this;
};

the problem of the original join function is that if the user has already joined the same room as the one the user trying to join, callback function (fn) does not trigger.

secondly, if the user join a room several times (In my unittest, a user trying to join the same room thousands times),

the code..

this.adapter.add(this.id, room, function(err) <<--- will be triggered later ...

and the result is that the user has joined the same room multiple times

something like this..

socket.rooms => ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']

if you like this code, please change the source code...

Question: doesn't seem to work, though all set up according to the docs

Hi!

I have the following setup:

#=========================================================================================
# Application setup
#=========================================================================================
config = require('config')
cluster = require('cluster')
_ = require('underscore')
app = require('express')()
http = require('http').Server(app)
socket = require('socket.io')(http, serveClient: false)

env = require('env')
helpers = require('app/javascripts/shared/helpers')
log = helpers.log

passport = require('passport')
session = require('express-session')
mongoose = require('mongoose')
redis = require('socket.io-redis')
MongoStore = require('connect-mongostore')(session)
socket.adapter(redis(host: 'localhost', port: 6379))


#=========================================================================================
# Forking
#=========================================================================================
if cluster.isMaster
  for i in [1..config.workers]
    log("Starting worker #{i}")
    cluster.fork()

  cluster.on 'exit', (worker, code, signal)->
    log("Worker #{worker.process.pid} died")

    if config.debug
      process.exit()
    else
      cluster.fork()

else



  #=======================================================================================
  # Instantiate server
  #=======================================================================================
  domain = require('domain').create()
  domain.on 'error', (err)->
    log(err.stack || err, 'red')

    killtimer = setTimeout ->
      process.exit(1)
    , config.death_timeout
    killtimer.unref()

  domain.run ->
    server = require('./app/javascripts/server')
    unless config.debug
      assetsHashMap = {}
      for key, value of require('./public/assets/hashmap.json')
        assetsHashMap[key.replace('.min', '')] = value

    mongoose.connect("mongodb://#{config.mongodb.host}/#{config.mongodb.database}", server: socketOptions: keepAlive: 1)
    mongoose.connection.on('error', (e)-> log("MongoDB operation failed: #{e}"))


    #=====================================================================================
    # Template globals
    #=====================================================================================
    generateTemplateGlobals = ->
      app.locals.pretty = config.debug
      app.locals.config = _.omit(_.clone(config), 'server_only_keys', config.server_only_keys...)
      app.locals._ = _
      app.locals.helpers = helpers


    #=====================================================================================
    # Global middleware
    #=====================================================================================
    normalizeUrl = (req, res, next)->
      try
        decodeURIComponent(req.originalUrl)
      catch
        url = '/'
        log("malformed URL, redirecting to #{url}")
        return res.redirect(301, url)

      [href, qs...] = req.originalUrl.split('?')

      if qs.length > 1 # should be 1?2, [2].length = 1
        url = href + '?' + qs.join('&')
        log("malformed URL, redirecting to #{url}")
        return res.redirect(301, url)

      next()

    getAsset = (name)->
      name = assetsHashMap[name] unless config.debug
      "/assets/#{name}"

    injectGetAsset = (req, res, next)->
      req.app.locals.getAsset = getAsset
      next()

    generateEnv = (req, res, next)->
      res.locals.env.rendered = (new Date).toUTCString()
      res.locals.env.lang = require('./config/lang_en_us')

      next()

    updateUserSession = (req, res, next)->
      # env.csrf = req.csrfToken()
      req.session._updated = (new Date).toUTCString() # forcing cookie to refresh itself
      req.session.touch()
      next()

    preRouteMiddleware = ->
      morgan = require('morgan')

      if config.debug
        app.use(morgan('dev'))
      else
        app.use(morgan('default'))

      app.use(normalizeUrl)

      app.use(require('serve-favicon')(__dirname + '/public/favicon.ico'))
      app.use(require('serve-static')(__dirname + '/public', redirect: false))

      app.use(require('body-parser').json())
      app.use(require('cookie-parser')())

      app.use session
        key: 'sid'
        secret: config.session.secret
        cookie: maxAge: config.session.lifetime
        store: new MongoStore(mongooseConnection: mongoose.connection)

      app.use(passport.initialize())
      app.use(passport.session())

      app.use(injectGetAsset)
      app.use(env.create)
      app.use(generateEnv)
      app.use(updateUserSession)

    postRouteMiddleware = ->
      if config.debug
        app.use(require('errorhandler')(dumpExceptions: true, showStack: true))
      else
        app.use(require('compression')())


    #=====================================================================================
    # Start listening
    #=====================================================================================
    app.enable('trust proxy') # usually sitting behind nginx
    app.disable('x-powered-by')

    app.set('port', config.port)
    app.set('views', "#{__dirname}/app/templates")
    app.set('view engine', 'jade')
    app.set('json spaces', 2) if config.debug
    app.set('socket', socket)

    mongoose.connection.once 'open', ->
      log('MongoDB connection established', 'cyan')

      generateTemplateGlobals()

      preRouteMiddleware()
      server.use(app) # Fire up the server, all the routes go here
      postRouteMiddleware()

      app_root = "http://#{config.hostname}:#{config.port}"

      if config.ip
        http.listen(app.get('port'), config.ip, -> log("Server listening on #{app_root} (bound to ip: #{config.ip})", 'cyan'))
      else
        http.listen(app.get('port'), -> log("Server listening on #{app_root} (unbound)", 'cyan'))

With only 1 fork running it works perfectly, though socket seems to use polling instead of websocket as a transport.
screen shot 2014-07-05 at 2 00 27 pm

Connection to redis is successful:
screen shot 2014-07-05 at 2 04 26 pm

When I start 4 forks, socket can't finish handshakes anymore. What am I doing wrong?
screen shot 2014-07-05 at 1 56 37 pm

Binary Support

Since 1.0, socket.io starts to support binary data by encoding object like below

[ '52-17{"a":{"_placeholder":true,"num":0},"b":5,"c":{"_placeholder":true,"num":1}}', <Buffer 41 42 43>, <Buffer 44 45 46> ]

The idea is to extract the binary part out and put a placehoder in the JSON as metadata. However, after encoding (in this call: Adapter.prototype.broadcast.call(this, packet, opts);), the original packet is changed as well. The original packet doesn't have any binary part either so only the JSON with placehoder gets published to redis server.

Maybe changing the order of these two lines can work. Publish the packet before its binary part got taken away.

118 Adapter.prototype.broadcast.call(this, packet, opts);
119 if (!remote) pub.publish(key, msgpack.encode([packet, opts]));

Incompatibility with socket.io-emitter

Well socket.io-redis is broken at the moment. Without some workaround you cant even use it: #73 (comment) and there come another issue, which broke compatibility with emits from socket.io-emitter, because of change psubscribe for subscribe: 800ef74 so other socket.io-emitter libs wont work too. If that two packages are related, why there is no tests with that package ?

Socket working and fails

In my application running node cluster i am not able to upgrade to version 1.0.
I think it does not detect the already existing connection and polls all the running instances.

My config:

var io = socketio.listen(server);
  var redis = require('socket.io-redis');

  io.set('log level',3);

  var redisConf = {
      host: 'localhost',
      port: 6379
  };

  if(process.env.REDIS_PORT_6379_TCP_PORT && process.env.REDIS_PORT_6379_TCP_ADDR) {
    redisConf.host=process.env.REDIS_PORT_6379_TCP_ADDR;
    redisConf.port=process.env.REDIS_PORT_6379_TCP_PORT;
  }

  io.adapter(redis(redisConf));

  io.of('/private').use(passportSocketIo.authorize({
      key:    'connect.sid',       //the cookie where express (or connect) stores its session id.
      secret:  config.session.secret, //the session secret to parse the cookie
      store:   app.sessionStore,     //the session store that express uses
      cookieParser: cookieParser,
      fail: function(data, accept) {     // *optional* callbacks on success or fail
        console.log('socket auth failed');
        accept();             // second param takes boolean on whether or not to allow handshake
      },
      success: function(data, accept) {
        accept();
      }
  })).on('connection', function (socket) {

GET http://localhost:3000/socket.io/?EIO=2&transport=polling&t=1408474615079-1&sid=KtFoj5tlfhjZQe1AAAAA 400 (Bad Request) socket.io.js:2815
WebSocket connection to 'ws://localhost:3000/socket.io/?EIO=2&transport=websocket&sid=KtFoj5tlfhjZQe1AAAAA' failed: Connection closed before receiving a handshake response

I am able to get data via the socket but there are a log of errors in my console. If i set the cluster worker instances to 1 only everything is fine.

Struggling to make the chat sample with clusters

Hi,

I forked yesterday this repo: https://github.com/liviuignat/chat-example-cluster, where basically I am trying to make the chat sample application with cluster and socket.io-redis so I can post it on Heroku and have a valid, strong sample of socket.io spawned over multiple node processes. The heroku url where it is posted is this: http://chat-example-cluster.herokuapp.com/

There seems to be more questions than solutions, related to this topic, and no concrete example to make it work, this is why I decided to create this example.

Unfortunately I am having problems with the setup and I would appreciate some help, if you could take a look here (https://github.com/liviuignat/chat-example-cluster/blob/master/index.js).

Most of the times I get "Connection closed before receiving a handshake response" on localhost. On Heroku I get "failed: Error during WebSocket handshake: Unexpected response code: 503" and "et::ERR_INCOMPLETE_CHUNKED_ENCODING".

From what I see nothing is being stored on redis also.

Any help would really be appreciated.

Thanks,

Intercept selected events on remote servers

May be a hook in the broadcast function would be helpful to do some treatment on remote servers. May be something like this:

//Server.js
io.adapter(adapter({key: 'io:', pubClient: pub, subClient: sub}));
io.of('/').adapter.use('newUser', function(event, data){
  this.add(data.id, data.room);
});

//Adapter.js
  Redis.prototype.broadcast = function(packet, opts, remote){ 
    //Intercept remote events
    if(remote){
      var event = packet.data[0];
      ~Object.keys(this.fns).indexOf(event) && this.fns[event].call(this, event, packet.data[1]);
    }

    Adapter.prototype.broadcast.call(this, packet, opts);
    if (!remote) pub.publish(key, msgpack.encode([packet, opts]));
  };

Only broadcasts? No function to send to a particular socket.

normally within 1 node instance you could:
emit message from sockets[A] --->node instance -->sockets[B]
emit message from sockets[B] --->node instance -->sockets[A]

now, when i have 10 node instances, client A and client B on different instances, how do I send a message from A to B? Redis seem to only do broadcast.

PS: it seems if you do this on RedisStore it works:
socket.broadcast.emit('msg', data);
but how do I do
sockets[B].emit('msg', data);

Error launching package in Node

I have version 0.12.2 of Node, and I'm not able to get socket.io-redis to launch. I just get this error:

Express server listening on port 3000
events.js:85
      throw er; // Unhandled 'error' event
            ^
Error: Redis connection to 127.0.0.1:6379 failed - connect ECONNREFUSED
    at RedisClient.on_error (C:\Users\JWhite\Downloads\socket.io-redis-sample\node_modules\socket.io-redis\node_modules\redis\index.js:185:24)
    at Socket. (C:\Users\JWhite\Downloads\socket.io-redis-sample\node_modules\socket.io-redis\node_modules\redis\index.js:95:14)
    at Socket.emit (events.js:107:17)
    at net.js:459:14
    at process._tickCallback (node.js:355:11)

Any suggestions? Is it possible this package needs to be updated to be compatible with Node 0.12? You can replicate my issue by downloading the package socket.io-redis-sample from Github using version 12.2 of Node and then running npm install and npm start in the base directory. Thanks!

Redis question

I am new to 1.0 so i need some help with this. This is more of a structure question...
I have to use redis in my application but i am also using socket.io to have a sort of an api for client app. And on top of that i want to have multiple nodes running so i installed socket.io-redis and added it as adapter.

The thing that is bugging me is that i want to use node_redis for my redis usage. Can i somehow use that package as adapter for socket.io? Only because i don't want to have:

var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));

and

var redisnode  = require("redis"), client = redisnode.createClient();

I would like to pack that to one connection, any ideas?

Unpack error

I'm using redis as adapter and sometimes it gives me the error

TypeError: Object #<Object> has no method 'readUInt64BE'
    at Decoder.parse (/var/www/00k-admin/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:131:18)
    at Decoder.map (/var/www/00k-admin/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:34:23)
    at Decoder.parse (/var/www/00k-admin/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:68:17)
    at Decoder.map (/var/www/00k-admin/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:34:23)
    at Decoder.parse (/var/www/00k-admin/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:68:17)
    at Decoder.map (/var/www/00k-admin/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:34:23)
    at Decoder.parse (/var/www/00k-admin/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:68:17)
    at Decoder.array (/var/www/00k-admin/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:51:21)
    at Decoder.parse (/var/www/00k-admin/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:74:17)
    at Decoder.map (/var/www/00k-admin/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:34:23)
    at Decoder.parse (/var/www/00k-admin/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:68:17)
    at Decoder.array (/var/www/00k-admin/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:51:21)
    at Decoder.parse (/var/www/00k-admin/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:74:17)
    at Object.decode (/var/www/00k-admin/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:199:23)
    at Redis.onmessage (/var/www/00k-admin/node/node_modules/socket.io-redis/index.js:93:24)
    at RedisClient.EventEmitter.emit (events.js:106:17)

Namespaces

What would be the best way to handle syncing namespace creation among clustered node.js instances?
When one instance creates namespaces and a client connects to another instance it won't find these namespaces and can't connect.
Is this something socket.io-redis should do or should I write my own pub/sub system to handle this?

Support Redis UNIX domain sockets

Please revert this commit which removes the Redis UNIX domain socket support:

f49d9f8

Background: Redis server can be accessed in two ways.

  1. via TCP connection, you need to know Redis hostname/IP address and port.
  2. via UNIX domain socket, this is a special file and you need to know the path to it. Works only when client and server are located on the same host.

Multiple clients can use the same UNIX domain socket file at the same time. Comment in the revert indicates that there must be a some sort of misunderstanding about that.

I have a cluster with 10 workers handling socket.io connections at the same time. I'd like to use this library to make them to work together. I prefer to use UNIX domain sockets instead of local TCP connections because UNIX domain socket provide a better performance on Linux as they bypass the TCP/IP stack.

@rauchg

Performance / stability of socket.io-redis

Regarding to: socketio/socket.io#1457

In the previous version of socket.io is concept of stores, in version 1.0 there are adapters. Stores in previous version was really susceptible for memory leaks and other errors. How this was fixed in version 1.0?

I couldn't find a good explanation of this concept. Basically there is a poor information stream for version 1.0

How to get all clients in a room, or all rooms a client is in?

Is there a way to do this across multiple node processes in the recent v1.0 release?

I saw this was a very requested feature. Is there a chance this gets implemented in a the near future? If not, has anyone succeeded in simulating this functionality based on the current 1.0 version?

Thanks!

Documentation doesn't match release

From the readme, I can handle Redis connection error as follows:

adapter.pubClient.on('error', function(){});
adapter.subClient.on('error', function(){});

But I couldn't do it at all. Turns out the latest release version (0.1.4) doesn't implement that. Can you release a version that matches the documentation? If not, I think the documentation should be updated or at least made clear to avoid people from pulling their hair. Thanks!

New maintainer for this project

This is obviously a very popular, useful and important part of Socket.IO which has not been maintained for a while. Has Socket.IO considered appointing a new maintainer? It's a big hassle finding a combination of fixes from all the forks right now.

Problem using UNIX domain socket

it fails on decoding message

var args = msgpack.decode(msg);

events.js:72
throw er; // Unhandled 'error' event
^
SyntaxError: Unexpected token �
at Object.parse (native)

How to emit events in callback function?

Here is my case: nodejs server receives messages from AMQP, then push those messages to UI through socket.io-redis.

I try to call emit() function in the callback of AMQP, it works well. However, I can NOT find data in redis server?

Codes

var redisPort = 6379,
    redisHost = 'localhost';

var app = require('express')(),
    http = require('http').Server(app),
    io = require('socket.io')(http),
    redisAdapter = require('socket.io-redis'),
    redis = require('redis');

var
    pub = redis.createClient(redisPort, redisHost),
    sub = redis.createClient(redisPort, redisHost, {detect_buffers: true});

//
io.adapter( redisAdapter({pubClient: pub, subClient: sub}) );

app.get('/', function(req, res){

    res.sendFile('index.html');
});

io.on('connection', function(socket){
    console.log("web socket connection ...");

    socket.join('room');

    socket.on('disconnect', function(){
        console.log("socketio diconnect...");
    });
});

callback function in AMQP

function response(msg){
    io.to('room').emit('online', msg);
}

...

ch.consume(queue, response, {noAck: true});

Am I right to use this API emit()? or any other API?

Exception error with msgpack

I had exception error when using "socket.io": "1.3.7", "socket.io-redis": "0.2.0", nodejsV4
My config:
var pub = redis(socket_redis.port, socket_redis.host);
var sub = redis(socket_redis.port, socket_redis.host, {
return_buffers: true
});

Error: 367 trailing bytes
at Object.decode (/var/www/api/node_modules/msgpack-js/msgpack.js:200:47)
at Redis.onmessage (/var/www/api/node_modules/socket.io-redis/index.js:93:24)
at emitTwo (events.js:87:13)
at RedisClient.emit (events.js:172:7)
at RedisClient.return_reply (/var/www/api/node_modules/redis/index.js:618:22)
at /var/www/ibooknail-api/node_modules/redis/index.js:309:18
at doNTCallback0 (node.js:419:9)
at process._tickDomainCallback (node.js:389:13)

Errors when decoding message on other node

events.js:72
        throw er; // Unhandled 'error' event
              ^
Error: 58 trailing bytes
    at Object.decode (/vagrant/laravel/node/node_modules/socket.io-redis/node_modules/msgpack-js/msgpack.js:200:47)
    at Redis.onmessage (/vagrant/laravel/node/node_modules/socket.io-redis/index.js:95:24)
    at RedisClient.emit (events.js:117:20)
    at RedisClient.return_reply (/vagrant/laravel/node/node_modules/redis/index.js:696:22)
    at HiredisReplyParser.<anonymous> (/vagrant/laravel/node/node_modules/redis/index.js:321:14)
    at HiredisReplyParser.emit (events.js:95:17)
    at HiredisReplyParser.execute (/vagrant/laravel/node/node_modules/redis/lib/parser/hiredis.js:43:18)
    at RedisClient.on_data (/vagrant/laravel/node/node_modules/redis/index.js:547:27)
    at Socket.<anonymous> (/vagrant/laravel/node/node_modules/redis/index.js:102:14)
    at Socket.emit (events.js:95:17)

Easy to reproduce: start 2 node processes the have socket.io-redis configured, broadcast a message on one of the processes, the other one crashes on decoding of the message.

When I run redis-cli:

127.0.0.1:6379> PSUBSCRIBE socket.io#*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "socket.io#*"
3) (integer) 1
1) "pmessage"
2) "socket.io#*"
3) "socket.io#8CwXOc"
4) "\x92\x83\xa4type\x02\xa4data\x92\xa3new\x81\xa5title\xa8New note\xa3nsp\xa6/notes\x82\xa5rooms\xc4\xa5flags\xc4"

This gets called on a new message:

Redis.prototype.onmessage = function(pattern, channel, msg){

the msg has value:

efbfbdefbfbdefbfbd7479706502efbfbd64617461efbfbdefbfbd6e6577efbfbdefbfbd7469746c65efbfbd4e6577206e6f7465efbfbd6e7370efbfbd2f6e6f746573efbfbdefbfbd726f6f6d73efbfbdefbfbd666c616773efbfbd

Redis version: 2.8.17
Node version: 0.10.32
Socket.io version: 1.1.0
Socket.io-redis version: 0.1.3
Redis package version: 0.12.1

Target specific socket connections?

Is there any way to target specific socket connections, from an instance that doesn't have the connection to that socket connection?
I'm afraid this requires similar functionality as rooms, which also isn't implemented yet.

Feature Request: Check if Redis-Server available

Currently an exception is thrown if the specified redis server is not available.
Unfortunately the exception is thrown async by some event where no reasonable try-catch can be put in place.

Would be nice if socket.io-redis would provide functionality to check whether a configuration (host:port) is currently available/valid or not.

Unhandled error event

Hey,
I implemented a redis adapter in this way

var socketioPub = m_redis.createClient(null, null, { detect_buffers: true });
socketioPub.on('error', function (err) { ... });
var socketioSub = m_redis.createClient(null, null, { detect_buffers: true });
socketioSub.on('error', function (err) { ... });
io.adapter(m_socketio_redis({ pubClient: socketioPub, subClient: socketioSub }));

So if the redis server e.g. is not running I can handle that error, but I get an unhandled error event from https://github.com/Automattic/socket.io-redis/blob/master/index.js#L73

[...]
  function Redis(nsp){
    Adapter.call(this, nsp);

    var self = this;
    sub.psubscribe(prefix + '#*', function(err){
      if (err) self.emit('error', err); <--------------------------------
    });
    sub.on('pmessage', this.onmessage.bind(this));
  }
[...]

because there is no event listener attached and I see no way to attach one from the outside. Any ideas?

Best regards!

uncaught error: 348 trailing bytes

I am using socket.io-emitter to broadcast an event to a set of channels with a for loop:

In the file, I have:

// in the file
var io = require('socket.io-emitter')({
  host: 'localhost',
  port: 6379
});

module.exports = {
    exampleFunction: function(req, res, next) {
      var channels = req.param('channels'),
            data = req.param('data');

      for (var i=0; i<channels.length; i++) {
        io.to(channels[i]).emit('example event', data)
      }
    }

}

In app.js, I have socket.io-redis:

io.adapter(socketio_redis({ 
  host: 'localhost', 
  port: 6379,
  pubClient: redis.createClient(6379, '127.0.0.1'),
  subClient: redis.createClient(6379, '127.0.0.1')
}))

When I try to run exampleFunction, I get the following uncaught error in my console:

Error: 348 trailing bytes
    at Object.decode (C:\Users\Zachary\Documents\GitHub\thegraduate_backend\node
_modules\socket.io-redis\node_modules\msgpack-js\msgpack.js:200:47)
    at Redis.onmessage (C:\Users\Zachary\Documents\GitHub\thegraduate_backend\no
de_modules\socket.io-redis\index.js:93:24)
    at RedisClient.EventEmitter.emit (events.js:106:17)
    at RedisClient.return_reply (C:\Users\Zachary\Documents\GitHub\thegraduate_b
ackend\node_modules\redis\index.js:672:22)
    at ReplyParser.<anonymous> (C:\Users\Zachary\Documents\GitHub\thegraduate_ba
ckend\node_modules\redis\index.js:309:14)
    at ReplyParser.EventEmitter.emit (events.js:95:17)
    at ReplyParser.send_reply (C:\Users\Zachary\Documents\GitHub\thegraduate_bac
kend\node_modules\redis\lib\parser\javascript.js:300:10)
    at ReplyParser.execute (C:\Users\Zachary\Documents\GitHub\thegraduate_backen
d\node_modules\redis\lib\parser\javascript.js:211:22)
    at RedisClient.on_data (C:\Users\Zachary\Documents\GitHub\thegraduate_backen
d\node_modules\redis\index.js:534:27)
    at Socket.<anonymous> (C:\Users\Zachary\Documents\GitHub\thegraduate_backend
\node_modules\redis\index.js:91:14)

When I change data to be something smaller, like:

var data = { testing: true }

I get the same error, but to a smaller number of trailing bytes. e.g. 95 trailing bytes.

Sending a message from one node to a specific socket on another node.

So I'm trying to run a multi-node system where one user can initiate private contact with another user, potentially across nodes, but I'm not sure how to do this with a multi-node system and socket.io-redis.

Originally with a single node, I was simply attaching session objects to each socket. This allowed me to search for socketIds based on session ids and then send a message accordingly, something like: client1 -> socket-server -> client 2.

But now, I can't search the sockets of one node from another.

I'm assuming there must be a way to collect all the sockets connected to the entire network from the redis cache used by socket.io, but I'm not sure and I don't know how to find out. I've been inspecting redis with a desktop client and I can't see anything being cached by socket-io.

Anyways, is it possible to scan the sockets of one node from another node? Or, is what I'm trying to do even possible in this framework?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.