响应中的不稳定行为 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>
);
}
}
我有一个 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>
);
}
}