2013-07-26 76 views
1

我正在使用express.js编写web应用程序。一旦服务呼叫完成Node.js侦听会话变量更改并触发服务器发送的事件

我的web应用程序实现以下

  1. 用户讯息100 JSON对象
  2. 每个JSON目的通过一种服务呼叫
  3. 处理,会话变量被递增
  4. 在增加会话变量时,必须向客户端发送服务器端事件以更新进度条

如何实现监听会话变量更改以触发服务器发送的事件?

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

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

任何适当的建议是欢迎

编辑(基于阿尔贝托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. 当第一个记录被更新和服务器使用响应对象(快速响应对象)触发旁边事件

  2. 当第二条记录更新时,我尝试使用相同的responseObject触发服务器端事件。我得到一个错误说不能设置标头已经被发送的响应

+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”的响应,并将内容类型设置为“文本/事件流”。 Redis将负责其余的事情。

  2. 然后,另一个请求到击中/your-endpoint-that-processes-json.(注完成的“张贴JSON对象”任务的另一个URL端点做:显然这个要求由同一个用户/浏览器进行......但是应用程序不知道/关心)

    在此操作中,您将处理其JSON数据,增加计数器或执行任何操作...并返回200响应。但是,您在此操作中执行的其中一项操作是在#1用户正在监听的Redis频道上“发布”一条消息,以便客户端获得更新。从技术上讲,这个动作不需要返回任何东西给客户端,假设用户会根据200-状态代码或发送服务器发送的事件发送某种类型的反馈...

我可以给你的一个实例是this gist,它是this article的一部分。请注意,这篇文章已经有几年了,所以有些代码可能需要稍微调整一下。另外请注意,这不能保证只是一个例子(即:它没有被“加载测试”或类似的东西)。但是,它可以帮助您开始。

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); 
相关问题