Fastify (Node.js) vs Webflux (Spring): confronto delle prestazioni Hello world

Mar 21 2023
Una delle mie serie di articoli popolari è stata il confronto delle prestazioni tra Node.js e Springboot.

Una delle mie serie di articoli popolari è stata il confronto delle prestazioni tra Node.js e Springboot. Ho iniziato la serie confrontando il server HTTP nativo di Node con Springboot (l'articolo è qui ). Poiché l'articolo ha ottenuto un numero record di visualizzazioni, ho ricevuto commenti che:

  1. Un server nativo non dovrebbe essere paragonato a un framework come Spring
  2. Per il confronto tra mele e mele, dovrebbe essere utilizzato il webflux del framework reattivo di Spring invece del threadpool di Spring

Con questo articolo, continuerò ad affrontare il punto numero 2. Questa volta confronterò Spring Webflux con il framework fastify di Node.js. Perché Fastify questa volta? Perché Fastify è piuttosto veloce rispetto a Express. I risultati sono difficili da prevedere. Scopriamo chi vince questa volta.

Una nota su Spring Webflux

La programmazione reattiva è uno stile di programmazione che si occupa di flussi di dati asincroni ! Un modello di sviluppo che si concentra sull'osservazione dei flussi di dati e sulla reazione ai cambiamenti e sulla loro propagazione. Impari la programmazione reattiva per creare sistemi reattivi: sistemi distribuiti altamente resilienti o microservizi! Spring Webflux è un esempio di programmazione reattiva.

La configurazione del test è la stessa:

  • MacBook M1 con 16 GB di RAM
  • Node.js v19.8.1
  • Primavera 3.0.4 su Java 17

Node.js

import Fastify from "fastify";
const fastify = Fastify({ logger: false });

fastify.get("/", () => {
  return "Hello world!";
});

fastify.listen({ port: 3000 });

Il codice "ciao mondo" di Spring Webflux è stato preso dalla loro documentazione ufficiale:

HelloWorldHandler.java

package hello;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;

import reactor.core.publisher.Mono;

@Component
public class HelloWorldHandler {

  public Mono<ServerResponse> hello(ServerRequest request) {
    return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
      .body(BodyInserters.fromValue("Hello World!"));
  }
}

package hello;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;

@Configuration(proxyBeanMethods = false)
public class HelloWorldRouter {

  @Bean
  public RouterFunction<ServerResponse> route(HelloWorldHandler helloWorldHandler) {

    return RouterFunctions
      .route(GET("/"), helloWorldHandler::hello);
  }
}

package hello;

import reactor.core.publisher.Mono;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;

@Component
public class HelloWorldClient {

  private final WebClient client;

  public HelloWorldClient(WebClient.Builder builder) {
    this.client = builder.baseUrl("http://localhost:3000").build();
  }

  public Mono<ClientResponse> getMessage() {
    return this.client.get()
      .uri("/")
      .exchange();
  }

}

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class Application {

  public static void main(String[] args) {
    ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
    HelloWorldClient helloWorldClient = context.getBean(HelloWorldClient.class);
  }
}

Passiamo direttamente ai risultati.

A differenza di Express, Fastify è in grado di offrire una buona concorrenza a Springboot con Webflux, ma perde terreno in RPS e in altre metriche di latenza. Fastify è in grado di segnare sull'utilizzo della CPU e della memoria.

VINCITORE: Webflux primaverile

Grazie per aver letto! Fatemi sapere eventuali commenti, suggerimenti, ecc.