Node.js监听会话变量更改并触发服务器发送的事件

时间:2013-07-26 13:14:24

标签: node.js server-sent-events

我正在使用express.js编写一个webapp。

我的webapp实现了以下

  1. 用户发布100个json对象
  2. 通过服务电话处理每个json对象
  3. 服务调用完成后,会话变量递增
  4. 在增加会话变量时,必须将服务器端事件发送到客户端以更新进度条
  5. 如何实现侦听会话变量更改以触发服务器发送的事件?

    听取变量变化不是我寻求的唯一解决方案吗?

    我需要在处理JSON对象后实现发送服务器发送的事件。

    欢迎任何适当的建议

    编辑(根据Alberto Zaccagni的评论)

    我的代码如下所示:

    function processRecords(cmRecords,requestObject,responseObject)
    {
        for (var index = 0; index < cmRecords.length; index++) 
        {
            post_options.body = cmRecords[index];
            request.post(post_options,function(err,res,body)
            {
                if(requestObject.session.processedcount)
                    requestObject.session.processedcount = requestObject.session.processedcount + 1;
                else
                    requestObject.session.processedcount = 1;
    
                if(err)
                {
                    appLog.error('Error Occured %j',err);
                }
                else
                {
                    appLog.debug('CMResponse: %j',body);
                }
    
                var percentage = (requestObject.session.processedcount / requestObject.session.totalCount) * 100;
    
                responseObject.set('Content-Type','text/event-stream');
                responseObject.json({'event':'progress','data':percentage});
            });
        };
    
    
    }
    
    1. 更新第一条记录并使用responseObject(快速响应对象)触发服务器端事件时

    2. 更新第二条记录后,我尝试使用相同的responseObject触发服务器端事件。我收到一条错误消息,指出无法将标头设置为已发送的响应

2 个答案:

答案 0 :(得分:0)

如果没有看到主应用程序中的路线/操作,很难确切了解情况......

但是,我认为您遇到的问题是您尝试向客户端(浏览器)发送两组标头,这是不允许的。这是不允许的原因是因为浏览器不允许您在发送初始响应后更改响应的内容类型...因为它使用它作为如何处理您发送响应的响应的指示器。在将这些(或任何其他标头)发送到客户端一次后(一个请求 - >一个响应 - >一组标头返回到客户端),您无法更改其中任何一个(或任何其他标头)。这可以防止您的服务器出现精神分裂症(例如,通过从“200 Ok”响应切换到“400 Bad Request”)。

在这种情况下,在初始请求中,您告诉客户“嘿,这是一个有效的请求,这是我的回复(通过200的状态,在其他地方设置或由ExpressJS承担),并请保持通讯渠道畅通,以便我发送更新(通过将您的内容类型设置为text/event-stream)“。

至于如何“修复”这个,有很多选择。当我这样做时,我已经使用redis的pub / sub功能作为连接所有内容的“管道”。所以,流程是这样的:

  1. 有些客户向/your-event-stream-url

    发送请求

    在此请求中,您设置了Redis订户。无论您想要什么,都可以处理此订阅中的任何内容。在您的情况下,您希望“在管道中将一些数据发送到具有至少data属性的JSON对象中的客户端。”设置此客户端后,只需返回“200 Ok”的响应,并将内容类型设置为“text / event-stream”。 Redis将负责其余工作。

  2. 然后,另一个请求到另一个URL端点,通过点击/your-endpoint-that-processes-json. 完成“发布JSON对象”的任务(注意:显然可以提出此请求)由相同的用户/浏览器...但应用程序不知道/关心那个)

    在此操作中,您将处理其JSON数据,增加计数器或执行任何操作...并返回200响应。但是,您在此操作中要执行的操作之一是在Redis频道上“发布”消息,您的第1步中的订阅者正在侦听,以便客户端获取更新。从技术上讲,假设用户将根据200状态代码或从管道发送的服

  3. 我可以给你的一个明确的例子是this gist,它是this article的一部分。请注意,这篇文章已经有几年了,所以有些代码可能需要稍微调整一下。还要注意,这不能保证只是一个例子(即:它没有经过“负载测试”或类似的东西)。但是,它可能会帮助您入门。

答案 1 :(得分:0)

我想出了一个解决方案,请告诉我这是否是正确的做事方法?

此解决方案是否可以跨会话使用?

服务器端代码

var events = require('events');
var progressEmitter = new events.EventEmitter();

exports.cleanseMatch = function(req, res)
{
    console.log('cleanseMatch Inovked');
    var progressTrigger = new events.EventEmitter;
    var id = '';
    var i = 1;  

     id = setInterval(function(){
        req.session.percentage = (i/10)*100;
        i++;
        console.log('PCT is: ' + req.session.percentage);
        progressEmitter.emit('progress',req.session.percentage)

        if(i == 11) {
        req.session.percentage = 100;
        clearInterval(id);              
        res.json({'data':'test'});
        }
    },1000);
}

exports.progress = function(req,res)
{
    console.log('progress Inovked');
    // console.log('PCT is: ' + req.session.percentage);

    res.writeHead(200, {'Content-Type': 'text/event-stream'});
    progressEmitter.on('progress',function(percentage){
         console.log('progress event fired for : ' + percentage);
         res.write("event: progress\n");
         res.write("data: "+percentage+"\n\n");
    });
}

客户端代码

var source = new EventSource('progress');
source.addEventListener('progress', function(e) {
    var percentage = JSON.parse(e.data);
    //update progress bar in client
    App.updateProgressBar(percentage);
}, false);