Um, am I the only one who considers Node.js a beautiful tool suited for developing servers?
Why async by default? Because defaults should encourage best practices. It can handle C10K problem out of the box. With libraries like Q, the code can actually be rather easy to follow and maintain.
One of the biggest reasons why a multi-user server should be coded in async-by-default style is as follows: often, you want to obtain objects and not care about how they are obtained. In procedural languages, this isn't possible. Consider the following challenges:
1) Query sent to 10 different shards, wait until they all return, combine the results in the app, and then use them. In a synchronous language, you HAVE TO go to one shard, then another, then another, and the time is 10x longer. In an async style, it's only as long as the longest query!
2) Some of the objects may have already been cached, whereas others need to be obtained. In addition, the ones that need to be obtained shouldn't be requested more than once at the same time, but callbacks should be placed on a waiting list.
3) Various parts of your code (e.g. from different requests) might want to request the same object. Not only that, but you might want to batch your requests. That is to say, wait 10 milliseconds and see if any other (unrelated) parts of your code also want to grab some objects from the same DB table. Suddenly you are issuing a lot less queries because you can build middleware like this easily in JS. Not so in PHP for example - I would know. A lot is wasted there.
4) Let's consider #1 some more. The queries may go to other HTTP endpoints - do you really want to wait that long for I/O? Furthermore, even if they are going to local MySQL shards, your site becomes slower with an extra factor of O(log n) with the number of users. Kind of like when you use a relational database instead of a graph database for one-degree-away lookups. It could have been O(1) but instead you introduced another factor of O(log n). Not terrible, but certainly gives you a 5-20x cost down the line.
Not just more difficult. Threads are really wasteful to spawn just so you can hit 10 shards. Even if you have a thread pool. Evented I/O is much better than threads.
When NGiNX launched, it was the evented web server in a sea of threaded servers. Guess which paradigm won.
It's not a dichotomy. The answer is that both models are useful, and most systems support hybrid threaded and evented modes. Node.js only supports one model, which is a huge drawback.
In PHP and other scripted web server languages? Please show me how it's done.
PHP does preforking first of all. You have about 30 "threads" sitting around, able to handle 30 clients. This is STILL not the same as evented programming, which lets you send out these requests and wait.
Speaking of -- have you ever heard of evented i/o?
Let me put it this way ... threads and workers are good for handling incoming requests (one worker per request). But for outgoing requests, it's nice to have evented i/o!
Threads do have a cost in terms of virtual memory, but on 64-bit architectures, this is a non-issue.
Optimizing for "C10K" (which, btw, was a big number in 1999, not today) by using an event loop will actually harm latency and performance in the case that you are dealing with a small number of highly active connections. Believe it or not the kernel can do just as good (or better) a job of scheduling than Node's event loop.
Comments
Um, am I the only one who considers Node.js a beautiful tool suited for developing servers?
Why async by default? Because defaults should encourage best practices. It can handle C10K problem out of the box. With libraries like Q, the code can actually be rather easy to follow and maintain.
One of the biggest reasons why a multi-user server should be coded in async-by-default style is as follows: often, you want to obtain objects and not care about how they are obtained. In procedural languages, this isn't possible. Consider the following challenges:
1) Query sent to 10 different shards, wait until they all return, combine the results in the app, and then use them. In a synchronous language, you HAVE TO go to one shard, then another, then another, and the time is 10x longer. In an async style, it's only as long as the longest query!
2) Some of the objects may have already been cached, whereas others need to be obtained. In addition, the ones that need to be obtained shouldn't be requested more than once at the same time, but callbacks should be placed on a waiting list.
3) Various parts of your code (e.g. from different requests) might want to request the same object. Not only that, but you might want to batch your requests. That is to say, wait 10 milliseconds and see if any other (unrelated) parts of your code also want to grab some objects from the same DB table. Suddenly you are issuing a lot less queries because you can build middleware like this easily in JS. Not so in PHP for example - I would know. A lot is wasted there.
4) Let's consider #1 some more. The queries may go to other HTTP endpoints - do you really want to wait that long for I/O? Furthermore, even if they are going to local MySQL shards, your site becomes slower with an extra factor of O(log n) with the number of users. Kind of like when you use a relational database instead of a graph database for one-degree-away lookups. It could have been O(1) but instead you introduced another factor of O(log n). Not terrible, but certainly gives you a 5-20x cost down the line.
Have you ever heard of threads?
Threads are more difficult than writing event-loop based code IMO
Not just more difficult. Threads are really wasteful to spawn just so you can hit 10 shards. Even if you have a thread pool. Evented I/O is much better than threads.
When NGiNX launched, it was the evented web server in a sea of threaded servers. Guess which paradigm won.
It's not a dichotomy. The answer is that both models are useful, and most systems support hybrid threaded and evented modes. Node.js only supports one model, which is a huge drawback.
threads don't have to be more difficult. check out actors.
Meteor seems to think threads are the way to go (Fibers)
In PHP and other scripted web server languages? Please show me how it's done.
PHP does preforking first of all. You have about 30 "threads" sitting around, able to handle 30 clients. This is STILL not the same as evented programming, which lets you send out these requests and wait.
Speaking of -- have you ever heard of evented i/o?
Let me put it this way ... threads and workers are good for handling incoming requests (one worker per request). But for outgoing requests, it's nice to have evented i/o!
http://celluloid.io/
https://github.com/jruby/jruby/wiki/Concurrency-in-jruby
Yes, I wrote a Ruby wrapper to libev before Ry wrote Node, and this coroutine abstraction to get rid of the async gunk, but Ry left that part out:
http://revactor.github.io/philosophy/
Got anything for PHP?
I don't care about PHP
Referencing C10k implies yes. Threads cost.
Do you know what threads cost? Probably not.
Linux has an O(1) scheduler.
Threads do have a cost in terms of virtual memory, but on 64-bit architectures, this is a non-issue.
Optimizing for "C10K" (which, btw, was a big number in 1999, not today) by using an event loop will actually harm latency and performance in the case that you are dealing with a small number of highly active connections. Believe it or not the kernel can do just as good (or better) a job of scheduling than Node's event loop.