Wednesday, February 21, 2024

Spring Boot Microservice Example Using WebClient

In the post Spring Boot Microservice - Service Registration and Discovery With Eureka we have seen an example of using Eureka for Service registration and discovery. In that example RestTemplate is used for inter-service communication. In this post we'll see how to use WebClient for communication between microservices.

As per Spring framework documentation:
As of Spring 5.0 org.springframework.web.client.RestTemplate class is in maintenance mode, with only minor requests for changes and bugs to be accepted going forward. Please, consider using the org.springframework.web.reactive.client.WebClient which has a more modern API and supports sync, async, and streaming scenarios.

So, this is one of the reasons for considering using WebClient.

WebClient

Spring WebFlux includes WebClient to call remote REST services. WebClient has a functional, fluent API based on Reactor. It is fully non-blocking and it supports streaming.

Spring Boot Microservice with WebClient example

We'll use the example used in Spring Boot Microservice - Service Registration and Discovery With Eureka as our base example and made changes to it to use WebClient in place of RestTemplate.

Add maven dependency

To use WebClient Spring Webflux is needed so you need to add Webflux starter to your pom.xml. That should be done in CustomerService as this is the service from where we need to communicate with AccountService.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

Configuration for WebClient

We'll configure WebClient.Builder instance which in turn is used to create a WebClient instance, where ever it is needed using build() method.

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
	WebClient.Builder loadBalancedWebClientBuilder(){
		return WebClient.builder();
	}
}

Changes in CustomerService implementation

import java.util.List;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import com.netjstech.customerservice.dao.entity.Customer;
import com.netjstech.customerservice.dao.repository.CustomerRepository;
import com.netjstech.customerservice.dto.AccountDto;
import com.netjstech.customerservice.dto.CustomerDto;
import com.netjstech.customerservice.exception.CustomerServiceException;
import reactor.core.publisher.Flux;


@Service
public class CustomerServiceWebClientImpl implements CustomerService{
  
  private WebClient.Builder webClientBuilder;
  private CustomerRepository customerRepository;
  
  CustomerServiceWebClientImpl(WebClient.Builder webClientBuilder, CustomerRepository customerRepository){
    this.webClientBuilder = webClientBuilder;
    this.customerRepository = customerRepository;
  }
  
  
  private String baseURL = "http://ACCOUNT-SERVICE/account/";
  
  @Override
  public Customer saveCustomer(Customer customer) {
    return customerRepository.save(customer);
  }

  @Override
  public CustomerDto getCustomerById(Long id) {
    Customer customer = customerRepository.findById(id)
         .orElseThrow(()-> new CustomerServiceException("No customer found for the given id - " + id));
    Flux<AccountDto> accountFlux = getWebClient(id);
    System.out.println("Flux " + accountFlux);
    List<AccountDto> accounts = accountFlux.collectList().block();
    System.out.println(accounts);
    // Need to create CustomerDTO so need accounts data
    CustomerDto customerDto = CustomerDto.builder()
           .id(customer.getId())
           .name(customer.getName())
           .age(customer.getAge())
           .city(customer.getCity())
           .accounts(accounts)
           .build();
    return customerDto;
  }
  
  Flux<AccountDto> getWebClient(Long customerId){
    return webClientBuilder.baseUrl(baseURL)
                 .build()
                 .get()
                 .uri("customer/" + customerId)
                 .accept(MediaType.APPLICATION_JSON)
                 .retrieve()
                 .bodyToFlux(AccountDto.class);
                     
  }
}

For sending request and receiving response there are two methods provided by WebClient- exchange() and retrieve(). Out of these two, exchange() method is deprecated since Spring 5.3. In place of that there are two alternative methods exchangeToFlux() and exchangeToMono(). These methods provide more control via access to the ClientResponse. This can be useful for advanced scenarios, for example to decode the response differently depending on the response status.

With retrieve() method you can declare how to extract the response. For example, you can extract a ResponseEntity with status, headers, and body or you can just retrieve the body. In the usage as shown above in the CustomerServiceWebClientImpl only body is retrieved.

After retrieve() method bodyToFlux() or bodyToMono() method is used to decode the body to a Flux with elements of the given type or to decode the body to the given target type. In the example shown here we need a collection of accounts so Flux is used.

Once these changes are done start the EurekaDiscoveryServiceApplication which starts the Eureka Server. Then start the CustomerService and AccountService applications.

Once all the services are started access http://localhost:8081/customer/1 where internally a call is made to account-service from customer-service using WebClient.

Spring Boot Microservice Example Using WebClient

That's all for this topic Spring Boot Microservice Example Using WebClient. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring Boot Microservices Introduction
  2. Spring Boot Microservices Example
  3. Spring Boot Microservice Example Using FeignClient
  4. Spring Boot Microservice - Externalized Configuration With Spring Cloud Config
  5. Spring Boot Microservice Circuit Breaker Using Resilience4j

You may also like-

  1. Spring depends-on Attribute and @DependsOn With Examples
  2. How to Inject Null And Empty String Values in Spring
  3. Spring NamedParameterJdbcTemplate Insert, Update And Delete Example
  4. Add Double Quotes to a String Java Program
  5. StringBuffer Class in Java With Examples
  6. Namespace And Variable Scope in Python
  7. Custom Async Validator in Angular Template-Driven Form
  8. Controlled and Uncontrolled Components in React

No comments:

Post a Comment