Node.js v. Erlang

原文在墙外: http://ksdlck.com/post/16493417514/node-js-v-erlang 

Node and Erlang both set out to solve an issue that developers of a particular class of systems routinely face: dealing with massive concurrency.

Erlang uses the actor model; one spawns concurrent tasks (“actors”) quickly and easily, and these actors reference each other like nodes on a network, communicating with one another by sending immutable bits of data (“messages”) back and forth. This model is very understandable for the programmer, but also troubling in that there is no shared state. It is this very thing that gives Erlang such a good basis for some of its more interesting abilities (like fault tolerance), but it is also something that can be awkward to program against. At the same time, Erlang enforces some very good practices on programmers, by making them move shared state into external systems designed specifically for that, be it the Mnesiadatabase itself part of Erlang, or other systems.

Node adopts the Javascript model; there will be no actual concurrency, but interleaving of tasks is possible through yielding. Javascript is and has always been, at its heart, a language that is cooperatively scheduled using the likes of setTimeout, onClick, and XMLHTTPRequest. Node takes this same concurrency model, but provides an inequal, overlapping set of operations through which tasks yield, with setTimeout making a reappearance, and process.nextTick, and all the various other IO operations having both a yielding and non-yielding persona available. In short, Node (and its Lua counterpart Luvit) represent something that I’ve been wanting for a long time: cooperative scheduling based around lightweight contexts and automatic yielding on IO, with a core that leverages the various concurrent non-blocking IO syscalls that have arisen in the past few years on most major OSes.

 

libUV (the IO core around which Node is built) provided the platform independence, by combining libev’s mature support for epoll, kqueue, /dev/poll, and friends with Windows’ IOCP. V8 provided the Javascript language runtime. Node provides a thin wrapper between these two, giving the Javascript running in V8 a set of APIs for performing IO, and leveraging Javascript’s closure support as CPS, so that IO functions know what to do next. Of course, Node’s standard library includes a bit more than that, with an HTTP client and server, a stream convention, and a crypto binding to OpenSSL.

 

Unlike Erlang, Node eschews support for multiple cores with its lack of support for preemption. In reality, this is not so much a limitation of Javascript per se as a limitation of the environments in which Javascript is run. Javascript, as a procedural language, is no less capable of spawning threads and synchronizing between them with the same primitives used by every other language in the known world, but V8 doesn’t allow for this, in part because its GC doesn’t support multithreading. I’m looking into whether this is an issue which can be overcome, thought mostly out of curiosity, as real multithreading is almost entirely useless for Node in today’s environment. A single core of any modern processor has more than enough bandwidth to saturate that machine’s disks and network interfaces and CPU intensive tasks are typically not best implemented in Javascript anyway, and Node is perfectly capable of proxying off CPU intensive operations to a Java or C internal service.

 

Erlang is a much more mature platform, and has a lot of things that would be great to see in an environment more like Node. For me, Erlang is beautiful, but perhaps errs on the side of being too opinionated. Erlang proports to offer features that are vital to systems needing high uptime, but one could imagine that a sufficiently flexible system would be able to provide the same functionality with a library (see the various actor libraries available for Scala for example). Node is just as opinionated as Erlang, but I agree with more of its opinions, based on the benefits they provide the programmer. Node is not a panacea—far from it, in fact—both because I find faults with Javascript as a language, and for other reasons. But it does provide an extremely low impedance mismatch with browser development, Javascript (and, in particular, Coco, which augments Javascript’s sad syntax and some of its more verbose boilerplate) is far from the worst language I’ve cared to work with, and the Node community is both young and vibrant which is not something I can say of Perl, Python, Ruby, or Erlang.

 

In the end, Node and Erlang find their way to almost the same place. They are both dynamic languages, with similar native datastructures, and one can see that Node’s ubiquitous EventEmitter is, in many ways, isomorphic to Erlang’s message passing scheme. Libraries like hook.ioprovide some semblance of the agnosticism with which one treats the location of an adressee in Erlang. Hotplugging of code is possible with no particular hassle, but—arguably as it should be—the responsibility of the programmer to do in such a way as to not break the application. They both are ideall suited to workloads that are primarily bottlenecked on IO, and both excel at shifting data from one place to another.