响应中的不稳定行为 express + next + 对并发请求做出反应

Erratic behaviour in response express + next + react with concurrent requests

我有一个 Node 应用程序 运行 ExpressJS、Next 和 React,这个应用程序根据收到的 header 动态加载不同的布局。这个 header 是由 Nginx 在虚拟主机中设置的。所以想象一下 3 个不同的域必须加载同一个应用程序,但是每个虚拟主机在这个 header 中都有不同的值,所以这是应用程序读取的内容和 returns 正确的布局。

此方法按预期工作,直到此 header 中具有不同值的 2 个请求同时到达。当发生这种情况时,其中一个的响应有时可能会完全或部分与另一个重叠。

我知道 app.local 和 request.local 的功能可以隔离 request/response 但是在使用它之后,它并没有解决我的问题,或者也许我是错误的使用方式。

在此link您可以看到该应用程序的软件架构概念(已简化)。如果您想重现该错误,则必须使用此 header 设置 2 个具有不同值的虚拟主机,并同时加载两个域:

https://codesandbox.io/s/zmoq4q3mp

如有任何帮助,我们将不胜感激。

非常感谢。

问题很简单,您将当前模板分配给 config.layoutTpl,它是整个应用程序共享的单例,它不是该请求唯一的。

所以基本上,所有请求都在修改同一个 config 对象,这就是您遇到问题的原因。使用并发时,不能使用 "globals",否则会出现此类问题。

您必须将数据从 _app.js 传递到 _document.js,或者直接在 _document.js 中获取模板,这是您现在唯一使用它的地方。

下面的方法会起作用,尽管接下来可能有更好的方法来传递数据。

您需要做的不是将模板分配给 config,而是将其分配给每个请求都是唯一的 ctx

_app.js

export default withRedux(initStore)(
  class MyApp extends App {
    static async getInitialProps({ Component, ctx }) {

      let layoutTpl = null;
      if (ctx.req && !process.browser) {
        const { layouttpl } = ctx.req.headers;
        layoutTpl = layouttpl;
      }

      // Assign the current template, to the context
      // This is unique for this specific request
      ctx.layoutTpl = layoutTpl || config.layoutTpl;

      return {
        pageProps: Component.getInitialProps
          ? await Component.getInitialProps(ctx)
          : {}
      };
    }

    /* ... */
}

_document.js

// import config from "../config";

// I'm passing the template directly, instead of the config object here
const varJs = layoutTpl => `
var layoutTpl = "${layoutTpl}";
`;

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {

    const initialProps = await Document.getInitialProps(ctx);
    // get the value from `ctx` and use it in `render`
    return { ...initialProps, layoutTpl: ctx.layoutTpl };
  }

  render() {
    const { layoutTpl } = this.props;
    // Get the layoutTpl which is unique to this request
    return (
      <html>
        <Head>
          <base href="/" />
          <meta
            name="viewport"
            content="width=device-width,initial-scale=1,maximum-scale=3,minimal-ui"
          />
          <script dangerouslySetInnerHTML={{ __html: varJs(layoutTpl) }} />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}