0
0
Lập trình
NM

Tính Đ idempotency trong API: Tại sao hệ thống của bạn cần điều này?

Đăng vào 3 tuần trước

• 8 phút đọc

Tính Đ idempotency trong API: Tại sao hệ thống của bạn cần điều này?

Trong bất kỳ hệ thống nào, có những quy trình không thể bị trùng lặp và hệ thống phải có khả năng chịu đựng các yêu cầu trùng lặp. Việc phòng ngừa vấn đề là tốt hơn so với việc sửa chữa chúng sau này.

Các vấn đề thường gặp

Khi một yêu cầu không thành công, khách hàng thường sẽ thử lại. Hệ thống của bạn có thể xử lý các yêu cầu này nhiều lần, dẫn đến việc dữ liệu bị hỏng.

Ví dụ:

  • Khách hàng: "Thêm 100 đơn vị vào kho"
  • Máy chủ: Thêm vào kho
  • Khách hàng: Mạng gặp lỗi, do đó thử lại
  • Máy chủ: Thêm 100 đơn vị... hai lần
  • Kết quả: 200 đơn vị thay vì 100 ❌

Giải pháp: Tiêu đề Idempotency

Để giải quyết vấn đề này, bạn cần sử dụng tiêu đề idempotency trong các yêu cầu API của mình.

Trách nhiệm của Khách hàng:

  • Tạo khóa idempotency duy nhất cho mỗi yêu cầu.
  • Sử dụng lại cùng một khóa idempotency cho bất kỳ lần thử lại nào.

Trách nhiệm của Máy chủ:

  • Nếu khóa idempotency được cung cấp, lưu trữ các phản hồi thành công. Chỉ lưu các phản hồi 2XX vì chúng là những phản hồi cập nhật dữ liệu chính của cơ sở dữ liệu. Các phản hồi 4XX, 5XX không thay đổi cơ sở dữ liệu.
  • Nếu không có khóa idempotency, xử lý như bình thường, vì đó là trách nhiệm của khách hàng để thêm idempotency cho các hoạt động quan trọng.
  • Không lưu trữ dữ liệu idempotency cho các API HTTP GET vì chúng chỉ truy xuất dữ liệu và không cập nhật dữ liệu nào.
  • Dựa trên nhu cầu, thêm một công việc theo lịch để loại bỏ dữ liệu idempotency, ví dụ: Xóa các bản ghi cũ hơn 7 ngày.

Thiết kế cơ sở dữ liệu mẫu (Postgres) để lưu trữ phản hồi:

Cột Kiểu Có thể null Mặc định
id int8 (Khóa chính) False Tự động Tăng
idempotency_key VARCHAR(50) False NA
http_status_code VARCHAR(50) True NA
http_response TEXT True NA
created_at TIMESTAMP False CURRENT_TIMESTAMP

Triển khai mẫu (API):

Interceptor

java Copy
@Component
class SimpleIdempotencyInterceptor implements HandlerInterceptor {

    @Autowired
    private final IdempotencyDataRepositoryImpl idempotencyRepository;

    @Override
    public boolean preHandle(
        HttpServletRequest request,
        HttpServletResponse response,
        Object handler
    ) throws Exception {
        if (request.getMethod().equals(HttpMethod.GET.name()) || !isPathEnabled(request)) {
            return true;
        }

        String idempotencyKey = request.getHeader("myapp-idempotency-key");
        if (idempotencyKey != null) {
            return handleIdempotency(idempotencyKey, response);
        }

        return true;
    }

    @Override
    public void afterCompletion(
        HttpServletRequest request,
        HttpServletResponse response,
        Object handler,
        Exception ex
    ) throws Exception {
        String idempotencyKey = request.getHeader("myapp-idempotency-key");
        if (idempotencyKey != null && response.getStatus() >= 200 && response.getStatus() <= 299) {
            cacheResponse(idempotencyKey, response);
        }
    }

    private boolean handleIdempotency(String idempotencyKey, HttpServletResponse response) {
        IdempotencyDataEntity existing = idempotencyRepository.findByIdempotencyKey(idempotencyKey);

        switch (existing == null ? "null" : "exists") {
            case "null":
                idempotencyRepository.save(
                    new IdempotencyDataEntity(
                        idempotencyKey,
                        0,
                        ""
                    )
                );
                return true;
            default:
                sendCachedResponse(response, existing);
                return false;
        }
    }

    private void cacheResponse(String idempotencyKey, HttpServletResponse response) {
        try {
            IdempotencyDataEntity existing = idempotencyRepository.findByIdempotencyKey(idempotencyKey);
            if (existing != null) {
                String responseBody = "/* extracted response body */";
                idempotencyRepository.save(
                    new IdempotencyDataEntity(
                        idempotencyKey,
                        response.getStatus(),
                        responseBody
                    )
                );
            }
        } catch (Exception e) {
            log.warn("Failed to cache idempotency response for key: " + idempotencyKey, e);
        }
    }
}

Lịch trình để dọn dẹp dữ liệu Idempotency cũ:

java Copy
/**
     * Dọn dẹp các bản ghi idempotency cũ hơn 7 ngày
     * Chạy hàng ngày lúc 2 giờ sáng
     */
    @Scheduled(cron = "0 0 2 * * *")
    public void cleanupOldIdempotencyRecords() {
        try {
            LocalDateTime cutoffDate = LocalDateTime.now().minusDays(7);
            int deletedCount = idempotencyRepository.deleteByCreatedDateBefore(cutoffDate);
            logger.info("Đã dọn dẹp {} bản ghi idempotency cũ (cũ hơn {})", deletedCount, cutoffDate);
        } catch (Exception e) {
            logger.error("Dọn dẹp bản ghi idempotency cũ không thành công", e);
        }
    }

Thực tiễn tốt nhất

  • Luôn luôn kiểm tra và đảm bảo rằng khóa idempotency được cung cấp cho các yêu cầu quan trọng.
  • Thực hiện lưu trữ dữ liệu idempotency một cách an toàn để tránh mất mát dữ liệu.

Những cạm bẫy phổ biến

  • Không lưu trữ khóa idempotency cho các yêu cầu GET.
  • Quá trình dọn dẹp không đúng cách có thể dẫn đến việc mất dữ liệu quan trọng.

Mẹo hiệu suất

  • Tối ưu hóa việc lưu trữ và truy vấn dữ liệu idempotency để cải thiện hiệu suất hệ thống.

Giải quyết vấn đề

  • Nếu gặp lỗi trong quá trình xử lý yêu cầu, hãy kiểm tra lại cách quản lý khóa idempotency.

Kết luận

Tính idempotency là một khía cạnh quan trọng cần xem xét trong thiết kế API. Việc thực hiện nó đúng cách giúp hệ thống của bạn trở nên mạnh mẽ hơn, giảm thiểu rủi ro và đảm bảo tính toàn vẹn của dữ liệu. Hãy bắt đầu triển khai tính năng này ngay hôm nay để bảo vệ hệ thống của bạn khỏi các vấn đề tiềm ẩn.

Câu hỏi thường gặp

  1. Idempotency là gì?
    Idempotency là tính chất của một yêu cầu, cho phép nó được thực hiện nhiều lần mà không thay đổi trạng thái của hệ thống.
  2. Tại sao idempotency lại quan trọng trong API?
    Nó giúp đảm bảo rằng các yêu cầu giống nhau không làm hỏng dữ liệu và giữ cho hệ thống ổn định.
  3. Làm thế nào để triển khai idempotency trong API?
    Bằng cách tạo một khóa idempotency duy nhất cho mỗi yêu cầu và lưu trữ phản hồi tương ứng.

Hãy thực hiện các bước cần thiết để đảm bảo rằng hệ thống của bạn luôn hoạt động hiệu quả và đáng tin cậy.

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào