服务器端渲染的React应用程序因加载而崩溃

Geo*_*nov 9 node.js express reactjs server-side-rendering ssr

我正在使用react-boilerplate(使用react-router,sagas,express.js)作为我的React应用程序,并且在它之上我添加了SSR逻辑,这样一旦它收到HTTP请求,它就会根据URL呈现反应组件到字符串并发送HTML字符串返回客户端.

虽然反应呈现在服务器端发生,但它也fetch通过传真向一些API(基于URL的最多5个端点)发出请求,以便在组件实际呈现组件之前获取组件的数据.

如果我同时向Node服务器发出几个请求,那么一切都很好,但是一旦我模拟了100多个并发请求的加载并且它开始处理它,那么在某些时候它会崩溃而没有任何异常的迹象.

我在尝试调试应用程序时注意到的是,一旦节点服务器开始处理100多个传入请求,它就会同时向API发送请求,但在停止堆叠这些请求之前不会收到任何实际响应.

用于在服务器端呈现的代码:

async function renderHtmlDocument({ store, renderProps, sagasDone, assets, webpackDllNames }) {
  // 1st render phase - triggers the sagas
  renderAppToString(store, renderProps);

  // send signal to sagas that we're done
  store.dispatch(END);

  // wait for all tasks to finish
  await sagasDone();

  // capture the state after the first render
  const state = store.getState().toJS();

  // prepare style sheet to collect generated css
  const styleSheet = new ServerStyleSheet();

  // 2nd render phase - the sagas triggered in the first phase are resolved by now
  const appMarkup = renderAppToString(store, renderProps, styleSheet);

  // capture the generated css
  const css = styleSheet.getStyleElement();

  const doc = renderToStaticMarkup(
    <HtmlDocument
      appMarkup={appMarkup}
      lang={state.language.locale}
      state={state}
      head={Helmet.rewind()}
      assets={assets}
      css={css}
      webpackDllNames={webpackDllNames}
    />
  );
  return `<!DOCTYPE html>\n${doc}`;
}

// The code that's executed by express.js for each request
function renderAppToStringAtLocation(url, { webpackDllNames = [], assets, lang }, callback) {
  const memHistory = createMemoryHistory(url);
  const store = createStore({}, memHistory);

  syncHistoryWithStore(memHistory, store);

  const routes = createRoutes(store);

  const sagasDone = monitorSagas(store);

  store.dispatch(changeLocale(lang));
  
  match({ routes, location: url }, (error, redirectLocation, renderProps) => {
    if (error) {
      callback({ error });
    } else if (renderProps) {
      renderHtmlDocument({ store, renderProps, sagasDone, assets, webpackDllNames })
        .then((html) => {
          callback({ html });
        })
        .catch((e) => callback({ error: e }));
    } else {
      callback({ error: new Error('Unknown error') });
    }
  });
}
Run Code Online (Sandbox Code Playgroud)


所以我的假设是,一旦收到太多的HTTP请求就会出现问题,这反过来又会产生更多的API端点请求以呈现反应组件.

我注意到它renderAppToString()为每个客户端请求后阻塞事件循环300ms ,所以一旦有100个并发请求,它会阻塞它大约10秒钟.我不确定这是不是正常或坏事.

是否值得尝试限制对节点服务器的同时请求?

我找不到有关SSR + Node崩溃主题的更多信息.因此,如果有人在过去经历过类似的问题,我会感谢任何关于在哪里查看以确定问题或可能的解决方案的建议.

moo*_*ard 2

对 的调用renderToString()是同步的,因此它们在运行时会阻塞线程。因此,当您有 100 多个并发请求时,您的队列将极度阻塞,挂起大约 10 秒,这并不奇怪。

编辑:有人指出,React v16 本身支持流式传输,但您需要使用将renderToNodeStream()HTML 流式传输到客户端的方法。它应该返回与流式传输完全相同的字符串renderToString(),因此您不必等待完整的 HTML 呈现后才开始向客户端发送数据。

  • 由于 React v16 `renderToString` 支持流式传输,因此无需使用外部库。https://reactjs.org/blog/2017/09/26/react-v16.0.html#better-server-side-rendering (2认同)