Можно ли сделать асинхронный renderToString с worker_threads? - PullRequest
4 голосов
/ 25 апреля 2019

Я думал о том, как оптимизировать реагирующий серверный рендеринг, особенно ту часть моего приложения, которая является узким местом, а именно синхронный вызов renterToString. Поскольку это блокирующий вызов, длительные вызовы функции renderToString приводят к значительному снижению пропускной способности моего приложения.

Я наткнулся на поток (здесь он переведен с китайского на английский с google translate), который выдвигает гипотетическую гипотезу использования worker_threads для разгрузки renderToString и его асинхронности.

https://translate.google.com/translate?hl=en&sl=zh-CN&u=https://cnodejs.org/topic/5b2e596557137f22415c4e63&prev=search

Я хотел проверить эту функциональность, и похоже, что существует блокировщик, в котором символ не может быть передан в качестве данных работнику, поскольку его нельзя клонировать:

DataCloneError: Symbol(react.element) could not be cloned.

Вот пример кода для настройки логики рендеринга рабочего потока / сервера:

// asyncRenderer.js
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');

if (isMainThread) {
  module.exports = async function asyncRenderToString(component) {
    return new Promise((resolve, reject) => {
      const worker = new Worker(__filename, {
        workerData: component,
      });
      worker.on('message', resolve);
      worker.on('error', reject);
      worker.on('exit', (code) => {
        if (code !== 0) {
          reject(new Error(`Worker stopped with exit code ${code}`));
        }
      });
    });
  };
} else {
  const { renderToString } = require('react-dom/server');
  const HTML = renderToString(workerData);
  parentPort.postMessage(HTML);
}


// server.jsx
const asyncRenderer = require('./asyncRenderer');
...
const html = await renderToString(<App/>);
// Return the html

Я бы хотел услышать, возможно ли использовать для этой цели worker_threads, и если да, то каким будет наилучший подход к настройке потоков рендерера / рабочего.

1 Ответ

2 голосов
/ 25 апреля 2019

Я бы хотел услышать, возможно ли использовать для этой цели worker_threads, и если да, то каким будет наилучший подход к настройке потоков рендерера / работника.

Прежде всего в целом worker_threads подходит для работы с интенсивным использованием процессора (например, такой), где полезны параллелизм и общая память.

В этом конкретном случае одинаково эффективно будет просто вращать несколько процессов Node.js.

Более того, поскольку ReactDOMServer рендерится довольно медленно - вам почти всегда приходится помещать кэш перед рендерингом на стороне сервера в React (или Vue или Angular). Так что это гораздо более простой подход к повышению производительности.

Что касается того, как бы вы использовали здесь worker_threads, то оно не работает, потому что вы передаете ему компонент. Вместо этого я бы передал ему имя маршрута или компонента и вместо него require этот компонент.

В качестве альтернативы, вы можете сериализовать компонент самостоятельно и десерилизовать его на другой стороне. Символы требуют уникальности, поэтому мы не клонируем их по умолчанию при перемещении между рабочими в Node.js. Это ошибка, которую вы видите.

...