Go gin vs Springboot Webflux: Hello world performance comparison
--
In the previous article, I’ve compared the performance of Go gin vs Springboot for a simple hello world case. The default mode of Spring is threadpool based. The modern mode of Spring is going towards reactive programming (async, event loop based). In this article, I’m going to compare the same hello world case to see who performs better: Go gin or Springboot webflux.
A note about Spring Webflux
Reactive Programming is a programming style which deals with asynchronous data streams! A development model which focuses on observing data streams and reacting on changes and propagating them. You learn Reactive Programming to build Reactive Systems — a highly resilient distributed systems or Microservices! Spring Webflux is an example of reactive programming.
Test setup
Configuration
The test is executed on MacBook Pro M1 with 16G of RAM.
The software versions are:
- Go 1.20.2
- Springboot 3.0.5 over Java 17
Reactive systems have certain characteristics that make them ideal for low-latency, high-throughput workloads. Project Reactor and the Spring portfolio work together to enable developers to build enterprise-grade reactive systems that are responsive, resilient, elastic, and message-driven. Reactive systems better utilize modern processors. Also, the inclusion of back-pressure in reactive programming ensures better resilience between decoupled components.
As Springboot is a framework, I’m going to use a popular framework on Go side as well: Gin. Gin is a web framework written in Go. It features a martini-like API with performance that is up to 40 times faster thanks to httprouter. If you need performance and good productivity, you will love Gin.
Code
The hello world code in both cases is as follows:
Go
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")
}
Springboot webflux
Unlike the default Springboot which required only one file to be written by us, the webflux based app requires atleast four source files.
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!"));
}
}
HelloWorldRouter.java
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);
}
}
HelloWorldClient.java
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();
}
}
Application.java
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);
}
}
Execution
Each test is executed for 5M (5 million) requests.
The tests are executed for 25, 100, and 300 concurrent connections.
The load test is carried out using Bombardier HTTP testing tool.
The following are the charts showing the results, followed by a quick analysis at the end:
Analysis
We’ve already seen that, the competition was tough for the threadpool (default mode of springboot) based hello world case in the last article. This time it’s even fiercer.
Purely looking at RPS numbers, Go beats Springboot webflux by a decent margin (the margin reduces at high level of concurrency).
However, the latency numbers are more into Springboot webflux’s favor. Barring maximum latency, Spring wins in all other latency measurements.
The CPU usage is comparable, while Spring continues to use a lot of memory, especially when compared to Go.
Winner: Tough to conclude