Tích hợp Spring Boot Microservices với WebClient qua WebFlux

Bài viết này trình bày cách thực hiện giao tiếp giữa các microservices trong Spring Boot bằng WebClient, một thành phần quan trọng trong kiến trúc phản ứng (reactive). Kể từ phiên bản 5.0, RestTemplate đã được đưa vào chế độ bảo trì và WebClient được khuyến nghị sử dụng thay thế vì khả năng hỗ trợ cả các kịch bản đồng bộ, bất đồng bộ và xử lý luồng dữ liệu.

WebClient là một HTTP client không chặn, hoạt động dựa trên mô hình phản ứng, sử dụng các thư viện HTTP client như Reactor Netty ở tầng dưới. Để sử dụng WebClient trong dự án Spring Boot, bạn cần thêm dependency Spring WebFlux vào classpath.

Các bước thực hiện

Chúng ta sẽ xây dựng hai microservices: Department ServiceUser Service. User Service sẽ sử dụng WebClient để gọi API của Department Service và lấy thông tin chi tiết về bộ phận của người dùng.

Bước 1: Cập nhật Dependency cho User Service

Mở tệp pom.xml của dự án user-service và thêm các dependency sau:


		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-webflux</artifactId>
		</dependency>
		<dependency>
			<groupId>io.netty</groupId>
			<artifactId>netty-resolver-dns-native-macos</artifactId>
			<classifier>osx-aarch_64</classifier>
		</dependency>

Lưu ý: Dependency netty-resolver-dns-native-macos được thêm vào để giải quyết các vấn đề có thể phát sinh liên quan đến phân giải DNS trên macOS ARM.

Bước 2: Cấu hình WebClient như một Spring Bean

Tạo một @Bean cho WebClient trong lớp cấu hình chính của user-service:


package io.wz.userservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.reactive.function.client.WebClient;

@SpringBootApplication
public class UserServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }

    @Bean
    public WebClient webClient(){
        // Xây dựng WebClient. Có thể cấu hình thêm Base URL ở đây nếu cần.
        return WebClient.builder().build();
    }
}

Bước 3: Tích hợp và sử dụng WebClient để gọi API

Tiêm (inject) WebClient vào service và sử dụng nó để thực hiện các lệnh gọi REST API:


// Trong UserService, ví dụ: UserServiceImpl
// ...
private WebClient httpClient; // Tiêm WebClient

// ...
// Thực hiện cuộc gọi GET đến Department Service
DepartmentDto departmentDetails = httpClient.get()
        .uri("http://localhost:8080/api/departments/" + user.getDepartmentId()) // Địa chỉ của Department Service
        .retrieve() // Lấy phản hồi
        .bodyToMono(DepartmentDto.class) // Chuyển đổi phản hồi thành Mono<DepartmentDto>
        .block(); // Chặn để lấy kết quả (cho kịch bản đồng bộ)
// ...

Dưới đây là toàn bộ mã nguồn của lớp UserServiceImpl để tham khảo:


package io.wz.userservice.service.impl;

import io.wz.userservice.dto.DepartmentDto;
import io.wz.userservice.dto.ResponseDto;
import io.wz.userservice.dto.UserDto;
import io.wz.userservice.entity.User;
import io.wz.userservice.repository.UserRepository;
import io.wz.userservice.service.UserService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

@Service
@AllArgsConstructor
public class UserServiceImpl implements UserService {

    private UserRepository userRepository;
    private WebClient webClient; // WebClient đã được cấu hình như một bean

    @Override
    public User saveUser(User user) {
        return userRepository.save(user);
    }

    @Override
    public ResponseDto getUser(Long userId) {

        ResponseDto responseDto = new ResponseDto();
        // Lấy thông tin người dùng từ User Repository
        User user = userRepository.findById(userId).orElseThrow(() -> new RuntimeException("User not found"));
        UserDto userDto = mapToUserDto(user);

        // Sử dụng WebClient để gọi Department Service
        DepartmentDto departmentDto = webClient.get()
                .uri("http://localhost:8080/api/departments/" + user.getDepartmentId()) // URL của Department Service
                .retrieve()
                .bodyToMono(DepartmentDto.class) // Chuyển đổi kết quả sang Mono
                .block(); // Chặn để nhận kết quả (sử dụng cho ví dụ đồng bộ)

        responseDto.setUser(userDto);
        responseDto.setDepartment(departmentDto);

        return responseDto;
    }

    // Hàm tiện ích chuyển đổi User sang UserDto
    private UserDto mapToUserDto(User user){
        UserDto userDto = new UserDto();
        userDto.setId(user.getId());
        userDto.setFirstName(user.getFirstName());
        userDto.setLastName(user.getLastName());
        userDto.setEmail(user.getEmail());
        return userDto;
    }
}

Kiểm thử

Sau khi hoàn tất cấu hình, hãy khởi động Department Service trước, sau đó khởi động User Service. Khi cả hai dịch vụ đều đang chạy trên các cổng khác nhau, bạn có thể thực hiện lệnh gọi API để kiểm tra chức năng:

API Get User

Khi gọi API này, bạn sẽ nhận được phản hồi bao gồm cả thông tin người dùng và thông tin bộ phận tương ứng. Điều này xác nhận rằng User Service đã gọi thành công Department Service bằng WebClient.

Thẻ: Spring Boot Microservices webclient WebFlux Reactive Programming

Đăng vào ngày 23 tháng 7 lúc 11:34