Javascript 节点JS:将响应对象传递到服务器端事件的Bull队列

Javascript 节点JS:将响应对象传递到服务器端事件的Bull队列,javascript,node.js,express,bull.js,Javascript,Node.js,Express,Bull.js,我被困在一个建筑决策上。我有Node+Express应用程序,它有一个用于上传文件的API。上传完成后,响应关闭,上传的文件在Bull Queue+Redis的帮助下由FFMPEG批处理。这种结构工作得很好,但最近我开始测试服务器端事件,以便向最终用户提供有关处理的更新。但我无法将响应对象传递给Bull队列,以便从服务器向用户写入定期更新 1。进口 import childProcess from 'child_process'; import Bull from 'bull' const Qu

我被困在一个建筑决策上。我有Node+Express应用程序,它有一个用于上传文件的API。上传完成后,响应关闭,上传的文件在Bull Queue+Redis的帮助下由FFMPEG批处理。这种结构工作得很好,但最近我开始测试服务器端事件,以便向最终用户提供有关处理的更新。但我无法将响应对象传递给Bull队列,以便从服务器向用户写入定期更新

1。进口

import childProcess from 'child_process';
import Bull from 'bull'
const Queue = new Bull('background_job', {redis: {port: process.env.port, host: process.env.host, password: process.env.password}});
const authUser = (req) => {
    return new Promise((resolve, reject) => {
      //do some work
    })
}

const checkUploadFile = (result) => {
    return new Promise((resolve, reject) => {
      //do some more work
    })
}

const insertPost= (result, res) => {
    return new Promise((resolve, reject) => {
      //do final work
       ...........
      //preparing server side events
       const headers = {
            'Content-Type': 'text/event-stream',
            'Connection': 'keep-alive',
            'Cache-Control': 'no-cache',
            'Access-Control-Allow-Origin': '*'
        };
        res.writeHead(200, headers);
        res.write(JSON.stringify({status: true, id: 1})); //testing server side events for the first time

        //Let's continue to Bull
        const data = {res: res} <- error here: TypeError: Converting circular structure to JSON 
        const opts = {removeOnComplete: true, removeOnFail: true}
        resolve({data: data, opts: opts});
    })
}
Queue.process((job, done) => {
    const res = job.data.res
    childProcess.execFile('someScript.sh', [`some`, `arguments`], { stdio: ['pipe', 'pipe', 'ignore']}, (err, stderr, stdout) => {
        if(err){
            done(new Error("Failed: " + err))
            res.write(JSON.stringify({status: true, id: 2})); //here using SSE
            res.end()
        } else {
            done()
            res.write(JSON.stringify({status: false})); //here using SSE
            res.end()
        }
    })
})
2。上传功能

const uploadVideo = async(req, res) => {
    try{
        const result = await authUser(req);
        const result2 = await checkUploadFile(result);
        const result3 = await insertPost(result2, res);
        await Queue.add(result3.data, result3.opts)
    } catch(err){
        res.status(403).send(err);
    }
}
3。承诺

import childProcess from 'child_process';
import Bull from 'bull'
const Queue = new Bull('background_job', {redis: {port: process.env.port, host: process.env.host, password: process.env.password}});
const authUser = (req) => {
    return new Promise((resolve, reject) => {
      //do some work
    })
}

const checkUploadFile = (result) => {
    return new Promise((resolve, reject) => {
      //do some more work
    })
}

const insertPost= (result, res) => {
    return new Promise((resolve, reject) => {
      //do final work
       ...........
      //preparing server side events
       const headers = {
            'Content-Type': 'text/event-stream',
            'Connection': 'keep-alive',
            'Cache-Control': 'no-cache',
            'Access-Control-Allow-Origin': '*'
        };
        res.writeHead(200, headers);
        res.write(JSON.stringify({status: true, id: 1})); //testing server side events for the first time

        //Let's continue to Bull
        const data = {res: res} <- error here: TypeError: Converting circular structure to JSON 
        const opts = {removeOnComplete: true, removeOnFail: true}
        resolve({data: data, opts: opts});
    })
}
Queue.process((job, done) => {
    const res = job.data.res
    childProcess.execFile('someScript.sh', [`some`, `arguments`], { stdio: ['pipe', 'pipe', 'ignore']}, (err, stderr, stdout) => {
        if(err){
            done(new Error("Failed: " + err))
            res.write(JSON.stringify({status: true, id: 2})); //here using SSE
            res.end()
        } else {
            done()
            res.write(JSON.stringify({status: false})); //here using SSE
            res.end()
        }
    })
})
5。PM2记录的错误

TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'Socket'
    |     property 'parser' -> object with constructor 'HTTPParser'
    --- property 'socket' closes the circle
我尝试使用
JSON.stringify(res)
将响应对象作为JSON传递,但也没有成功。现在,我正在考虑这种方法是否正确,或者是否应该使用Socket.io(对于简单的服务器端事件来说,这是一种过分的做法)


谢谢你

你为什么还要写这句话:

const data = {res: res} <- error here: TypeError: Converting circular structure to JSON.
const data = {res: res} <- error here: TypeError: Converting circular structure to JSON 
例如:

const uploadVideo = async(req, res) => {
    try{
        const result = await authUser(req);
        const result2 = await checkUploadFile(result);
        const result3 = await insertPost(result2, res);
        await Queue.add(res, result3.opts); // still have access to res
    } catch(err){
        res.status(403).send(err);
    }
删除此行:

const data = {res: res} <- error here: TypeError: Converting circular structure to JSON.
const data = {res: res} <- error here: TypeError: Converting circular structure to JSON 
编辑:

我明白你的意思。看了看公牛舱。你为什么不能这样做呢

const uploadVideo = async(req, res) => {
  try{
      res.jobId = 0; // we need a way to know if job completed is our request          const result = await authUser(req);
      const result2 = await checkUploadFile(result);
      const result3 = await insertPost(result2, res);
      Queue.add({id: res.jobId, somedatafromresult3: 'result3.somedata' }, result3.opts);
      Queue.on("completed", (err, data) => {
        if (data.id === res.jobId) { // check to see if completed job is our one.
          res.write(JSON.stringify(data)); //here using SSE
          res.end()
        }
        console.log(data);
      });
  } catch(err){
      res.status(403).send(err);
  }
}
然后在process函数中,只需返回将发出的数据。i、 e

  videoQueue.process(function(job, done){
  childProcess.execFile('someScript.sh', [`some`, `arguments`], { stdio: ['pipe', 'pipe', 'ignore']}, (err, stderr, stdout) => {
    if(err){
        done(err, {status: true, id: job.data.id});
    } else {
      done(null, {status: false, id: job.data.id});
    }
})
})
)

您可以使用与通过SSE连接到客户端的路由通信。使用
作业更新进度。进度(百分比)
,传递一个数字。然后,Express route作用域可以在此基础上旋转,并随着作业的进行向客户端发送SSE事件

下面是一个基本的可运行示例,作为概念证明,您可以将处理、错误处理和
job.progress
和SSE逻辑添加到其中

const express=require(“express”);
const fs=要求(“fs”).承诺;
常量路径=要求(“路径”);
const Queue=require(“bull”);
常量睡眠=(毫秒=1000)=>
新承诺(解析=>setTimeout(解析,毫秒))
;
const queue=新队列(“test”,process.env.REDIS_URL);
进程(4,异步作业=>{
for(设i=1;i{
试一试{
res.set({
“访问控制允许来源”:“*”,
“缓存控制”:“无缓存”,
“连接”:“保持活动状态”,
“内容类型”:“文本/事件流”,
});
res.flushHeaders();
const job=wait queue.add({
秒数:Math.abs(+req.query.seconds)| 10,
});
让连接=真;
res.on(“关闭”,()=>{
连接=错误;
});
for(;已连接;等待睡眠()){
const j=wait queue.getJob(job.id);
const progress=等待j.进度();
res.write(`${progress}\n`);
如果(进度>=100){//TODO处理作业错误
打破
}
}
res.write(等待job.finished());
}
捕捉(错误){
res.write(错误消息);
}
最后{
res.end();
}
})
.listen(应用程序获取(“端口”),()=>
log(`server监听端口${app.get(“端口”)}`)
)
;
样本运行:

$curl本地主机:5000
0
10
20
30
40
50
60
70
80
90
100
作业64完成!


另请参见哪个示例客户端可以读取响应流。

这不起作用。它抛出了相同的错误。而且我没有直接从
uploadVideo
函数传递
res
,因为我还需要在
队列中传递其他数据。add
方法。它如何抛出相同的错误,因为“res”未分配到任何位置。问题是const data={res:res}。因此,如果您删除了它,错误怎么会仍然存在。是的,但是您可以扩展Queue.add所使用的参数。它现在是在哪一行上导致错误的?Queue.add()的定义在哪里?因为我将Queue.process误认为Queue.add。请查看我编辑的答案。