Spring Boot Webflux Native vs Go frameworks: Performance comparison for hello world case

Mayank C
Tech Tonic

--

This is a requested article.

Numerous people have requested to compare Spring Boot Webflux 3.2 running natively (image built using graal) vs popular frameworks on the Go side (Gin, Fiber, & Echo). We’ve already seen a similar comparison with Spring Boot Native (non-reactive).

Here it is. This article compares Spring Boot Webflux 3.2 native with the three most popular frameworks on the Go side.

Note: This isn’t really an apple to apple comparison. Neither of the Go frameworks provide even a fraction of the extensive features provided by Spring Boot ecosystem. We’ll still compare them because of the reader’s interest.

Test setup

All tests are executed on MacBook Pro M2 with 16G RAM & 8+4 CPU cores. The load tester is Bombardier (Written in Go). The software versions are:

  • Java 21 Graal CE
  • Spring Boot Webflux 3.2
  • Go 1.21.5

The application code is as follows:

Java

application.properties

server.port=3000
spring.threads.virtual.enabled=true

Application.java

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.reactivestreams.Publisher;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import reactor.core.publisher.Mono;

@SpringBootApplication
@EnableWebFlux
@EnableAsync
@Controller
public class Application {

public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class);
}

@GetMapping("/")
@ResponseBody
public Publisher<String> handler() {
return Mono.just("Hello world!");
}
}

Gin

package main

import (
"net/http"
"github.com/gin-gonic/gin"
)

func main() {
r := gin.New()

r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Hello world!")
})

r.Run(":3000")
}

Fiber

package main

import (
"github.com/gofiber/fiber"
)
func main() {
app := fiber.New()
port := ":3000"
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello World!")
})
app.Listen(port)
}

Echo

package main

import (
"net/http"
"github.com/labstack/echo"
)
func main() {
e := echo.New()
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello World!")
})
e.Start(":3000")
}

Results

Each test consists of running 5M for 50, 100, and 300 concurrent connections.

The results in chart form are as follows:

Time taken & RPS

Latencies

Resource usage

Impressions

As mentioned at the start of this article, this isn’t an apple to apple comparison. Spring Boot’s feature set is way too big for Gin/Fiber/Echo (they’re more like Express.js) to match.

Nevertheless, Spring’s reactive arm in native code gives a tough competition to Go’s frameworks. There isn’t any noticeable difference in RPS and latencies.

However, Spring Boot webflux’s resource usage is significantly higher than Go. The CPU usage is high, but memory usage is comparatively very high.

We’ve already seen a similar comparison with Spring Boot Native (non-reactive).

Thanks for reading this article!

--

--