Backend

99 Ngày Spring — Ngày 66: Gọi API ngoài bằng RestClient và WebClient

SSite Admin
1 tháng 10, 2026 2 phút đọc 2 lượt xem
99 Ngày Spring — Ngày 66: Gọi API ngoài bằng RestClient và WebClient

Một API ngoài có thể chậm, lỗi hoặc trả dữ liệu bất ngờ. Spring Framework 6.1, dùng trong Spring Boot 3.2 trở lên, cung cấp RestClient đồng bộ với fluent API; WebClient dùng mô hình reactive không chặn. Chọn theo cách ứng dụng xử lý luồng, rồi đặt giới hạn và hợp đồng lỗi rõ ràng.

Gọi API ngoài bằng RestClient và WebClient

1. RestClient cho luồng đồng bộ

RestClient phù hợp khi service/controller đang xử lý đồng bộ. Dùng RestClient.Builder do Boot cung cấp để giữ customizer và hạ tầng được cấu hình. retrieve() ánh xạ phản hồi; mã 4xx/5xx mặc định dẫn đến ngoại lệ, không phải DTO hợp lệ. Đặt base URL thay vì nối chuỗi URL rời rạc.

@Bean
RestClient catalog(RestClient.Builder builder) {
    return builder.baseUrl("https://catalog.example").build();
}
Product getProduct(RestClient client, String id) {
    return client.get().uri("/products/{id}", id)
        .retrieve().body(Product.class);
}

2. WebClient và hợp đồng không chặn

WebClient trả Mono/Flux và phù hợp pipeline WebFlux. Nếu gọi block() ngay trong event-loop, ta đánh mất lợi ích không chặn và có thể gây nghẽn. Một service MVC đồng bộ vẫn có thể dùng WebClient, nhưng cần lý do rõ ràng; chỉ đổi client không tự biến toàn bộ request thành reactive.

Mono<Product> getProduct(WebClient client, String id) {
    return client.get().uri("/products/{id}", id)
        .retrieve().bodyToMono(Product.class);
}

3. Timeout và phân loại lỗi

Phân biệt lỗi kết nối, timeout, HTTP 4xx và 5xx. Đặt timeout ở HTTP client bên dưới, và một ngân sách tổng cho nghiệp vụ; timeout của một stage không luôn dừng được I/O nền. Không trả cùng một thông báo cho 404 của đối tác và lỗi mạng. Log endpoint, loại lỗi và thời gian, nhưng tránh ghi token hoặc dữ liệu nhạy cảm.

client.get().uri("/products/{id}", id)
    .retrieve()
    .onStatus(s -> s.value() == 404,
        (req, res) -> { throw new ProductMissing(id); })
    .body(Product.class);

4. Retry chỉ khi an toàn

Retry một lỗi tạm thời có thể giúp, nhưng nhân tải lên đối tác đang quá tải. Giới hạn số lần, thêm backoff và jitter, và giữ tổng deadline. Với POST tạo đơn, chỉ retry khi có idempotency key hoặc cam kết từ API đối tác. Test 429, 5xx, timeout và phản hồi chậm trước khi triển khai.

  • Chọn RestClient cho luồng MVC đồng bộ; giữ Mono xuyên suốt luồng WebFlux.

  • Theo dõi tỷ lệ lỗi, độ trễ và số lần retry theo từng đối tác.

Client HTTP là một ranh giới lỗi của hệ thống. Chọn kiểu gọi phù hợp, đặt timeout ở đúng lớp và retry có điều kiện. Ngày 67 kiểm tra dữ liệu vào bằng custom validation.

Tài liệu đối chiếu

  • https://docs.spring.io/spring-framework/reference/integration/rest-clients.html

  • https://docs.spring.io/spring-boot/3.5/reference/io/rest-client.html

S

Site Admin

Engineer and writer. Building things with TypeScript and distributed systems.

Bình luận (0)

Bạn cần đăng nhập bằng Google để bình luận.

Hãy là người bình luận đầu tiên.

Bài viết liên quan

99 Ngày Java — Ngày 66: Lock và deadlock

Dùng ReentrantLock, tryLock và thứ tự khóa cố định để tránh deadlock trên Java 21.

1 thg 10, 20262 phút1
99 Ngày Java — Ngày 65: Future và CompletableFuture

Callable/Future, thenCompose và thenCombine, xử lý lỗi, timeout và executor rõ ràng trên Java 21.

30 thg 9, 20262 phút0
99 Ngày Spring — Ngày 65: Xử lý bất đồng bộ với @Async

@EnableAsync, executor có giới hạn, CompletableFuture, lỗi của void task và giới hạn proxy/self-invocation.

30 thg 9, 20262 phút0