Giải quyết vấn đề tranh chấp tài nguyên dựa trên kiến trúc message

Tranh chấp tài nguyên là vấn đề lớn cần phải giải quyết trong việc xây dựng hệ thống lớn. Việc tranh chấp tài nguyên ảnh hưởng lớn tới hiệu năng và độ ổn định của hệ thống, tính chính xác của nghiệp vụ. 

Việc tranh chấp rất đa dạng, phụ thuộc rất nhiều vào mô hình nghiệp vụ và cách tổ chức dữ liệu. Nên các giải pháp cũng rất đa dạng và không có giải pháp nào có thể giải quyết triệt để cho mọi trường hợp. 

Bài viết này chỉ trình bày một trong các giải pháp mà nhóm tôi áp dụng trong quá trình xây dựng kiến trúc hệ thống.

Vấn đề: Trong hệ thống giao vận, tất cả các nghiệp vụ đều xoay quanh việc quản lý trạng thái đơn hàng. Từ khi đơn hàng phát sinh đến khi đơn hàng kết thúc, có vô vàn nghiệp vụ làm thay đổi trạng thái đơn hàng. 

Trong kiến trúc hệ thống của chúng tôi, có một module quản lý trung tâm toàn bộ trạng thái đơn hàng. Đây là nơi mà dữ liệu trạng thái từ các luồng nghiệp vụ khác nhau sẽ đổ về một cách bất đồng bộ để cập nhật trạng thái.

Do tính đa dạng và phức tạp của nghiệp vụ, nên việc cập nhật sẽ có nguy cơ gây ra tranh chấp lớn. Trong đó tồn tại nguy cơ xảy ra các tranh chấp chéo dẫn tới deadlock. Các nghiệp vụ này lại được cài đặt rất khác nhau, bởi rất nhiều developer dẫn tới việc kiểm soát ở mức độ lập trình là không thể. 

Nhưng nếu không giải quyết được thì module trung tâm sẽ hoàn toàn bị tê liệt khi lượng đơn hàng tăng cao, hoặc sẽ xử lý vô cùng chậm chạp.

Điều này dẫn tới các đòi hỏi sau:

Cần phải đảm bảo không xảy ra tranh chấp tài nguyên đơn hàng.

Giải pháp phải được triển khai ở mức kiến trúc hệ thống để không lệ thuộc vào việc cài đặt cụ thể.

Giải pháp phải đảm bảo việc cập nhật là chính xác và ổn định Các giải pháp được áp dụng:

1. Phân tách tài nguyên:

Để đảm bảo không xảy ra tranh chấp tài nguyên thì cần phải xác định được loại tài nguyên sẽ xảy ra tranh chấp và tìm cách phân tách các tài nguyên sao cho việc tranh chấp không xảy ra. 

Thông thường với một đơn hàng thì thường bao gồm nhiều loại tài nguyên khác nhau: order, order item… Thực tế, việc cập nhật các loại tài nguyên này thường không được kiểm soát. Cập nhật nhiều loại tài nguyên của nhiều đơn hàng khác nhau trong nhiều nghiệp vụ khác nhau. 

Điều này dẫn tới việc tranh chấp xảy ra lớn.

Giải pháp áp dụng là: quản lý đơn hàng và các thành phần của đơn hàng như là một loại tài nguyên duy nhất – tài nguyên đơn hàng và phân biệt nhau bởi Id đơn hàng. 

Mọi hoạt động thao tác với các thành phần đơn hàng sẽ được coi như là thao tác với một đơn hàng. Nghĩa là không tồn tại một nghiệp vụ nào cập nhật tùy tiện các thành phần khác nhau của 1 đơn hàng đồng thời với việc cập nhật các đơn hàng khác. Một đơn hàng được định danh với Id và sẽ là cơ sở để phân chia việc tranh chấp.

2. Phân tách một quá trình xử lý nhiều đơn hàng thành nhiều quá trình xử lý đơn hàng riêng lẻ.

Việc xác định được phạm vi tài nguyên chỉ là cơ sở ban đầu. Nó không đảm bảo việc tránh tranh chấp chéo khi mà tồn tại nhiều nghiệp vụ khác nhau, mỗi nghiệp vụ cập nhật một số đơn hàng. Chỉ cần việc cập nhật các đơn hàng này không theo thứ tự là có thể xảy ra deadlock.

Giải pháp áp dụng là: sử dụng message queue để chia tách một quá trình xử lý nhiều đơn hàng thành nhiều quá trình xử lý đơn hàng rời rạc. Các lệnh xử lý cho từng đơn hàng sẽ được gửi qua message queue để các consumer nhận và xử lý từng đơn hàng riêng lẻ.

3. Đảm bảo việc xử lý cho từng đơn hàng ổn định và chính xác.

Việc tách rời việc xử lý các đơn hàng chỉ đảm bảo không xảy ra việc tranh chấp giữa các transaction khi cùng cập nhật nhiều đơn hàng trong một transaction. 

Nhưng nó chưa đủ để tránh tranh chấp các xử lý trên cùng một đơn hàng. Khi các lệnh xử lý của một đơn hàng được gửi đi, các consumer sẽ nhận được các message đó gần như song song và xảy ra cuộc tranh giành với chính tài nguyên đơn hàng. 

Điều này dẫn tới nguy cơ lock cao và làm sai lệch thứ tự xử lý các lệnh. Khi các lệnh ra sau nhưng lại được commit trước.

Giải pháp áp dụng là: Đẩy các lệnh xử lý của cùng một đơn hàng về cùng một nơi tại cùng một thời điểm. Điều này đảm bảo là các lệnh của một đơn hàng được xử lý tuần tự. 

Điều này loại bỏ hoàn toàn việc tranh chấp xảy ra trên một đơn hàng. Nhưng không thể tạo ra hàng ngàn queue cho hàng ngàn đơn hàng được. Có một số queue như Kafka và Windows Service Bus cung cấp các feature nâng cao cho phép routing message dưa trên partition key/session id về một nơi. 

Do đó các message của một đơn hàng sẽ được phân cụm để gửi về một nơi xử lý tại cùng một thời điểm.

Kết luận:

Giải quyết tranh chấp là một vấn đề quan trọng, có tính sống còn với đối với hệ thống lớn. Tư tưởng chính các giải pháp vẫn là làm sao phân tách tài nguyên tốt nhất có thể để giảm thiểu tranh chấp tối đa. Ngoài ra còn phải chú ý tới mức độ yêu cầu về tính toàn vẹn của dữ liệu. Như các giải pháp ở trên chỉ phù hợp với mô hình dữ liệu chấp nhận eventually consistent. Với các vấn đề đòi hỏi giải pháp strong consistent thì phải xây dựng các giải pháp lock: optimistic lock, pessimistic lock, key range lock…

#ntechdevelopers


 

Ntech Developers

Programs must be written for people to read, and only incidentally for machines to execute.

Post a Comment

Previous Post Next Post