Implementation of Spring WebClient Load Balancing
Create the EurekaServer Service
Step 1: Create a spring boot project using Spring Initializr, and add the below dependencies:
Dependencies:
- Spring Web
- Eureka Server
- Spring Dev Tools
- Lombok
After creating the project, the folder structure will be like below image:
Step 2: Open application.properties and write the below properties to configure the server port and Eureka server configurations.
spring.application.name=EurekaServerService
server.port=8761
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone =http://${eureka.instance.hostname}:${server.port}/eureka
Step 3: In the main class, include @EnableEurekaServer annotation to activate the Eureka server functionality of the application.
package org.example.eurekaserverservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerServiceApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerServiceApplication.class, args);
}
}
Step 4: Run the application
Once the project is completed, run the application and it will start at port 8761.
Create the example-service
Step 1: Create a spring boot project using spring initializer and add the below dependencies.
Dependencies:
- Spring Web
- Spring Dev Tools
- Lombok
After creating the project, the folder structure will be like below.
Step 2: Open application.properties and write the below properties to configure the server port and Eureka server configurations.
spring.application.name=example-service
server.port=8082
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
spring.cloud.loadbalancer.ribbon.enabled=false
Step 3: Create the ServiceController class
Go to src > main > java > org.example.exampleservice > ServiceController and put the below code.
package org.example.exampleservice;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ServiceController {
@GetMapping("/test")
public String test() {
return "Hello from your-service instance!";
}
}
Step 4: In the main class, include @EnableDiscoveryClient annotation to activate the Eureka client functionality of the application.
package org.example.exampleservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class ExampleServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleServiceApplication.class, args);
}
}
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>example-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>example-service</name>
<description>example-service</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Step 5: Run the application
Once the project is completed, run the application and it will start at port 8082.
Create the WebClient
Step 1: Create a Spring project using Spring Initializr, and add the below dependencies:
Dependencies:
- Spring Web
- Eureka Server
- Spring Dev Tools
- Lombok
After successfully the project creation done, the folder structure will be like below.
Step 2: Open the application.properties and write the below properties to configure server port and Eureka server configurations.
spring.application.name=web-client
spring.cloud.loadbalancer.ribbon.enabled=false
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
eureka.instance.hostname=localhost
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
Step 3: Create the WebClientConfig class
Go to src > main > java > org.example.webclient > config > WebClientConfig and put the below code.
package org.example.webclient.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
@Configuration
public class WebClientConfig {
@Bean
@LoadBalanced
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
Step 4: Create the TestController class
Go to src > main > java > org.example.webclient > controller > TestController and put the below code.
package org.example.webclient.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Mono;
@RestController
public class TestController {
@Autowired
private WebClient.Builder webClientBuilder;
@GetMapping("/test")
public Mono<String> testLoadBalancer() {
return webClientBuilder.build()
.get()
.uri("http://example-service/test")
.retrieve()
.bodyToMono(String.class)
.onErrorResume(WebClientResponseException.class, ex -> {
if (ex.getStatusCode() == HttpStatus.INTERNAL_SERVER_ERROR) {
return Mono.just("Internal Server Error: " + ex.getMessage());
} else {
return Mono.error(ex);
}
})
.onErrorResume(Exception.class, ex -> Mono.just("An unexpected error occurred: " + ex.getMessage()));
}
}
Step 5: In the main class, include @EnableDiscoveryClient annotation to activate the Eureka client functionality of the application.
package org.example.webclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class WebClientApplication {
public static void main(String[] args) {
SpringApplication.run(WebClientApplication.class, args);
}
}
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>web-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>web-client</name>
<description>web-client</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Step 6: Run the application
Once the project is completed, run the application and it will start at port 8080.
EurekaServer Dashboard
http://localhost:8761
Output:
WebClient Testing
GET http://localhost:8080/test
Output:
Spring WebClient Load Balancing
Spring WebClient is a non-blocking and it is a reactive client for making HTTP requests. When building the microservices, it is essential to distribute requests evenly across the service instance to ensure scalability and reliability. Spring Cloud LoadBalancer can provide an easy way to integrate load balancing with WebClient. It allows us to distribute the requests among multiple of the services registered with the service discovery tool like Eureka.
Load Balancing in Microservices
In the microservices architecture, load balancing helps to distribute the incoming network traffic across multiple servers or service instances. It can ensure no single instance is overwhelmed and improve reliability. Spring Cloud LoadBalancer, when used with WebClient, allows the automatic distribution of the requests among service instances.
Working of Spring Cloud LoadBalancer
Spring Cloud LoadBalancer can use different algorithms to balance the requests. When the request is made using the WebClient the LoadBalancer intercepts it and redirects it to the appropriate service instance of the application.