Fallback pattern
Fallback pattern
Fallback pattern
là mẫu thiết kế được sử dụng để cung cấp một giải pháp thay thế/dự phòng khi phương thức xử lý chính gặp lỗi nhằm đảm bảo tính sẵn sàng (availability).
Cơ chế hoạt động
Phương thức xử lý chính được gọi, trong quá trình xử lý nếu có lỗi xảy ra thì sẽ có một xử lý dự phòng cho phương thức chính khi gặp lỗi.
Primary Request: Thử gọi phương thức xử lý chính (nếu thành công, trả về kết quả).
Failure Handling: Nếu phương thức xử lý chính bị lỗi, hệ thống chuyển sang phương án dự phòng (fallback).
Fallback Options:
Trả về dữ liệu cache (nếu có).
Trả về dữ liệu mặc định.
Chuyển hướng sang một phương thức xử lý khác.
Gửi thông báo lỗi có kiểm soát thay vì để hệ thống bị crash.
Trường hợp sử dụng
Khi một service bị lỗi hoặc không thể phản hồi kịp thời.
Khi muốn cung cấp dữ liệu dự phòng thay vì hiển thị lỗi.
Khi cần duy trì trải nghiệm người dùng ổn định, ngay cả khi có sự cố.
Ví dụ:
Gợi ý sản phẩm: Nếu service recommendation gặp lỗi, hiển thị danh sách sản phẩm mặc định.
Cách triển khai
1. Sử dụng với Circuit Breaker
Ví dụ nếu sử dụng Spring Boot hay Spring Framework thì có thể dễ dàng triển khai fallback pattern bằng cách sử dụng Resilience4j
hoặc Hystrix
(hiện tại Hystrix đã bị đánh dấu là deprecated)
Ví dụ:
import org.springframework.stereotype.Service;
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.annotation.FallbackMethod;
@Service
public class PaymentService {
private static final String SERVICE_NAME = "paymentService";
@CircuitBreaker(name = SERVICE_NAME, fallbackMethod = "fallbackPayment")
public String processPayment() {
// Gọi API bên ngoài hoặc service khác
throw new RuntimeException("Payment service unavailable"); // Giả lập lỗi
}
public String fallbackPayment(Exception e) {
return "Fallback payment: Hệ thống đang bận, vui lòng thử lại sau!";
}
}
2. Sử dụng với Spring Retry hoặc resilience4j.retry
Ngoài fallback, ta có thể kết hợp với Retry để thử lại một vài lần trước khi fallback.
Ví dụ:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.client.HttpServerErrorException;
@Service
public class RetryService {
@Autowired
private RestTemplate restTemplate;
@Retryable(value = {HttpServerErrorException.class}, maxAttempts = 3)
public String callService() {
String url = "http://external-service/endpoint";
return restTemplate.getForObject(url, String.class);
}
@Recover
public String fallback(HttpServerErrorException e) {
return "Service is currently unavailable. Please try again later.";
}
}
Ví dụ:
import io.github.resilience4j.retry.annotation.Retry;
@Retry(name = SERVICE_NAME, fallbackMethod = "fallbackPayment")
public String processPayment() {
// Thử gọi API, nếu thất bại thì retry trước khi fallback
}
3. Sử dụng với try-catch
Một cách tiếp cận đơn giản là sử dụng try-catch để xử lý lỗi và cung cấp fallback.
Ví dụ:
@Service
public class SimpleService {
@Autowired
private RestTemplate restTemplate;
public String callService() {
String url = "http://external-service/endpoint";
try {
return restTemplate.getForObject(url, String.class);
} catch (RestClientException e) {
return fallback(e);
}
}
private String fallback(Throwable throwable) {
return "Service is currently unavailable. Please try again later.";
}
}
4. Sử dụng với Feign Client
Feign là một HTTP client cho microservices và có thể sử dụng với Resilience4j để cung cấp fallback.
Ví dụ:
@FeignClient(name = "external-service", fallbackFactory = ExternalServiceFallbackFactory.class)
public interface ExternalServiceClient {
@GetMapping("/endpoint")
String getEndpoint();
}
@Component
public class ExternalServiceFallbackFactory implements FallbackFactory<ExternalServiceClient> {
@Override
public ExternalServiceClient create(Throwable cause) {
return new ExternalServiceClient() {
@Override
public String getEndpoint() {
return "Service is currently unavailable. Please try again later.";
}
};
}
}