简体   繁体   中英

Use express as proxy for websocket

I have a data provider which gives me stock prices via TCP connection. The data provider only allows a static IP to connect to their service.

But since I need to format the data before sending it to my front-end I want to use my express back-end as a proxy.

What that means is:

  • I need to connect my back-end to my data provider via websocket(socket.io) in order to get the data (back-end acts as client)
  • I need my back-end to broadcast this received data to my front-end(back-end acts as server)

My question is: Is that possible at all? Is there an easier way to achieve this? Is there a documentation on how to use an express app as websocket server and client at once?


EDIT:

I got this working now. But my current solution kills my AWS EC2 instance because of huge CPU usage. This is how I've implemented it:

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);

Does anyone have an idea why this causes a huge CPU load? I've tried to profile my code with clinicjs but I couldn't find a apparent problem.


EDIT2: To be more specific: My data provider provides my with stock quotes. So every time a quote changes, I get new data. I then parse this data and emit it via io.emit . Could this be some kind of bottleneck?

This is the profile I get after I run clinicjs :

在此处输入图像描述

I don't know how many resources you have on your AWS, but 1,000 clients shouldn't be a problem.

I have personally encountered 2 bottlenecks:

  1. Clients connected with Ajax, not WS (It used to be a common problem with old socket.io )
  2. The socket.io libraries were served by Node, not Nginx / Apache. Node is poor at keeping-alive management.

Check also:

  1. How often do you get data from some.ip ? Good idea is aggregate and filter it.
  2. Do you need to notify all customers of everything? Is it enough just to inform interested? (Live zone)
  3. Maybe it is worth moving the serving to serviceWorker.js or Push Events?

As part of the experiment, log yourself events. Receiving data, connecting and disconnecting the client. Observe the server logs.

As part of the debugging process, log events. Receiving data, connecting and disconnecting the client. Observe the server logs.

Or maybe this code is not responsible for the problems, but the data download for the first view. Do you have data in the buffer, or do you read for GET index.html ?

To understand what was going on with your situation, I created an elementary TCP server that published JSON messages every 1ms to each client that connects to it. Here is the code for the server:

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');

As you see, we set up a setInterval function that will emit a simple JSON message to each connected client every 1 ms.

For the client, I used something very similar to what you have. At first, I tried pushing every message received by the server to the browser to the WebSocket connection. In my case, it also pushed the CPU to 100%. I don't know exactly why.

Nonetheless, even though your data is being updated every 1 ms, it is doubtful that you need to refresh your webpage at that rate. Most websites work at 60 fps. That would mean updating the data every 16ms. So, a straightforward solution would be to batch the data and send it to the browser every 16 ms. Just this modification greatly increases performance. You can go even further by extending the batch time or filtering some of the sent data.

Here is the code for the client, taking advantage of batch messages. Bear in mind that this is a very naive implementation made to show the idea. A better adjustment would be to work the streams with libraries like 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');
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM