使用Express作为WebSocket的代理

3
我有一个通过 TCP 连接提供股票价格的数据提供商。该数据提供商仅允许静态 IP 连接其服务。
但是,由于我需要在将数据发送到前端之前对其进行格式化,因此我想使用我的 Express 后端作为代理。
这意味着:
- 我需要通过 WebSocket (socket.io) 将后端连接到我的数据提供商以获取数据(后端作为客户端)。 - 我需要使我的后端广播接收到的数据给前端(后端作为服务器)。
我的问题是:这是否可能?是否有更简单的方法来实现这一点?是否有文档说明如何同时将 express 应用程序用作 WebSocket 服务器和客户端?
编辑:
我现在已经解决了这个问题。但是,我的当前解决方案会因为巨大的 CPU 使用率而使我的 AWS EC2 实例死亡。这是我实现它的方式:
const net = require('net');
const app = require('express')();
const httpServer = require('http').createServer(app);

const client = new net.Socket();

const options = {
  cors: {
    origin: 'http://someorigin.org',
  },
};

const io = require('socket.io')(httpServer, options);

client.connect(1337, 'some.ip', () => {
  console.info('Connected to some.ip');
});

client.on('data', async (data) => {
  // parse data
  const parsedData = {
    identifier: data.identifier,
    someData: data.someData,
  };

  // broadcast data
  io.emit('randomEmitString', parsedData);
});

client.on('close', () => {
  console.info('Connection closed');
});

httpServer.listen(8081);

有没有人知道为什么这会导致巨大的CPU负载?我尝试使用clinicjs分析我的代码,但我找不到明显的问题。


编辑2:更具体地说:我的数据提供程序提供股票报价。因此,每当报价变化时,我都会获得新数据。然后,我解析这些数据并通过io.emit发出它。这可能会导致某种瓶颈吗?

这是我运行clinicjs后得到的剖面:

enter image description here


当然。您可以使用 io.emit() 在 Socket.io 中向所有连接的客户端广播消息(其中 io 是已初始化的 Socket.io 服务器实例)。只需在 Socket.io 服务器启动后连接到数据提供程序即可。 - cbr
你确定想要一个(低级)socket连接到你的数据提供者,而不是一个socket.io socket、一个ws socket或者一个http连接吗? - Christian Fritz
1
注意:socket.io不是简单的websocket客户端/服务器。 - Marc
@SauravKumar 0. 我在本地运行它,没有连接到我的前端。 - Ic3m4n
谢谢。从 some.ip 发送的数据频率是多少?如果您也告诉我们接收到的数据大小,那就更好了。 - Saurav Kumar
显示剩余2条评论
2个回答

0
为了理解您的情况,我创建了一个基本的TCP服务器,每1毫秒向连接到它的每个客户端发布JSON消息。以下是服务器的代码:
var net = require('net');

var server = net.createServer(function(socket) {
    socket.pipe(socket);
});

server.maxConnections = 10

server.on('close', ()     => console.log('server closed'))
server.on('error', (err)  => console.error(err))
server.on('listening', () => console.log('server is listening'))
server.on('connection', (socket) => {
  console.log('- client connected')
  socket.setEncoding('utf8')

  var intervalId = setInterval(() => socket.readyState === "open" && 
    socket.write(JSON.stringify({
      id: intervalId,
      timestamp: Date.now(),
    }) + '\n'), 1)

  socket.on('error'  , (err) => console.error(err))
  socket.on('close'  , ()    => {
    clearInterval(intervalId)
    console.log('- client closed the connection')
  })
})

server.listen(1337, '0.0.0.0');

如您所见,我们设置了一个setInterval函数,每1毫秒向每个连接的客户端发送一个简单的JSON消息。

对于客户端,我使用了与您类似的东西。起初,我尝试将服务器接收到的每条消息推送到浏览器的WebSocket连接中。在我的情况下,它也将CPU推到了100%。我不知道确切的原因。

尽管您的数据每1毫秒更新一次,但很难相信您需要以那种速率刷新网页。大多数网站以60 fps工作。这意味着每16ms更新一次数据。因此,一个简单的解决方案是批处理数据并在每16ms将其发送到浏览器。仅此修改就大大提高了性能。您甚至可以通过延长批处理时间或过滤一些已发送的数据来进一步优化。

这是利用批处理消息的客户端代码。请记住,这是一个非常天真的实现,只是为了展示这个想法。更好的调整是使用像RxJS这样的库来处理流。

// tcp-client.js
const express    = require('express');
const http       = require('http');
const { Server } = require("socket.io");
const net        = require('net')

const app    = express();
const server = http.createServer(app);
const io     = new Server(server);
const client = new net.Socket()

app.get('/', (req, res) => {
  res.setHeader('content-type', 'text/html')
  res.send(`
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>TCP - Client</title>
</head>
<body>
  <script src="/socket.io/socket.io.js"></script>
  <script>
    var socket = io();
    socket.on('msg', (msg) => document.body.textContent = msg);
  </script>
</body>
</html>
`);
});

io.on('connection', (socket) => {
  console.log('- user connected');
  socket.on('disconnect', () => {
    console.log('- user disconnected');
  });
});

var buffer = []

setInterval(() => {
  io.emit("msg", JSON.stringify(buffer))
  buffer = []
}, 16)

client.connect(1337, '127.0.0.1', function() {
    console.log('- connected to server');
});

client.on('data', function(data) {
  buffer.push(data.toString("utf8"))
});

client.on('close', function() {
    console.log('- connection to server closed');
});

server.listen(3000, () => {
  console.log('listening on 0.0.0.0:3000');
});

0

我不知道您在AWS上有多少资源,但1000个客户不应该是问题。

我个人遇到了2个瓶颈:

  1. 客户端使用Ajax连接而不是WS(这曾经是旧版socket.io的常见问题)
  2. socket.io库由Node而非Nginx / Apache提供服务。 Node不擅长保持活动管理。

还要检查:

  1. 你多久从some.ip获取一次数据?聚合和过滤数据是个好主意。
  2. 你需要通知所有客户有关所有事情吗?仅通知感兴趣的人是否足够?(现场区域)
  3. 也许将服务移至serviceWorker.js或Push Events值得考虑?

作为实验的一部分,记录事件。接收数据,连接和断开客户端。观察服务器日志。

作为调试过程的一部分,记录事件。接收数据,连接和断开客户端。观察服务器日志。

或许这段代码并不是导致问题的原因,而是首次视图数据下载。您的缓冲区里有数据吗?还是直接读取 GET index.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接