Spring Boot Webflux Native vs Go frameworks: Performance comparison for hello world case
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!