Should anyone be shocked that you shouldn't do synchronous calls in an asynchronous framework? Or that you should use nginx to serve your static content and not Node? Requesting remote services in parallel is a no brainer. What does blazing fast really mean here? Using your framework correctly just sounds like "normal fast".
The whole target of his dissatisfaction is this quote on the Node home page: "Almost no function in Node directly performs I/O, so the process never blocks. Because nothing blocks, less-than-expert programmers are able to develop fast systems." That's bullshit. Evented programming isn't some magical fairy dust. If your request handler takes 500ms to run, you're not going to somehow serve more than two requests per second, node or no node. It's blocked on your request handling.
And all that stuff Apache does for you? Well, you get to have fun setting that up in front of the nodejs server. Your sysadmin will love you.
Basically if you're doing a lot of file/network IO that would normally block, node is great. You can do stuff while it's blocked, and callbacks are easier to pick up and handle than threads. But how often does that happen? Personally my Rails app spends about 10% of its time in the DB and the rest slowly generating views (and running GC, yay). AKA CPU-bound work. AKA stuff Node is just as slow at handling, with a silly deployment process to boot.
Try making something that depends on an I/O resource that is really slow, that will give you more of a feeling for it quickly.
I did this in one of my projects (Ajax-Gist.com) where I was directly querying a 3rd party API (GitHub) for each request. That meant that each request was client->server roundtrip time plus server->api roundtrip time, which worked out to be around 200ms, or a total throughput of 5 requests per second.
This is obviously unacceptable and a complete waste of resources so I re-wrote the whole thing to be asynchronous. The new application, running in a single thread on the same hardware can now handle around 70 requests per second with 30 concurrent users. That's pretty damn fast for a Rails application...
Now I didn't use Node to solve my problem (though I considered it!) but the principles and the message I took away was the same. I think it's a handy tool to have in your toolkit, but as you've already realized it's simply not necessary for a lot of projects, particularly smaller ones.
Obviously if you have node doing less it's going to be "faster". Or is it? I'm not really sure that offloading tasks qualifies as being faster. I'd like to see some data on this though. Basic caching as supplied with connect should serve static files pretty damn fast I would think. Perhaps nginx does that for a living and kicks ass, but node shouldn't be all that bad.
>I assume you can imagine some cpu bound tasks that would have node.js at 12 connections/sec or less.
Certainly. That would fall under "newb/clueless" design, though. Anyone who would throw a CPU-bound task into a primary Node server shouldn't be allowed near architectural design decisions. Whereas code in PHP written using best practices can easily end up with a server that can barely hit 100 queries per second.
Imagine, for instance, a situation where the client needs to do 50 requests to the server to render a page [1][2], and each query ends up with 20ms of latency on the PHP side; assuming you're running 8 threads (and the client makes 8 concurrent requests), a single page query could block your server for 125ms. A slow client or network might even block your PHP threads for longer. Node could crunch through ten thousand requests like that per second when running on four CPUs, meaning 125 of these bloated pages rendered per second, compared to ... 8 or less.
Even with decent client pages that can render with ONE server query, a couple dozen database lookups are par for the course, sometimes including an authentication lookup on a third-party OAUTH server. That could be 125ms all by itself, and in PHP your thread is blocked while the lookup happens. With the async model, once the query is off, the server is doing work on other requests until the query has returned data.
Many CPU-bound tasks like "convert an image" are already coded in Node to happen in the background, triggering a callback when they're done so you can then send the result to the client. And in Node it's absolutely trivial to offload any likely CPU-bound task to a microservice, where the NodeJS server just queries the microservice and waits for the result. Which you'd want to do, of course, if a task is CPU-bound, because you would want a faster server than V8 running it anyway. Go would be a likely candidate, and Go handles threading either through light/async threads or via actual threading, as necessary. It's quite awesome.
And if you really can't trust your developer to write code without extensive time-consuming calculations, then make them use Elixir or Erlang. It will use preemptive multitasking at the VM level if a thread takes up too much time, and even if they foolishly write a task that takes hundreds of milliseconds to complete, it will still task swap and serve other clients.
But arguing that pathologically bad code in Node can make it perform as badly as PHP does all the time isn't exactly a ringing endorsement for the language.
[1] In 2014 the average number of objects a web page requested was 112, and seemed to continue to be going up, though I'm assuming a lot of those are static resources and third party requests, like for analytics and ads. http://www.websiteoptimization.com/speed/tweak/average-web-p... I've personally seen pages with 70-80 requests against a PHP backend to render one page.
[2] And I wouldn't call a client page needing 50 requests a best practice, but I'm assuming that we're talking about the server side here, and that we are being forced to deal with an existing client that behaves that way. So call it "best practices on the server."
I like node.js, but this post completely misunderstands "Node.js is Cancer". The point of that post isn't that node.js is slow; its point is that node.js does concurrency, but not parallelism; i.e., if your code is CPU-bound, it can only serve one request at a time.
I wouldn’t describe 4x nodejs as “super fast”. Nodejs is one of the slowest popular backends, or at least it was when I did a comparison a few years ago.
Nginx has a lot of tricks to handle this for you, including caching and working over multiple cores. There is also less framework overhead in Nginx because it's not trying to do dynamic content. I'd be surprised if Node beats Nginx for serving static content, but alas I do not have any proof. At the very least it will use time in Node that is better off serving dynamic content.
For tasks that are primarily IO bound, async architectures can scale more than synchronous languages. Period.
It would take intentionally (or newb/cluelessly) bad design to end up with a Node server that DOSed at 12 connections per second.
In PHP, if you run 4 threads, it just takes a backend with a 333ms latency (on all queries performed serially) to limit you to 12 connections per second. If you only run one thread, you just need a backend with a cumulative 83ms latency to get DOSed at 12 connections per second. In a more realistic scenario, a typical crappy framework will result in dozens of queries for a single page, but it comes down to the same thing.
In Node, you can run one thread with a cumulative 333ms backend latency and still handle thousands of connections per second. They'll each just end up waiting a bit more than 333ms for their results, assuming the database itself isn't DOSed (which takes a surprisingly high load -- way above the levels we're talking). Actually, depending on how interconnected the backend queries are, Node may actually result in less than a total 333ms latency, because many of those queries may also be parallelized by the browser, and will then be handled in parallel by the server (and much of the latency may actually be in http negotiations and/or establishing a database connection, honestly).
The fact that they were only getting 1k reqs/sec with Node gives me concern. It clearly shows something went wrong there very early on at a very fundamental level. By no means is Node the end-all be-all for performance by any measure, but you should definitely be getting much much higher throughput than 1k reqs/sec.
Simply booting up a single core http server should net you around 4-5k requests per second. Spin up an instance per core and you should be _at least_ in the 10k realm.
I think they mention fast because the built-in Deno webserver is now faster than node:http. I've been following Deno for a while, and even deployed a service with it, and it's awesome that it has finally surpassed node performance.
I think if you've used Deno before convenient definitely fits, you can build quite a bit without needing external libraries, typescript by default, you kinda just start working on your problem - just a much cleaner version of what node could have been in my experience.
> Node does not multithread requests, but it surely can process many requests simultaneously if these requests are waiting for async operations: database, external APIs or other types of I/O usually. That's the very core idea of evented servers.
Within a single request, Node can async its dealing with outside services (databases, api's, etc), but it is still only processing one request at a time. There is no 'synchronized' keyword in javascript. ;-)
There is an interesting header in there: X-Heroku-Dynos-In-Use. From what I understand, this header is the number of dynos that a router is communicating with. For us, this is always around 2-3.
I suspect that the router is just a dumb nginx process sitting in front of my app. It is setup to communicate with 2-3 of my dyno's in a round robin fashion. If any one of those dyno's doesn't process the request fast enough, then requests start to back up. Once requests start to back up past 30s worth of execution, the router starts just killing those queued requests instead of just leaving them in a queue or sending the requests to another set of dyno's. Even worse is if you have a dyno that crashes (nodejs likes to crash at the first sign of an exception). I suspect that is why we see 2 or 3 in that header.
I think that part of the problem is that the routers don't just start talking to more dyno's if you have them available. So, it doesn't matter if you have 50, 200, 500 dyno's because the router is always only talking to a small subset of them. Even if you add more dyno's in the middle of heavy requests, you are still stuck with H12 for the existing dyno's. A full system restart is necessary then.
People keep saying that Node is async native, but the GC is global and synchronous, and that property will bite you on availability even at moderate traffic, i.e. sooner than you'd expect.
> If you were to do this on nginx you'd have to write the
> module in C.
You can write a module to glue nginx and V8. Many people've done it. It takes less than 400 lines of code and a lot of it is nginx typical module code. (The problem is more about the lack of nginx online help, perhaps.)
> The fact that you can write a web server in a few lines of easy to
> understand and maintain Javascript that can handle over 10,000
> concurrent connections without breaking a sweat is a
> breakthrough.
Yes. But the big performance issue still is hitting the database and disks. There's no point in having a super fast web server if the DB is dog slow like the vast majority of databases out there. Including the NoSQL bunch. They are not fixing the issue of latency vs. scalability vs. reliability. For that they need to address many uncomfortable problems of current hardware architectures. This is the elephant in the room.
That's exactly the kind of apps I usually write, and nginx is fast enough that the DB is typically the bottleneck. In rare cases I've run into CPU as the bottleneck for computationally intensive tasks. If I/O is your bottleneck, then that indicates to me that Node.JS is actually performing worse for you on I/O than nginx is for me.
Yeah I don't have any node.js speed concerns for it, I just would like to have the simplest editor microservice architecture, and putting the client to a service in a service was a bridge further than I wanted.
I find a number of the fast facts to be horribly misleading due to their brevity...
"Node.js runs everything in ‘parallel’, meaning that all of your tasks can be run at the same time. For example, let’s say you have a webpage that has 100 lines of code with 10 database calls. With a web server like Apache, these 10 database calls must be made in succession. While running node.js, you can run all 10 of these database calls at once, which makes the webpage load faster."
errr... maybe...
"Current web servers must open many connections to interact with their operating system (OS), while node.js only needs to open 1 connection. Each connection costs the system memory, so using fewer connections is more efficient and faster for the entire system."
there are many lightweight polling webservers like nginx, lighttpd etc
Should anyone be shocked that you shouldn't do synchronous calls in an asynchronous framework? Or that you should use nginx to serve your static content and not Node? Requesting remote services in parallel is a no brainer. What does blazing fast really mean here? Using your framework correctly just sounds like "normal fast".
reply