【问题标题】:Need a little help understanding Node.js behavior. Why does a koa server respond synchronously to two simultaneous requests from the same browser?需要一点帮助来理解 Node.js 的行为。为什么 koa 服务器会同步响应来自同一个浏览器的两个同时请求?
【发布时间】:2018-02-01 17:38:38
【问题描述】:

所以我只是想尝试用 koa 和 koa-router 编写一个 hello-world 服务器。这是我的代码。

const Koa = require('koa')
const Router = require('koa-router')

const timeout = (ms) => new Promise(res => setTimeout(res, ms))
const timestamp = () => {
    const d = new Date()
    return `${d.getUTCMinutes()}:${d.getUTCSeconds()}`
}
let req = 0

const app = new Koa()

app.use(async (ctx, next) => {
    ctx.state.req = req
    console.log(`URL: ${ctx.url}  time: ${timestamp()} request: ${req++}`)
    await next()
})

const router = new Router()

router.get('/', async (ctx, next) => {
    ctx.body = {
        message: "Hello, World!"
    }
    await next()
})

router.get('/favicon.ico', async ctx => ctx )

router.get("/test", async ctx => ctx.body = "Just a test")

app.use(router.routes())
app.use(async ctx => {
    await timeout(10000)
    console.log(`Hit req: ${ctx.state.req}! time: ${timestamp()}`)
    ctx.body.message += "After the delay!"
})


app.listen(3000)
console.log(`Listening on port 3000`)

现在这对于来自单个浏览器的单个请求非常有效。当我尝试从同一浏览器向localhost:3000 发出第二个请求时,就会出现问题。在第一个请求完成之前,第二个请求不会被注册(处理?)。如果我向/test 发出请求或使用其他浏览器发出请求,则不会发生这种情况。 Chrome 和 Firefox 的行为是一致的。唯一的例外是 Firefox Quantum。

Listening on port 3000
URL: /  time: 47:5 request: 0
URL: /  time: 47:7 request: 1
Hit req: 0! time: 47:15
Hit req: 1! time: 47:17
URL: /  time: 47:21 request: 2
Hit req: 2! time: 47:31
URL: /  time: 47:31 request: 3
Hit req: 3! time: 47:41
URL: /  time: 58:14 request: 4
Hit req: 4! time: 58:24
URL: /  time: 58:47 request: 5
URL: /  time: 58:50 request: 6
Hit req: 5! time: 58:57
Hit req: 6! time: 59:0

这里的请求 0 和 1 来自两个不同的浏览器。如您所见,请求 1 与请求 0 的状态无关。请求 2 和 3 来自同一个浏览器(Chrome、Firefox 或 Opera,它们每个都相同)。这里请求 3 仅在服务器完成对请求 2 的响应后才被注册。请求 4 是一个孤立的请求。请求 5 和 6 来自 Firefox Quantum,它的行为符合预期。

【问题讨论】:

  • 也许浏览器只想使用单个连接?不过,我认为限制高于 1。
  • @Ryan 什么意思,TCP 连接到服务器?即使这样,浏览器不应该能够通过管道在一个连接上发送多个请求吗?
  • 是的,我指的是 TCP 连接。您是正确的,浏览器应该能够通过管道发送多个请求,即使在单个连接上也是如此,但您确定它是吗?我知道随着 HTTP/2 的出现,Firefox 至少已经取消了它的 HTTP/1.1 流水线偏好。也不知道 Chrome 是否曾经默认启用它。
  • 问题在于调用 await next 而不是 next() 而不是浏览器
  • @DanielStaleiny 但是,根据 Koa 文档,使用 await 调用它非常好。在这里,我将代码精简到最低限度,这使得await 看起来多余,但我需要在我的代码中使用它,因为我希望控件针对每个请求向上游流动。 github.com/koajs/koa

标签: node.js google-chrome firefox asynchronous koa


【解决方案1】:

更新:最初的问题是你的服务器表现得像同步服务器,是由在你的中间件中调用 await next() 而不是 return next() 引起的。当您将其更改为等待时,它会等待解决该承诺并随后返回。这意味着您无法再处理任何请求,因为您正在等待首先解决。希望这将帮助您了解何时正确调用 next

将中间件按原样放在路由器顶部

const Koa = require("koa");
const Router = require("koa-router");

const timeout = ms => new Promise(res => setTimeout(res, ms));
const timestamp = () => {
  const d = new Date();
  return `${d.getUTCMinutes()}:${d.getUTCSeconds()}`;
};

let req = 0;

const app = new Koa();

app.use(async (ctx, next) => {
  ctx.state.req = req;
  console.log(`URL: ${ctx.url}  time: ${timestamp()} request: ${req++}`);
  return next();
});

const router = new Router();

router.get("/", async ctx => {
  ctx.body.message = ctx.body.message + " Hello, World!";
});

router.get("/favicon.ico", async ctx => ctx);

router.get("/test", async ctx => (ctx.body = "Just a test"));
app.use(async (ctx, next) => {
  await timeout(10000);
  console.log(`Hit req: ${ctx.state.req}! time: ${timestamp()}`);
  ctx.body = { message: "With the delay!" };
  return next();
});
app.use(router.routes());

app.listen(3000);
console.log(`Listening on port 3000`);

并在控制台中响应。结果符合预期,我调用了多个请求相同和不同的浏览器,它可以工作。一切都是异步和延迟的。

Listening on port 3000
URL: /  time: 11:56 request: 0
URL: /  time: 11:58 request: 1
URL: /  time: 12:0 request: 2
Hit req: 0! time: 12:6
Hit req: 1! time: 12:8
Hit req: 2! time: 12:10
URL: /  time: 12:10 request: 3
URL: /favicon.ico  time: 12:11 request: 4
URL: /  time: 12:19 request: 5
Hit req: 3! time: 12:20
Hit req: 4! time: 12:21
URL: /favicon.ico  time: 12:21 request: 6
Hit req: 5! time: 12:29
URL: /  time: 12:29 request: 7
Hit req: 6! time: 12:31
Hit req: 7! time: 12:39

我所做的是更改中间件的顺序,因为您在点击 get route '/' 后就有了它。正确更改 next() 并且一切正常。 我不得不在中间件中定义消息。

/**** 旧消息 ****/

router get 没有 (ctx, next) 但只有 ctx。只有中间件和参数有 ctx 和 next 解析。 删除下一个并等待下一个(),它应该可以工作 koa router docs

【讨论】:

  • 等等什么意思?路由器中的所有示例都使用next。
  • @KidCoder:我很确定await next() 不正确。当返回一个承诺时,你使用next()。 (所有异步函数都返回 Promise。)不过可能也没有破坏任何东西。
猜你喜欢
  • 2011-04-07
  • 2015-08-08
  • 1970-01-01
  • 2015-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多