【问题标题】:nodejs push notification that subscribes to redis订阅redis的nodejs推送通知
【发布时间】:2014-06-15 22:11:50
【问题描述】:

仅适用于登录用户,如果他们有任何信息,我想以某种方式通知他们。新通知。

例如,假设一个成员给他们发了一条私人消息,我想告诉用户他们有一条新消息要查看(假设他们没有刷新页面)。

使用 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 吗?
即使在阅读了有关它的页面后,我也不确定它是如何工作的。

【问题讨论】:

    标签: node.js long-polling


    【解决方案1】:

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

    您需要以下模块:

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

    我还将假设您的外部应用程序和您的 node 服务器都可以访问一些共享数据库(可以是 redis),其中存储了 node 客户端会话信息(例如 redis-session-store 用于 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 会话)。如果不是这种情况,只需从客户端向node 发出相应的socket.io 消息,并以与新连接相同的方式在下面的方法中注册一个处理程序(这超出了本文的范围)示例):

    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
            }
        });
    });
    

    最后,您的客户端将大致如下所示(在 Jade 中,但这只是因为我已经按照这些思路拥有了整个堆栈):

    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');
            });
        });
    

    【讨论】:

      【解决方案2】:

      Flickr 也对他们如何使用 NodeJS 和 Redis 创建一个高度可用且可扩展的推送通知系统进行了很好的解释。

      http://code.flickr.net/2012/12/12/highly-available-real-time-notifications/

      【讨论】:

        猜你喜欢
        • 2015-06-19
        • 2021-10-16
        • 1970-01-01
        • 2019-09-24
        • 1970-01-01
        • 2017-11-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多