Node.js Express Cluster vs Spring Boot: Hello World performance

Mayank C
Tech Tonic

--

In this brief article, we will explore a direct comparison of the performance between Node.js Express in cluster mode and Spring Boot using virtual threads. Our focus will be on a well-known benchmark test called the ‘Hello World’ benchmark. Many readers have asked for this specific comparison, making it a highly requested topic.

Setup

We conducted all tests on a MacBook Pro M2 with 16GB of RAM, using specific software versions:

  • Node.js v21.1.0
  • Spring Boot v3.1.5 with Java v 21.0.1

To generate HTTP load, we utilized the Bombardier test tool. Below is the application code we used:

Express (cluster) application

import cluster from "node:cluster";
import express from "express";

if (cluster.isPrimary) {
for (let i = 0; i < 8; i++) {
cluster.fork();
}

cluster.on(
"exit",
(worker, code, signal) => console.log(`worker ${worker.process.pid} died`),
);
} else {
const app = express();
app.get("/", requestHandler);
app.listen(3000);
}

function requestHandler(req, res) {
res.send("Hello World!");
}

Spring Boot (Virtual threads) application

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer;
import org.springframework.context.annotation.Bean;
import java.util.concurrent.Executors;

@SpringBootApplication
@RestController
public class HelloWorldApplication {

public static void main(String[] args) {
SpringApplication.run(HelloWorldApplication.class, args);
}

@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
return protocolHandler -> {
protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
};
}

@GetMapping("/")
public String handleRequest() {
return "Hello World!";
}
}

Results

All tests are executed for 10 million requests using 50, 100, and 300 concurrent connections. The results in chart and tabular form are as follows:

Note: The Express application has been deployed on a cluster comprising eight processes. The measurement of resource consumption encompasses the combined utilization of all the workers within the cluster.

Conclusion

Regrettably, Spring Boot’s performance metrics were adversely affected due to a few unusual data points, causing it to consume twice as much time as the Express cluster. Nevertheless, it is worth noting that the Express cluster utilizes a significant quantity of system resources, such as CPU and memory, contributing to its high resource consumption.

Thanks for reading!

--

--