问题
Many articles online demonstrates nodejs as an example of reactor pattern. Isn't it rather proactor?
As far as I understand, the difference between the two is:
- reactor handles events in a single thread (synchronously),
- proactor handles events is multiple threads (asynchronously) with completion callbacks.
For example in this article:
Reactor Pattern is an idea of non-blocking I/O operations in Node.js. This pattern provides a handler(in case of Node.js, a callback function) that is associated with each I/O operation. When an I/O request is generated, it is submitted to a demultiplexer.
Isn't it actually definition of proactor?
回答1:
I wasn't familiar with the Proactor design pattern. After reading a bit about it I think I understand your confusion.
Many articles online demonstrates nodejs as an example of reactor pattern
This is true.
Isn't it actually definition of proactor?
This is also true.
The difference is your point of view.
Internally, node's event loop is a blocking call (ironically). That's just the most efficient way to use non-blocking I/O. Different OSes have different functions to request the OS to wake your process up if something you are interested in happens. Due to POSIX requirements there is a cross-platform API that all modern OSes support: select()
. Node.js actually uses libuv which automatically picks the right API at compile time depending on the target platform. But for the purposes of this answer we're going to focus on select()
. So lets look at select():
numberOfEvents = select(numberOfWaits, read, write, err, timeout);
The select()
function blocks for up to timeout
milliseconds or something happens to either the read, write or err files/sockets. With just a single function the OS provides enough functionality to implement most of node.js from timers like setTimeout()
and setInterval()
to listening to network sockets. Using select()
the event loop looks something like this:
// Pseudocode:
while(1) {
evaluateJavascript();
timeout = calculateTimers();
events = select(n, read, write, err, timeout);
if (events > 0 || timersActive()) {
getCallbacks(events, read, write, err, timers());
}
}
This is basically a Reactor design pattern.
However, node hides this away in its implementation. What it exposes to Javascript programmers is a set of APIs that registers callbacks and calls those callbacks when an event happens. This is partly historical (the browser APIs was designed that way) and partly practical (it's a much more flexible architecture - almost all GUI frameworks from GTK to wxWindows to .Net works this way).
You may recognise that this sounds a lot like a Proactor design pattern. And in fact it is.
So node.js itself is an example of Reactor design pattern.
Javascript programs written in node.js are examples of Proactor design pattern.
回答2:
The distinction has nothing to do with multithreading. It is as follows:
- Reactor - I want to read from a socket, so I subscribe to a data-is-available kind of event and, and when it fires react to it by synchronously reading the available amount.
- Proactor - I want to read from a socket, so I initiate a reading operation (one that proactively reads the data, without waiting for me to react to it's availability), and subscribe to some kind of read-is-complete notification, wherein the read data is immediately available to me.
Node has both kinds of APIs, e.g. stream.ReadableStream#readable/stream.ReadableStream#read are a Reactor interface, while fs.readFile is a Proactor.
来源:https://stackoverflow.com/questions/56739934/is-nodejs-representing-reactor-or-proactor-design-pattern