每次在 NodeJs 中命中 api 端点时发送服务器发送事件(SSE)
Send a Server Sent Event(SSE) each time when an api end point is hit in NodeJs
我正在尝试实现一种方法,每次我的服务器端点被命中时,都会在 NodeJs 中发送服务器发送事件 (SSE)。
我是这样实现的
//...code...
import EventSource from "eventsource";
const EventEmitter = require('events');
const events = new EventEmitter();
// events.setMaxListeners(10);
// api end point route from where the new order requests will be coming
router.route('/new-order')
.post((req, res) => {
const orderData = req.body.data;
//... save order and emit an event to send response to the client react app
events.emit('newOrder', orderData);
});
// define a function to send SSE events to the client
const sse = res => data => {
const dataToSend = JSON.stringify(data);
res.write(`data:${dataToSend}`);
res.write("\n\n");
};
// define an EventSource route for the client to be connected for new events
router.route('/sse')
.get((req, res) => {
res.writeHead(200, {
"Connection": "keep-alive",
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
});
res.write("\n");
const sendSSE = sse(res);
/*
PROBLEMATIC CODE-- EACH request to /sse route adds new event listener,
and in minutes, it exceeds the limit of maxListeners, and I get the warning
to increase the max event listeners limit by using setMaxListeners()
even if only 1 user is using the front end app.
*/
events.on('newOrder', sendSSE);
});
//...Code...
// Client Side
const newOrderReceived = (e) => {
console.log(JSON.parse(e.data));
};
if ( !window.eventSource ) {
window.eventSource = new EventSource('https://example.com/sse');
window.eventSource.onmessage = newOrderReceived;
}
但问题是即使只有 1 个用户在使用该应用程序,maxListeners 也会以非常快的速度耗尽。
如果我将事件绑定代码更改为
events.once('newOrder', sendSSE);
事件 maxListeners 错误消失,但它不会在第一次订购后通知我的应用程序。
我无法找到将事件绑定到 /sse
路由之外的方法,因为我需要通过
发送数据
res.write(`data:${dataToSend}`);
res.write("\n\n");
并且 res
对象仅在我的情况下的路线内可用 /sse
。
这个问题的解决方案是什么,或者是否有更好的方法可以在 NodeJS 中发送服务器发送的事件 (SSE) 来通知我的前端应用程序,每次我在 API 上收到请求终点?
如有任何帮助,我们将不胜感激。
P.S:我在搜索这个问题时看到的所有tutorials/guides都是在发送数据的路由中实现了一个setInterval,我没有查找说明如何发送数据以响应服务器上的事件的教程。
这是我解决这个问题的方法
// Events.js File
const EventEmitter = require('events');
const events = new EventEmitter();
events.setMaxListeners(parseInt(config.MAX_EVENT_LISTENERS));
events.on('error', (err) => {
// handle Error
});
// event to fire on each new order.
events.on('newOrder', (data) => {
// and in the events.js file, I access it from the global scope of Node
// so this is the `res` object from the route where I want to send SSE.
if ( global.sseResponse ) {
const sendSSE = sse(global.sseResponse);
sendSSE(data);
} else {
// no sse listener, do something else,
}
});
module.exports = events;
在我的路线文件中
// My routes file where I want to use this
router.route('/sse')
.get(
(req, res) => {
res.writeHead(200, {
"Connection": "keep-alive",
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
});
res.write("\n");
// Node has a global variable as browsers have a window variable, I attached the res object of my route to
// the Node's global object to access it outside of this route
global.sseResponse = res;
const sendSSE = ApiController.sse(res);
// keep the connection on
setInterval(() => {
sendSSE({ message: 'keep-connection-alive' });
}, 5000);
});
这是我用来发送 sse 的函数,你可以把它写在任何你想要的地方,然后从那里导出它以在多个地方使用
// function to send server sent events (sse)
const sse = res => data => {
const dataToSend = JSON.stringify(data);
res.write(`data:${dataToSend}`);
res.write("\n\n");
// this is the important part if using the compression npm module
res.flush();
},
使用这种方法解决了我的问题,希望对其他人也有帮助。
我正在尝试实现一种方法,每次我的服务器端点被命中时,都会在 NodeJs 中发送服务器发送事件 (SSE)。
我是这样实现的
//...code...
import EventSource from "eventsource";
const EventEmitter = require('events');
const events = new EventEmitter();
// events.setMaxListeners(10);
// api end point route from where the new order requests will be coming
router.route('/new-order')
.post((req, res) => {
const orderData = req.body.data;
//... save order and emit an event to send response to the client react app
events.emit('newOrder', orderData);
});
// define a function to send SSE events to the client
const sse = res => data => {
const dataToSend = JSON.stringify(data);
res.write(`data:${dataToSend}`);
res.write("\n\n");
};
// define an EventSource route for the client to be connected for new events
router.route('/sse')
.get((req, res) => {
res.writeHead(200, {
"Connection": "keep-alive",
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
});
res.write("\n");
const sendSSE = sse(res);
/*
PROBLEMATIC CODE-- EACH request to /sse route adds new event listener,
and in minutes, it exceeds the limit of maxListeners, and I get the warning
to increase the max event listeners limit by using setMaxListeners()
even if only 1 user is using the front end app.
*/
events.on('newOrder', sendSSE);
});
//...Code...
// Client Side
const newOrderReceived = (e) => {
console.log(JSON.parse(e.data));
};
if ( !window.eventSource ) {
window.eventSource = new EventSource('https://example.com/sse');
window.eventSource.onmessage = newOrderReceived;
}
但问题是即使只有 1 个用户在使用该应用程序,maxListeners 也会以非常快的速度耗尽。
如果我将事件绑定代码更改为
events.once('newOrder', sendSSE);
事件 maxListeners 错误消失,但它不会在第一次订购后通知我的应用程序。
我无法找到将事件绑定到 /sse
路由之外的方法,因为我需要通过
res.write(`data:${dataToSend}`);
res.write("\n\n");
并且 res
对象仅在我的情况下的路线内可用 /sse
。
这个问题的解决方案是什么,或者是否有更好的方法可以在 NodeJS 中发送服务器发送的事件 (SSE) 来通知我的前端应用程序,每次我在 API 上收到请求终点?
如有任何帮助,我们将不胜感激。
P.S:我在搜索这个问题时看到的所有tutorials/guides都是在发送数据的路由中实现了一个setInterval,我没有查找说明如何发送数据以响应服务器上的事件的教程。
这是我解决这个问题的方法
// Events.js File
const EventEmitter = require('events');
const events = new EventEmitter();
events.setMaxListeners(parseInt(config.MAX_EVENT_LISTENERS));
events.on('error', (err) => {
// handle Error
});
// event to fire on each new order.
events.on('newOrder', (data) => {
// and in the events.js file, I access it from the global scope of Node
// so this is the `res` object from the route where I want to send SSE.
if ( global.sseResponse ) {
const sendSSE = sse(global.sseResponse);
sendSSE(data);
} else {
// no sse listener, do something else,
}
});
module.exports = events;
在我的路线文件中
// My routes file where I want to use this
router.route('/sse')
.get(
(req, res) => {
res.writeHead(200, {
"Connection": "keep-alive",
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
});
res.write("\n");
// Node has a global variable as browsers have a window variable, I attached the res object of my route to
// the Node's global object to access it outside of this route
global.sseResponse = res;
const sendSSE = ApiController.sse(res);
// keep the connection on
setInterval(() => {
sendSSE({ message: 'keep-connection-alive' });
}, 5000);
});
这是我用来发送 sse 的函数,你可以把它写在任何你想要的地方,然后从那里导出它以在多个地方使用
// function to send server sent events (sse)
const sse = res => data => {
const dataToSend = JSON.stringify(data);
res.write(`data:${dataToSend}`);
res.write("\n\n");
// this is the important part if using the compression npm module
res.flush();
},
使用这种方法解决了我的问题,希望对其他人也有帮助。