Node.js vs Java: Comparação de desempenho do servidor HTTP nativo para o caso hello world

Apr 07 2023
Os artigos recentes sobre a série de comparação de desempenho ficaram bastante populares na comunidade média. Inicialmente, fiz uma comparação do servidor nativo do Node/Deno/Bun com o SpringBoot.

Os artigos recentes sobre a série de comparação de desempenho ficaram bastante populares na comunidade média. Inicialmente, fiz uma comparação do servidor nativo do Node/Deno/Bun com o SpringBoot. Recebi comentários de que essa era uma comparação injusta. SpringBoot é um framework e deve ser comparado com um framework do outro lado. Honrando os comentários, fiz uma comparação de acompanhamento do SpringBoot com Node/Deno/Bun rodando em express/fastify. Ainda recebi comentários de que uma comparação de servidores HTTP nativos em ambos os lados seria muito útil.

Neste e em alguns artigos subsequentes, compararei os servidores nativos fornecidos por Node.js, Deno, Bun e Go com o servidor nativo de Java. Este artigo compara o servidor nativo do Node com o servidor nativo do Java.

Todos os artigos recentes podem ser vistos na minha página de autor .

a configuração

Os testes são executados no MacBook Pro M1 com 16G de RAM.

As versões de software são:

  • Node.js v19.8.1
  • Java 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();
        }
    }
}

Cada teste é executado para 5 milhões (5 milhões) de solicitações.

Os testes são executados para 25, 100 e 300 conexões simultâneas.

O teste de carga é realizado usando a ferramenta de teste HTTP da Bombardier.

A seguir estão as tabelas que mostram os resultados para cada nível de simultaneidade:

O servidor HTTP nativo do Node acaba sendo mais rápido que o servidor nativo do Java. A diferença não é muito se é RPS ou números de latência, mas há uma diferença. Além disso, o Node usa muito menos recursos do sistema, como CPU e memória.

VENCEDOR: Node.js

Pode haver uma discussão sobre o uso do pool de encadeamentos em cache em Java. A outra opção é alocar um pool de encadeamentos de tamanho fixo no início. Aqui está um teste rápido usando 100 conexões simultâneas com um pool de threads de tamanho fixo de 125 threads. A única alteração de linha no código Java é:

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

Não vejo nenhuma melhoria de desempenho com um pool de threads de tamanho fixo.

Nos próximos artigos, a comparação com Java será apresentada para Deno, Bun e Go.

ATUALIZAR:

  • A comparação de Java com Deno já está disponível aqui
  • A comparação de Java com Bun já está disponível aqui
  • A comparação de Java com Go já está disponível aqui