节点服务器异步调用后端服务

Node server async call to backend service

我是 Node 新手,正在编写我的第一个节点服务器。它应该在调用后端休息服务后用一个简单的页面响应一个简单的获取请求。

我正在使用 express 来管理请求,并使用 axios 包来发出后端请求。问题是服务器阻塞了事件循环,我无法理解如何异步调用后端。

截至目前,前端服务器一次只能管理一个请求!!我预计如果后端服务每次都需要 10 秒来响应,那么前端服务器可以在 10 秒而不是 20 秒内响应两个并发请求。

我哪里错了?

这是前端节点代码的摘录:

app.get('/', function(req, res) {

      //Making the call to the backend service. This should be asynchronous...
      axios.post(env.get("BACKEND_SERVICE"), 
      { "user": "some kind of input"})
        .then(function(response){

        //do somenthing with the data returned from the backend...

        res.render('homepage');
        })
    }

这是后端节点代码的摘录:

app.post('/api/getTypes', jsonParser, function (req, res) {

      console.log("> API request for 'api/getTypes' SLEEP");
      var now = new Date().getTime();
      while(new Date().getTime() < now + 10000){ /* do nothing */ }
      console.log("> API request for 'api/getTypes' WAKE-UP");

      res.json({"types":"1"});
    }

问题是您的忙等待占用了后端服务器,以至于它甚至无法开始处理第二个请求。

我假设您正在尝试模拟获取类型需要一段时间的过程。赔率是你要做什么来获得类型将是异步的和 I/O-bound (读取文件,查询数据库等)。要模拟它,只需使用 setTimeout:

app.post('/api/getTypes', jsonParser, function (req, res) {

  console.log("> API request for 'api/getTypes' SLEEP");
  setTimeout(function() {
      console.log("> API request for 'api/getTypes' WAKE-UP");
      res.json({"types":"1"});
  }, 10000);
});

这避免了占用后端服务器的唯一线程,使其可以自由地开始重叠处理第二个(第三个、第四个...)请求。

这是 Node 的关键原则之一:如果你想避免,就不要同步做事。 :-) 这就是 API 如此面向异步的原因。

如果你确实发现在某个时候你有大量的 CPU-burning c运行ching 你需要做的来处理一个请求,你可以将它作为子进程分离出来服务器,而不是在服务器进程中执行。 Node 在设计上是单线程的,通过强调异步 I/O 实现了非常高的吞吐量。这对你需要做的大部分事情都很有效……直到它不需要。 :-)


回复您的评论:

The backend process will be written in another technology other than node, it will call a DB and it could take a while. I wrote that simple node rest service to simulate that. What I would like to understand is how the frontend server will react if the backend takes time to process the requests.

花时间处理请求和占用唯一的服务器线程忙等待(或做大量 CPU-繁重的工作)之间有很大的区别。您的忙等待模型执行大量 CPU-繁重的工作,但如果获取类型将在 Node 外部,您将不会忙等待它,您将排队等待异步回调完成(等待来自子进程的 I/O,或来自连接到第三个服务器进程的套接字的 I/O,或等待来自数据库的 I/O,等等)。因此,上面的 setTimeout 示例是您实际操作的更好模型。

忙等待使前端无法完成,因为它是这样的:

                               Backend
Time    Frontend                Queue                        Backend
−−−−    −−−−−−−−−−         −−−−−−−−−−−−−−−−−−−−−        −−−−−−−−−−−−−−−−−−−−−
0 sec   Request #1  −−−−−−> Receive request #1   −−−−−> Pick up job for request #1
0 sec   Request #1  −−−−−−> Receive request #2
                                                        Busy wait 10 seconds
10 sec  Got #1 back <−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− Send response #1
                                                 −−−−−> Pick up job for request #2
                                                        Busy wait 10 seconds
20 sec  Got #2 back <−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− Send response #2

所以即使前端没有忙等待,它也会看到 20 秒过去了,因为后端忙等待(无法做任何其他事情)每个请求 10 秒。

但这不是您的实际设置要做的,除非您使用的其他技术也是单线程的。 (如果是,您可能希望同时拥有多个 运行。)