【问题标题】:Avoid passing around request object避免传递请求对象
【发布时间】:2015-03-18 19:04:24
【问题描述】:

我正在编写一个 React 应用程序(Express 服务器),它使用共享(“同构”)代码库在客户端和服务器上呈现。在某些情况下,当服务器是渲染器时,我想访问请求对象,而不必到处传递它。由于节点一次响应一个请求,利用缓存变量是否安全,确保在发送响应之前重置它?

这可行,但有人能告诉我为什么这是一个好主意或坏主意吗?...

server.js

var r = require('./request-store');
app.use(function(req, res, next) {
  r.set(req);
  next();
});

var resolve = require('./resolve');
// ...deferring to react router for client / server routing
app.use(function(req, res) {
  resolve().then(function(data) {
    r.set(null);
    res.render('index', { data: data });
  });
});

request-store.js

var r = null;
module.exports.set = function(req) { r = req; };
module.exports.get = function() { return r; };

resolve.js

var r = require('./request-store');
module.exports = function() {
  return new Promise(resolve, reject) {
    if (isServer) {
      console.log(r.get());
      resolve();
    } else if (isClient) {
      resolve();
    }
  }
};

【问题讨论】:

    标签: javascript node.js express reactjs react-router


    【解决方案1】:

    但是,如果您同时收到来自不同用户的两个请求,会发生什么情况?第一个请求将被缓存,第二个将覆盖第一个用户的请求。同时假设您正在执行一些 IO 并且需要在完成后访问请求,那么第一个用户的请求对象将已经被第二个用户的请求覆盖。总的来说,这样做是非常危险的。最好的方法是使用某种内存键值存储,但您仍然需要以某种方式传递键。

    【讨论】:

    • 如果是这样的话,那是行不通的。那么 express 是否有可能在发出之前的 res 之前处理另一个 req 呢?
    • 当然,假设您有一个数据库查询需要几秒钟来处理,因为节点是异步的,所以该数据库操作不会阻止后续请求的执行。
    • 好吧,我试验的方式是在 Chrome 中打开多个标签,显然它会等待向同一 URL 发出下一个请求,直到第一个返回。这就是为什么我很困惑。使用多个浏览器 + apache bench 测试这个我清楚地看到了这个问题。谢谢。
    • 抱歉,这是错误的,Node 不是异步的,它是 100% 同步的 - 只有 Node 中的 I/O 操作是异步的(或非阻塞的)。在其他中间件运行之前,不可能覆盖请求除非之间存在 I/O 操作。不管是来自不同客户端的多个请求,V8引擎都会将请求排队并按顺序处理。
    • 是的,这是真的,但无关紧要。重要的是您以异步方式在节点应用程序中编写 IO,并且每个请求都在同一个线程中处理,因此共享内存。这是来自其他平台的新开发人员面临的两个最大难题。我可以修改我的评论说“因为节点的 IO 操作是异步的”,我的观点仍然成立。
    【解决方案2】:

    鉴于 Node 是单线程的,并且您似乎不会执行任何 I/O,那么是的,您的代码对我来说看起来是安全的。 Express 将同步运行所有中间件,因此在您的 Promise 运行时(因为它也是同步的)r.get() 应该返回正确的请求对象。

    但是,我不确定您是否需要缓存它,您可以将其直接传递到 resolve 并使用闭包将其捕获到您的 Promise 中

    var resolve = require('./resolve');
    // ...deferring to react router for client / server routing
    app.use(function(req, res) {
        // pass 'req' into `resolve`
        resolve(req).then(function(data) {
            r.set(null);
            res.render('index', { data: data });
        });
    });
    

    resolve.js

    var r = require('./request-store');
    module.exports = function(req) {
      // capture 'req' for use later
      return new Promise(resolve, reject) {
        if (isServer) {
          console.log(req);
          resolve();
        } else if (isClient) {
          resolve();
        }
      }
    };
    

    【讨论】:

    • nvm 我在尝试这个 w apache bench + concurrency 时看到了一个问题
    • @ndreckshage 看到了什么问题?
    • 多个请求同时发生。在 resolve.js 中尝试了 setTimeout(resolve, 5000),控制台记录了请求 # + 响应 #。混乱,因为我一次在 10 个选项卡中点击 chrome 页面,并且请求/响应按顺序记录(req、res、req、res、req、res)。使用 apache bench + concurrency 进行测试,我看到所有请求都记录在一起(req、req、req、res、res、res)。
    • 那是因为 setTimeout 使解析调用异步(与上面的示例不同)。如果您在我的回答中运行了关闭建议,尽管它应该给您正确的请求,即使有 setTimeout?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-01
    • 1970-01-01
    • 2011-11-25
    • 2014-12-22
    • 2018-10-06
    • 2012-10-27
    相关资源
    最近更新 更多