Go gin vs Springboot Webflux: Hello world performance comparison

Mayank Choubey
Tech Tonic
Published in
4 min readMar 26

--

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

--

--

Mayank Choubey
Tech Tonic

I write about Deno, Bun, and Node.js. My new book: https://choubey.gitbook.io/learn-react-in-a-day/