Bulkhead pattern

Dương TiếnDương Tiến
5 min read

Bulkhead pattern

Bulkhead pattern là mẫu thiết kế nhằm tăng cường độ bền bỉ của hệ thống bằng cách ngăn chặn sự cố từ một phần của hệ thống lan rộng ra toàn bộ hệ thống. Nó hoạt động tương tự như các khoang ngăn trong tàu thủy. Nếu một khoang bị rò rỉ thì chỉ khoang đó bị ngập nước, ngăn chặn nước tràn vào toàn bộ tàu.

Cơ chế hoạt động

Bulkhead pattern chia hệ thống thành các phần biệt lập (bulkheads) để mỗi phần có thể hoạt động độc lập. Nếu một phần bị lỗi hoặc quá tải, các phần khác vẫn có thể hoạt động bình thường. Điều này giúp ngăn ngừa hiện tượng “domino” trong hệ thống, nơi một sự cố có thể gây ra sự cố lan rộng. Cơ chế hoạt động của 2 cách triển khai chính:

1. Cách ly tài nguyên theo nhóm dịch vụ

  • Mỗi nhóm dịch vụ (service group) được cấp phát một lượng tài nguyên riêng biệt (thread pool, kết nối DB, bộ nhớ, v.v.).

  • Ví dụ: Trong một ứng dụng đặt vé máy bay, hệ thống có thể cách ly tài nguyên giữa:

    • Booking Service (dịch vụ đặt vé)

    • Payment Service (dịch vụ thanh toán)

    • Notification Service (dịch vụ gửi thông báo)

  • Nếu Booking Service bị quá tải, Payment Service vẫn hoạt động bình thường.

2. Cách ly theo loại yêu cầu (request type)

  • Tách biệt tài nguyên giữa các loại request quan trọngkhông quan trọng.

  • Ví dụ:

    • Request ưu tiên cao: Đặt hàng, thanh toán

    • Request ưu tiên thấp: Thống kê, báo cáo

  • Khi request ưu tiên thấp bị quá tải, các request quan trọng vẫn được xử lý.

Trường hợp sử dụng

Bulkhead Pattern phù hợp trong các tình huống sau:

  • Ngăn chặn hiệu ứng domino khi một service bị lỗi

  • Tăng độ tin cậy cho hệ thống bằng cách bảo vệ các phần quan trọng

  • Duy trì hiệu suất khi có lượng request tăng đột biến

Ví dụ thực tế

  • Dịch vụ ngân hàng: Tách biệt hệ thống xử lý giao dịch quan trọng với hệ thống báo cáo.

  • Thương mại điện tử: Bảo vệ hệ thống thanh toán khỏi bị ảnh hưởng bởi hệ thống gợi ý sản phẩm.

  • Hệ thống API Gateway: Chạy các thread pool riêng biệt cho từng nhóm API để tránh nghẽn toàn hệ thống.

Cách triển khai

1. SemaphoreBulkhead

Sử dụng cơ chế semaphore để giới hạn số lượng request đồng thời có thể truy cập vào một tài nguyên cụ thể. Phù hợp với các tác vụ nhanh chóng và đồng bộ.

Đặc điểm chính

  • Giới hạn cuộc gọi đồng thời: Giới hạn số lượng request đồng thời tối đa.

  • Sử dụng cho tác vụ nhanh và đồng bộ: Thích hợp cho các tác vụ ngắn gọn và không cần chờ đợi lâu.

  • Ít chi phí tài nguyên: Không cần quản lý luồng riêng.

Thông số cấu hình

maxConcurrentCalls

- Loại: Integer

- Mặc định: 25

- Ý nghĩa: Số lượng cuộc gọi đồng thời tối đa mà SemaphoreBulkhead cho phép.

maxWaitDuration

- Loại: Duration

- Mặc định: 0ms

- Ý nghĩa: Thời gian tối đa để chờ được xử lý. Nếu không có thread nào rảnh rỗi trong thời gian này, request sẽ bị từ chối.

2. ThreadPoolBulkhead

Sử dụng một thread pool để cô lập và quản lý các request, đảm bảo rằng các request không làm quá tải hệ thống chính. Phù hợp với các tác vụ tốn thời gian hoặc cần thực hiện không đồng bộ.

Đặc điểm chính

  • Giới hạn cuộc gọi bằng thread pool: Sử dụng một thread pool để quản lý các request.

  • Thích hợp cho tác vụ tốn thời gian và không đồng bộ: Thích hợp cho các tác vụ có thể tốn thời gian và cần thực hiện không đồng bộ.

  • Kiểm soát tài nguyên tốt hơn: Cung cấp kiểm soát tốt hơn đối với tài nguyên hệ thống bằng cách giới hạn số lượng luồng và kích thước hàng đợi.

Thông số cấu hình

coreThreadPoolSize

- Loại: Integer

- Mặc định: 1

- Ý nghĩa: Số lượng luồng tối thiểu trong ThreadPoolBulkhead.

maxThreadPoolSize

- Loại: Integer

- Mặc định: 10

- Ý nghĩa: Số lượng luồng tối đa trong ThreadPoolBulkhead.

queueCapacity

- Loại: Integer

- Mặc định: 100

- Ý nghĩa: Kích thước của hàng đợi trong ThreadPoolBulkhead. Nếu maxThreadPoolSize đạt giới hạn thì các request sẽ được đẩy vào queue.

keepAliveDuration

- Loại: Duration

- Mặc định: 20ms

- Ý nghĩa: Thời gian mà một luồng vượt quá số lượng luồng lõi sẽ được giữ sống trước khi bị chấm dứt.

Ví dụ

Cấu Hình Trong application.yml
resilience4j:
  bulkhead:
    instances:
      # semaphoreBulkhead
      semaphoreBulkhead:
        maxConcurrentCalls: 20
        maxWaitDuration: 500ms
      # threadPoolBulkhead
      threadPoolBulkhead:
        coreThreadPoolSize: 5
        maxThreadPoolSize: 10
        queueCapacity: 50
        keepAliveDuration: 500ms
Cấu Hình Trong Java

Sử Dụng SemaphoreBulkhead

import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import org.springframework.stereotype.Service;

@Service
public class SemaphoreBulkheadService {

    @Bulkhead(name = "semaphoreBulkhead", type = Bulkhead.Type.SEMAPHORE)
    public String process() {
        return "Processing with SemaphoreBulkhead...";
    }
}

Sử Dụng ThreadPoolBulkhead

import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import org.springframework.stereotype.Service;

@Service
public class ThreadPoolBulkheadService {

    @Bulkhead(name = "threadPoolBulkhead", type = Bulkhead.Type.THREADPOOL)
    public String process() {
        return "Processing with ThreadPoolBulkhead...";
    }
}
0
Subscribe to my newsletter

Read articles from Dương Tiến directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Dương Tiến
Dương Tiến