Node.js vs Java: confronto delle prestazioni del server HTTP nativo per il caso hello world

Apr 07 2023
I recenti articoli sulla serie di confronto delle prestazioni sono diventati piuttosto popolari nella comunità media. Inizialmente, ho confrontato il server nativo di Node/Deno/Bun con SpringBoot.

I recenti articoli sulla serie di confronto delle prestazioni sono diventati piuttosto popolari nella comunità media. Inizialmente, ho confrontato il server nativo di Node/Deno/Bun con SpringBoot. Ho ricevuto commenti secondo cui questo era un confronto ingiusto. SpringBoot è un framework e deve essere confrontato con un framework dall'altra parte. Onorando i commenti, ho fatto un confronto di follow-up di SpringBoot con Node/Deno/Bun in esecuzione su express/fastify. Ho ancora commenti sul fatto che un confronto tra server HTTP nativi su entrambi i lati sarebbe molto utile.

In questo e in alcuni articoli successivi, confronterò i server nativi forniti da Node.js, Deno, Bun e Go con il server nativo di Java. Questo articolo confronta il server nativo di Node con il server nativo di Java.

Tutti gli articoli recenti possono essere visti sulla mia pagina dell'autore .

Il set up

I test vengono eseguiti su MacBook Pro M1 con 16G di RAM.

Le versioni software sono:

  • Node.js v19.8.1
  • Giava 20

Node.js

import http from "node:http";

http.createServer((req, resp) => {
  try {
    if (req.method !== "GET") {
      return resp.writeHead(405).end();
    }
    if (req.url !== "/") {
      return resp.writeHead(404).end();
    }
    resp.writeHead(200, {
      "content-type": "text/plain",
    });
    resp.end("Hello world");
  } catch (e) {
    resp.writeHead(500).end();
  }
}).listen(3000);

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

public class NativeHelloWorldServer {

    public static void main(String[] args) throws Exception {
        HttpServer server = HttpServer.create(new InetSocketAddress(3000), 0);
        server.createContext("/", new MyHandler());
        server.setExecutor(Executors.newCachedThreadPool());
        server.start();
    }

    static class MyHandler implements HttpHandler {
        @Override
        public void handle(HttpExchange t) throws IOException {
            String response = "Hello world!";
            t.sendResponseHeaders(200, response.length());
            OutputStream os = t.getResponseBody();
            os.write(response.getBytes());
            os.close();
        }
    }
}

Ogni test viene eseguito per 5 milioni (5 milioni) di richieste.

I test vengono eseguiti per 25, 100 e 300 connessioni simultanee.

Il test di carico viene eseguito utilizzando lo strumento di test HTTP di Bombardier.

Di seguito sono riportate le tabelle che mostrano i risultati per ciascun livello di concorrenza:

Il server HTTP nativo di Node risulta essere più veloce del server nativo di Java. La differenza non è eccessiva se si tratta di RPS o numeri di latenza, ma c'è una differenza. Inoltre, Node utilizza molto meno risorse di sistema come CPU e memoria.

VINCITORE: Node.js

Potrebbe esserci un argomento sull'utilizzo del pool di thread memorizzato nella cache in Java. L'altra opzione consiste nell'allocare un pool di thread di dimensioni fisse all'inizio. Ecco un rapido test che utilizza 100 connessioni simultanee con un pool di thread a dimensione fissa di 125 thread. L'unica modifica di riga nel codice Java è:

//server.setExecutor(Executors.newCachedThreadPool());
server.setExecutor(Executors.newFixedThreadPool(125));

Non vedo alcun miglioramento delle prestazioni con un pool di thread di dimensioni fisse.

Nei prossimi articoli verrà presentato il confronto con Java per Deno, Bun e Go.

AGGIORNAMENTO:

  • Il confronto di Java con Deno è ora disponibile qui
  • Il confronto di Java con Bun è ora disponibile qui
  • Il confronto di Java con Go è ora disponibile qui