2014-06-15 111 views
1

对于仅限登录用户,我想以某种方式通知他们,如果他们有任何新的通知。例如,假如某个成员向他们发送了一条私人消息,我想告诉用户他们有一条要查看的新消息(假设他们没有刷新该页面)。nodejs订阅redis的推送通知

使用Nodejs和Redis,我该如何去做这件事?

注:我只需要nodejs发送一个小JSON给用户说他们有一个新的消息。

的工作流程如下,我在想:

1. user is logged in, a new message is sent to them. 
    2. somehow using nodejs and redis and long-polling, nodejs communicates back to the logged in users browser they have a pending message. 
    3. when nodejs sends this push notification, I then call another javascript function that will call a rest service to pull down additional json with the message. 

我整合的NodeJS到现有的应用程序,所以我要保持它尽可能简单以只负责通知并没有做任何的NodeJS额外的逻辑。

有人可以概述我该如何解决这个问题吗? 我应该以某种方式使用redis http://redis.io/topics/pubsub吗?
即使阅读了关于它的网页,我也不确定它是如何工作的。

回答

5

如果您正在将node服务集成到现有应用程序中,我宁愿使用某种消息传递系统将消息从该应用程序传递到node而不是DB,即使是内存数据库。 为了清晰起见,我会假设你可以使用rabbitmq。如果您确实需要使用redis,您只需要找到一种方法来使用它的发布,而不是使用rabbitmq发布和相应的节点端订阅,但我会想象整体解决方案是相同的。

您需要以下模块:

  • rabbitmq服务器在外部应用程序发送的消息
  • rabbitmq库(安装大致相同的redis复杂性),大多数语言都支持
  • rabit.js模块为node订阅消息或与外部应用程序进行通信
  • socket.io模块为node建立node服务器和客户端之间的实时连接

我也将承担两个您的外部应用程序和node服务器访问某些共享数据库(可以是Redis的),其中node客户端会话信息被存储(例如, redis-session-store for node)。这将允许使用sessionId来验证消息的用途,会话中的用户是否已登录,以及某些用户是否需要发送通知(通过外部应用程序)。

这是你的筹码可能会看怎么样(糙米):

node定义发布通知,它需要启动/停止对给定sessionId发送消息的外部应用程序。我将假设对于给定的sessionId,用户信息可以从共享数据库的任一侧(node或外部应用程序)恢复,并且可以验证用户(这里为了简化,通过检查session.authenticated_user)。还定义了用户收听传入消息的用户:

var context = require('rabbit.js').createContext(); 

var pub = context.socket('PUB'); 
var sub = context.socket('SUB'); 

定义从node服务器socket.io连接(县)客户端。一旦客户的网页被重新加载并且io.connect()被调用,下面的代码将被执行(参见答案末尾的clinet端)。在建立新的连接时,验证用户是否已登录(意味着它的凭证在会话中),注册套接字处理程序并向外部应用程序发布通知,以便开始为此发送消息sessionId。此处的代码假设在登录/注销时重新加载页面(因此会话为新的socket.io)。如果不是这种情况,只需从客户端发送相应的socket.io消息到node,并按照与新连接完成相同的方式在下面的方法中注册处理程序(这超出了本示例的范围):

var sessionStore = undefined; // out-of-scope: define redis-session-store or any other store 

var cookie = require("cookie"), 
    parseSignedCookie = require('connect').utils.parseSignedCookie; 

// will store a map of all active sessionIds to sockets 
var sockets = {}; 

// bind socket.io to the node http server 
var io = require('socket.io').listen(httpServer); 

// assumes some config object with session secrect and cookie sid 
io.sockets.on("connection", function(socket) { 
    if (socket.handshake.headers.cookie) { 
     var cks = cookie.parse(socket.handshake.headers.cookie); 
     var sessionId = parseSignedCookie(cks[config.connectSid], config.sessionSecret); 

     // retrieve session from session store for sessionId 
     sessionStore.get(sessionId, function(err, session) { 
      // check if user of this session is logged in, 
      // define your elaborate method here 
      if (!err && session.authenticated_user) { 
       // define cleanup first for the case when user leaves the page 
       socket.on("disconnect", function() { 
        delete sockets[sessionId]; 
        // notify external app that it should STOP publishing 
        pub.connect('user_exchange', function() { 
         pub.write(JSON.stringify({sessionId: sessionId, action: 'stop', reason: 'user disconnected'}), 'utf8'); 
        }); 
       }); 

       // store client-specific socket for emits to the client 
       sockets[sessionId] = socket; 

       // notify external app that it should START publishing 
       pub.connect('user_exchange', function() { 
        pub.write(JSON.stringify({sessionId: sessionId, action: 'start'}), 'utf8'); 
       }); 
      } 
     }); 
    } 
}); 

连接用户到rabbitmq交换赶上消息,并将它们发射到客户端:

sub.connect('messages_exchange', function() { 
    sub.on("readable", function() { 
     // parse incoming message, we need at least sessionId 
     var data = JSON.parse(sub.read()); 
     // get socket to emit for this sessionId 
     var socket = sockets[data.sessionId]; 
     if (socket) { 
      socket.emit("message", data.message); 
     } else { 
      // notify external app that it should STOP publishing 
      pub.connect('user_exchange', function() { 
       pub.write(JSON.stringify({sessionId: sessionId, action: 'stop', reason: 'user disconnected'}), 'utf8'); 
      }); 
      // further error handling if no socket found 
     } 
    }); 
}); 

最后你的客户会看起来大致是这样的(这里的翡翠,但是这只是因为我已经有这整个沿着这些线堆叠):

script(src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js") 
script(src="/socket.io/socket.io.js") 

script(type='text/javascript'). 
    $(function(){ 
     var iosocket = io.connect(); 
     iosocket.on('connect', function() { 
      // do whatever you like on connect (re-loading the page) 

      iosocket.on('message', function(message) { 
       // this is where your client finally gets the message 
       // do whatever you like with your new message 
      }); 
     }); 

     // if you want to communicate back to node, e.g. that user was logged in, 
     // do it roughly like this 
     $('#btnSend').click(function(event) { 
      iosocket.send('a message back to the node server if you need one'); 
     }); 
    });