1

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

我的 webapp 实现了以下

  1. 用户发布 100 个 json 对象
  2. 每个 json 对象都通过服务调用进行处理
  3. 服务调用完成后,会话变量会递增
  4. 在会话变量递增时,必须向客户端发送服务器端事件以更新进度条

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

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

一旦处理了 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 触发服务器端事件。我收到一条错误消息,提示无法将标头设置为已发送的响应

4

2 回答 2

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);
于 2013-07-27T00:32:21.943 回答
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. 然后,向另一个 URL 端点发出另一个请求,该端点通过点击完成“发布 JSON 对象”的任务/your-endpoint-that-processes-json.(注意:显然这个请求可能由同一个用户/浏览器发出......但应用程序不知道/不在乎关于那个)

    在这个动作中,你处理他们的 JSON 数据,增加你的计数器,或者做任何事情......并返回 200 响应。但是,您在此操作中要做的一件事是在您的订阅者从第 1 步开始收听的 Redis 频道上“发布”一条消息,以便客户端获取更新。从技术上讲,此操作不需要向客户端返回任何内容,假设用户将基于 200 状态代码或通过管道发送的服务器发送事件获得某种类型的反馈......

我可以给你的一个具体例子是这个要点,它是本文的一部分。请注意,这篇文章在这一点上已有几年的历史,因此可能需要稍微调整一些代码。另请注意,这不能保证只是一个示例(即:它没有经过“负载测试”或类似的东西)。但是,它可能会帮助您入门。

于 2013-07-26T16:30:40.963 回答