【问题标题】:Why are my request and response messages out of order?为什么我的请求和响应消息有问题?
【发布时间】:2016-09-19 12:37:40
【问题描述】:

我正在阅读一本教你 Node.JS 基础知识的书,并且我创建了几个程序 - 一个响应者和一个请求者。

响应者:

"use strict";

const fs = require("fs");
const zmq = require("zmq");
const responder = zmq.socket("rep"); // Create socket to reply to client requests

// Handle incoming requests
responder.on("message", function(data) {
    // Parse incoming message
    let request = JSON.parse(data);
    console.log("Received request to get: " + request.path);

    // Read file and reply with content
    fs.readFile(request.path, function(err, content) {
        console.log("Sending response content");
        responder.send(JSON.stringify({
            content: content.toString(),
            timestamp: Date.now(),
            pid: process.pid
        }));
    });
});

// Listen on TCP port 5433
responder.bind("tcp://127.0.0.1:5433", function(err) {
    console.log("Listening for zmq requesters...");
});

// Close the responder when the Node process ends
process.on("SIGINT", function() {
    console.log("Shutting down...");
    responder.close();
});

请求者:

"use strict";

const zmq = require("zmq");
const filename = process.argv[2];
const requester = zmq.socket("req"); // Create request endpoint

// Handle replies from responder
requester.on("message", function(data) {
    let response = JSON.parse(data);
    console.log("Received response:", response);
});

requester.connect("tcp://localhost:5433");

// Send request for content
for (let i=1; i <= 3; i++) {
    console.log("Sending request " + i + " for " + filename);
    requester.send(JSON.stringify({
        path: filename
    }));
}

所以我运行启动良好的响应程序,然后像这样运行请求程序(target.txt 已经存在于文件系统中):

> node requester.js target.txt

奇怪的是,鉴于 Node.js 的单线程,我希望输出总是是:

Sending request 1 for target.txt
Sending request 2 for target.txt
Sending request 3 for target.txt
Received response: { ...

但是,有时我会这样,但有时我会:

Sending request 1 for target.txt
Sending request 2 for target.txt
Received response: { ...
Sending request 3 for target.txt

这怎么可能?事件循环正在执行我的for 循环,这应该意味着“发送请求”行得到输出,然后它有机会调用响应处理程序。为什么有时会在记录第三个请求之前记录响应?

【问题讨论】:

  • 我认为鉴于网络的不确定性,您不应依赖请求/响应的顺序。您现在可以发出 request1,然后发出 request2 和 request3,但您可以按任何顺序获得响应。
  • @klikas 你了解 Node.js 的单线程本质吗?
  • 我知道 Node.js 是单线程的,我只是不知道你如何才能真正确保在现实世界中,你的请求会以特定的顺序到达你的服务器一直都是。
  • 我猜这与包含本机代码的zmq 有关,并且在这种情况下语义不同。该行为表明,如果响应可用,则在 send 实现中调用提供给 on 的回调。
  • @cartant 这是唯一的可能性。回调必须在发送操作期间同步调用才能发生。

标签: javascript node.js zeromq


【解决方案1】:

很明显,只有在send 实现中调用提供给on 的回调时,才会发生您看到的行为。

zmq 模块包含本机代码。它以本机代码接收响应,然后将它们提供给 JavaScript 主机。它对响应的接收不依赖于 JavaScript 事件循环。似乎在send 实现中,如果响应可用,则调用提供给on 的回调。据推测,任何未在send 调用中传递给回调的响应都会在下一轮通过事件循环传递。

【讨论】:

  • 或者所有的响应都完全绕过了事件循环?如果 ZMQ 使用自己的线程来执行on("message"... 回调,那它为什么需要担心 Node.js 的事件循环呢?
  • 它无法在另一个线程上执行 JavaScript 回调。 JavaScript 仍然是单线程的。但在send 内,它可以调用回调。
  • JavaScript 怎么可能是单线程的?当主事件循环正在将Sending request 记录到控制台时,ZMQ 出现并将Received response 记录到控制台。当然那一定是一个不同的线程。
  • JavaScript 是单线程的。有一些关于 JavaScript/本机代码互操作 here 的信息。 send 在 JavaScript 线程上被调用,并可能执行以下操作:发送消息;检查在 JavaScript 线程运行时收到的任何响应;如果有任何响应,将它们传递给on 回调;然后返回调用 JavaScript - 并继续遍历您的 for 循环。
  • 这似乎是一种奇怪而出乎意料的行为。当我“发送”时,我不希望它“发送并检查响应并运行回调处理程序”。我希望它发送,然后完成运行我的其余代码,然后继续运行事件循环和 then 回调(如果收到任何响应)。我不确定他们为什么按照他们的方式设计它。
【解决方案2】:

responder 函数中读取文件时,您使用fs.readFile 这是一个异步非阻塞函数(它将在新线程中读取文件)。这意味着它在接受新请求之前不会等待文件被读取。

如果你想使用同步函数,你可以使用

fs.readFileSync

这将等待文件被完全读取,然后发送响应。

【讨论】:

  • 这不是我的意思。我的观点是,请求程序在输出所有 3 个请求之前 将该响应输出到控制台,即使 Node.js 的单线程特性应该意味着它必须 输出所有 3 个请求,即使在此期间有响应到达。
  • 也许我还是不明白。但是当您使用fs.readFile 时,它将在不同的线程中执行此操作。因此,取决于它读取文件的速度,顺序可以改变。可能是您读取文件的所有请求在不同线程中同时运行,然后最快的请求将首先返回
猜你喜欢
  • 2021-12-17
  • 1970-01-01
  • 2015-11-30
  • 2019-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多