Запустите веб-сокет в веб-воркере или сервис-воркере - javascript

У меня есть 9 подключений к веб-сокетам с разных сайтов, которые работают над обновлением DOM данными. В настоящее время я подключаюсь ко всем и слушаю все веб-сокеты и обновляю данные с помощью вызова функции.

Проблема, с которой я сталкиваюсь, заключается в том, что существует много соединений через веб-сокеты, а также проблемы с использованием памяти и ЦП. Как я могу использовать как сервис-воркеров, так и веб-воркеров для оптимизации такого количества подключений к веб-сокетам?

async function appendGatePublicTickersData(e) {
  if (e.event == "update" && e.result[0].contract == "BTC_USD") {
    if ('total_size' in e.result[0]) {
      $(".gate-btc-open-interest").html(commaNumber(e.result[0].total_size))
      if ('last' in e.result[0]) {
        $(".gate-btc-open-value").html(commaNumber(customFixedRounding((e.result[0].total_size / e.result[0].last), 4)))
      }
    }

    if ('volume_24h_usd' in e.result[0]) {
      $(".gate-btc-24-volume").html(commaNumber(e.result[0].volume_24h_usd))
    }

    if ('volume_24h_btc' in e.result[0]) {
      $(".gate-btc-24-turnover").html(commaNumber(e.result[0].volume_24h_btc))
    }

    if ('funding_rate' in e.result[0]) {
      var fundingRateBtcGate = customFixedRounding(e.result[0].funding_rate * 100, 4)
      $(".public-gate-btc-funding").html(fundingRateBtcGate)
    }

    if ('funding_rate_indicative' in e.result[0]) {
      var predictedRateBtcGate = customFixedRounding(e.result[0].funding_rate_indicative * 100, 4)
      $(".public-gate-btc-predicted").html(predictedRateBtcGate)
    }
  }
}

var pubGateWs = new WebSocket("wss://fx-ws.gateio.ws/v4/ws/btc");

pubGateWs.addEventListener("open", function() {
  pubGateWs.send(JSON.stringify({
    "time": 123456,
    "channel": "futures.tickers",
    "event": "subscribe",
    "payload": ["BTC_USD", "ETH_USD"]
  }))
});

pubGateWs.addEventListener("message", function(e) {
  e = JSON.parse(e.data)
  appendGatePublicTickersData(e)
});

pubGateWs.addEventListener("close", function() {});

person Pavneet Singh    schedule 18.05.2020    source источник
comment
Перемещение части отправки и получения данных в Web Worker может улучшить производительность с этой стороны. Является ли узкое место в передаче данных или в обновлении DOM?   -  person Emiel Zuurbier    schedule 18.05.2020
comment
Я не уверен, но когда я подключаюсь ко всем веб-сокетам, использование ЦП и ОЗУ значительно увеличивается. Как переместить веб-сокет в Web Worker?   -  person Pavneet Singh    schedule 18.05.2020


Ответы (1)


Поскольку вы используете веб-сокеты, было бы неплохо использовать SharedWorker< /a>, чтобы создать новый поток для ваших веб-сокетов. Разница между обычным WebWorker и SharedWorker заключается в том, что веб-воркер будет создавать новый сеанс на каждой вкладке или в браузере при загрузке страницы, тогда как общий воркер будет использовать один и тот же сеанс на каждой вкладке. Таким образом, все ваши вкладки или окна будут иметь один и тот же рабочий процесс и одно и то же подключение к веб-сокету для работы.

Если данные обновляются очень часто (более 60 раз в секунду) и DOM необходимо обновлять каждый раз, используйте requestAnimationFrame, чтобы регулировать количество обновлений DOM. Он будет ждать следующего цикла перерисовки, прежде чем обновлять DOM новым содержимым, что составляет около 60 раз в секунду или 60 кадров в секунду.

Реализация этого хотела бы пример ниже:

Основная нить.

// Create shared worker.
const webSocketWorker = new SharedWorker('web-sockets-worker.js');

/**
 * Sends a message to the worker and passes that to the Web Socket.
 * @param {any} message 
 */
const sendMessageToSocket = message => {
  webSocketWorker.port.postMessage({ 
    action: 'send', 
    value: message,
  });
};

// Event to listen for incoming data from the worker and update the DOM.
webSocketWorker.port.addEventListener('message', ({ data }) => {
  requestAnimationFrame(() => {
    appendGatePublicTickersData(data);
  });
});
  
// Initialize the port connection.
webSocketWorker.port.start();

// Remove the current worker port from the connected ports list.
// This way your connectedPorts list stays true to the actual connected ports, 
// as they array won't get automatically updated when a port is disconnected.
window.addEventListener('beforeunload', () => {
  webSocketWorker.port.postMessage({ 
    action: 'unload', 
    value: null,
  });

  webSocketWorker.port.close();
});

Веб работник.

/**
 * Array to store all the connected ports in.
 */
const connectedPorts = [];

// Create socket instance.
const socket = new WebSocket("wss://fx-ws.gateio.ws/v4/ws/btc");

// Send initial package on open.
socket.addEventListener('open', () => {
  const package = JSON.stringify({
    "time": 123456,
    "channel": "futures.tickers",
    "event": "subscribe",
    "payload": ["BTC_USD", "ETH_USD"]
  });

  socket.send(package);
});

// Send data from socket to all open tabs.
socket.addEventListener('message', ({ data }) => {
  const package = JSON.parse(data);
  connectedPorts.forEach(port => port.postMessage(package));
});

/**
 * When a new thread is connected to the shared worker,
 * start listening for messages from the new thread.
 */
self.addEventListener('connect', ({ ports }) => {
  const port = ports[0];

  // Add this new port to the list of connected ports.
  connectedPorts.push(port);

  /**
   * Receive data from main thread and determine which
   * actions it should take based on the received data.
   */
  port.addEventListener('message', ({ data }) => {
    const { action, value } = data;

    // Send message to socket.
    if (action === 'send') {
      socket.send(JSON.stringify(value));

    // Remove port from connected ports list.
    } else if (action === 'unload') {
      const index = connectedPorts.indexOf(port);
      connectedPorts.splice(index, 1);
    }
  });

  // Start the port broadcasting.
  port.start();
});

Примечание: ваш appendGatePublicTickersData не использует ключевое слово await, поэтому он не обязательно должен быть функцией async.

person Emiel Zuurbier    schedule 18.05.2020
comment
Это прекрасно работает. Спасибо. Как добавить новое подключение к веб-сокету и прослушиватель к тому же коду? Должен ли я создавать нового общего рабочего для второго подключения или я должен слушать его на другом порту? - person Pavneet Singh; 18.05.2020
comment
Ну, это зависит от вас. Вы можете установить несколько WS-соединений в одном и том же воркере, распределить их по нескольким воркерам или даже позволить вашему общему воркеру создать больше воркеров, которые будут направлять все полученные данные одному общему воркеру, который, в свою очередь, отправит их обратно в основной поток. из одной точки. Но все должно быть в пользу производительности. Только вы можете узнать, что работает достаточно хорошо для вашего случая. - person Emiel Zuurbier; 18.05.2020
comment
Описание не соответствует коду. Несмотря на то, что вы используете общий рабочий процесс, вы создаете новый WebSocket в событии connect, поэтому для каждой вкладки браузера используется один WebSocket, а не один для источника. Чтобы сделать это для каждого источника, вам нужно запустить WebSocket вне события connect, а затем поддерживать массив подключенных портов и вызывать postMessage для каждого из них. - person whitehat101; 02.01.2021
comment
@whitehat101 вы абсолютно правы. Я изменил ответ, чтобы включить предложенные вами методы. Это должно использовать одно WS-соединение для всех вкладок в одном домене. - person Emiel Zuurbier; 03.01.2021