Rodrigo Ruiz
Rodrigo Ruiz

Reputation: 4355

Why making many requests on NodeJS is slow?

I set up a local express server with:

const express = require('express');
const app = express();
app.get('/test', (request, response) => {
    response.sendStatus(200);
});
const port = 3000;
app.listen(port, () => {});

Then I ran a script with:

const axios = require('axios');
async function main() {
    console.time('time');
    const requests = Array(5000).fill().map(() => axios.get('http://localhost:3000/test'));
    await Promise.all(requests);
    console.timeEnd('time');
}
main();

And my question is why this script takes 3 seconds on my machine?

I'd expect it to take a few milliseconds just like with any other for loop of 5000 iterations. Because I'm running the server locally and calling it via localhost, I expect no latency, therefore, the waiting time for the promises should be almost 0.

Can anyone explain to me what's going on?

Also, how can I do many requests at the same time faster?

EDIT

Looking here https://stressgrid.com/blog/webserver_benchmark/ I'd expect my single process node server to be able to handle at least 20k requests concurrently without any delay.

So I'm guessing there is some configuration missing on my machine. Maybe some flag when starting the node server?

Upvotes: 0

Views: 5345

Answers (2)

roim
roim

Reputation: 4930

3 things:

  • That benchmark is not properly setup.
  • Express is the slowest of all NodeJS web frameworks.
  • Your machine might be misconfigured.

You can find better benchmarks and a comparison of different frameworks here: https://www.fastify.io/benchmarks/

Their github repo explains all the setup they've done, so you can compare your machine against theirs too.

1. Benchmarking

To put it plainly, the benchmark you set up is not valid. It doesn't reproduce any real world scenario, and is not optimized for the synthetic scenario it creates.

Just to exemplify, since on Node everything is single threaded, you'd have better performance running requests serially so that connections can be reused (would also need to change your request framework to one that can reuse connections). HTTP 1 doesn't reuse connections if you issue requests in parallel, AND your client isn't setup to reuse connections anyways.

Let's take a look at what results look like after fixing that. On my machine, the benchmark you posted doesn't even run--node crashes if you try to open that many connections simultaneously on the same port. This version has about the same theoretical performance as your benchmark, and it runs:

const axios = require("axios");
async function main() {
  console.info(process.hrtime.bigint() / 1000000n + "ms");
  for (let i = 0; i < 5000; ++i) {
    await axios.get("http://localhost:3000/test");
  }

  console.info(process.hrtime.bigint() / 1000000n + "ms");
}
main();

That takes around 3 seconds on my machine (about the same time as yours). Now let's reuse connections:

const axios = require("axios");
const http = require("http");

async function main() {
  const httpAgent = new http.Agent({ keepAlive: true });

  console.info(process.hrtime.bigint() / 1000000n + "ms");
  for (let i = 0; i < 5000; ++i) {
    await axios.get("http://localhost:3000/test", { httpAgent });
  }

  console.info(process.hrtime.bigint() / 1000000n + "ms");
}
main();

This takes 800ms.

There's a lot of other details like this that your benchmark misses. I can't summarize all of them. You can compare your benchmark to Fastify's (linked above) to see how each difference impacts your measurement.

2. Frameworks

Express has its popularity for being simple, but it is not a fast framework. Take a look at more modern ones such as Koa or Fastify. Note that your app likely will do much more than just serve an empty page, so performance of your web framework is likely not important. That said, I don't think anyone should be using express in 2021 if they have a choice anyways, since their developer experience is also outdated (eg there's no support for awaiting a request within a middleware).

3. Local Machine

It could also just be that your computer is slow, etc. That's another reason to start by rerunning a standardized benchmark instead of creating your own.

Upvotes: 3

user16459683
user16459683

Reputation:

Define slow to begin with. You have Array(5000).fill() which we can interpreted as reserved me 5000 slots in memory for me in other word you do a for loop of 5000 then you do 5000 request so that means 10,000 looping. Do the same 10,000 looping on java and compare then tell me if JavaScript is slow. Also I don’t know if you have, but axios has quite a few internal validations

Upvotes: 1

Related Questions