Không chỉ xử lý: Cách chúng tôi tạo cơ sở dữ liệu phân tán từ Kafka Streams và kết quả của nó

Này Habr!

Chúng tôi nhắc nhở bạn rằng sau cuốn sách về Kafka chúng tôi đã xuất bản một tác phẩm thú vị không kém về thư viện API luồng Kafka.

Không chỉ xử lý: Cách chúng tôi tạo cơ sở dữ liệu phân tán từ Kafka Streams và kết quả của nó

Hiện tại, cộng đồng chỉ đang tìm hiểu các giới hạn của công cụ mạnh mẽ này. Vì vậy, một bài báo đã được xuất bản gần đây, bản dịch mà chúng tôi muốn giới thiệu với bạn. Từ kinh nghiệm của bản thân, tác giả cho biết cách biến Luồng Kafka thành nơi lưu trữ dữ liệu phân tán. Thích đọc sách!

thư viện Apache Suối Kafka được sử dụng trên toàn thế giới trong các doanh nghiệp để xử lý luồng phân tán trên Apache Kafka. Một trong những khía cạnh được đánh giá thấp của khung này là nó cho phép bạn lưu trữ trạng thái cục bộ được tạo ra dựa trên xử lý luồng.

Trong bài viết này, tôi sẽ cho bạn biết cách công ty chúng tôi tận dụng cơ hội này một cách có lợi khi phát triển một sản phẩm bảo mật ứng dụng đám mây. Bằng cách sử dụng Kafka Streams, chúng tôi đã tạo ra các vi dịch vụ trạng thái dùng chung, mỗi vi dịch vụ đóng vai trò là nguồn thông tin đáng tin cậy có khả năng chịu lỗi và có tính sẵn sàng cao về trạng thái của các đối tượng trong hệ thống. Đối với chúng tôi, đây là một bước tiến cả về độ tin cậy và tính dễ hỗ trợ.

Nếu bạn quan tâm đến một cách tiếp cận khác cho phép bạn sử dụng một cơ sở dữ liệu trung tâm duy nhất để hỗ trợ trạng thái chính thức của các đối tượng của mình, hãy đọc nó, nó sẽ rất thú vị...

Tại sao chúng tôi nghĩ rằng đã đến lúc thay đổi cách chúng tôi làm việc với trạng thái dùng chung

Chúng tôi cần duy trì trạng thái của nhiều đối tượng khác nhau dựa trên báo cáo của đại lý (ví dụ: trang web có bị tấn công không)? Trước khi chuyển sang Kafka Streams, chúng tôi thường dựa vào một cơ sở dữ liệu trung tâm duy nhất (+ API dịch vụ) để quản lý trạng thái. Cách tiếp cận này có nhược điểm của nó: tình huống hẹn hò chuyên sâu duy trì tính nhất quán và đồng bộ hóa trở thành một thách thức thực sự. Cơ sở dữ liệu có thể trở thành nút cổ chai hoặc rơi vào tình trạng điều kiện của cuộc đua và phải chịu đựng những điều không thể đoán trước.

Không chỉ xử lý: Cách chúng tôi tạo cơ sở dữ liệu phân tán từ Kafka Streams và kết quả của nó

Hình 1: Một kịch bản trạng thái phân chia điển hình được thấy trước khi chuyển sang
Luồng Kafka và Kafka: các tác nhân truyền đạt quan điểm của họ thông qua API, trạng thái cập nhật được tính toán thông qua cơ sở dữ liệu trung tâm

Gặp gỡ Kafka Streams, giúp dễ dàng tạo các vi dịch vụ trạng thái dùng chung

Khoảng một năm trước, chúng tôi đã quyết định xem xét kỹ lưỡng các kịch bản trạng thái chung của chúng tôi để giải quyết những vấn đề này. Chúng tôi ngay lập tức quyết định dùng thử Kafka Streams - chúng tôi biết khả năng mở rộng, tính khả dụng cao và khả năng chịu lỗi của nó cũng như chức năng phát trực tuyến của nó phong phú đến mức nào (các chuyển đổi, bao gồm cả các chuyển đổi có trạng thái). Đúng những gì chúng tôi cần, chưa kể hệ thống nhắn tin ở Kafka đã trưởng thành và đáng tin cậy như thế nào.

Mỗi vi dịch vụ có trạng thái mà chúng tôi tạo ra đều được xây dựng dựa trên phiên bản Kafka Streams với cấu trúc liên kết khá đơn giản. Nó bao gồm 1) nguồn 2) bộ xử lý có kho lưu trữ khóa-giá trị liên tục 3) phần chìm:

Không chỉ xử lý: Cách chúng tôi tạo cơ sở dữ liệu phân tán từ Kafka Streams và kết quả của nó

Hình 2: Cấu trúc liên kết mặc định của các phiên bản phát trực tuyến của chúng tôi dành cho các dịch vụ vi mô có trạng thái. Lưu ý rằng ở đây cũng có một kho chứa siêu dữ liệu lập kế hoạch.

Trong cách tiếp cận mới này, các tổng đài viên soạn thư được đưa vào chủ đề nguồn và người tiêu dùng—chẳng hạn như dịch vụ thông báo thư—nhận trạng thái chia sẻ đã được tính toán thông qua hệ thống thu thập (chủ đề đầu ra).

Không chỉ xử lý: Cách chúng tôi tạo cơ sở dữ liệu phân tán từ Kafka Streams và kết quả của nó

Hình 3: Luồng tác vụ mẫu mới cho một kịch bản có vi dịch vụ dùng chung: 1) tác nhân tạo thông báo đến chủ đề nguồn Kafka; 2) một dịch vụ vi mô có trạng thái chia sẻ (sử dụng Luồng Kafka) xử lý nó và ghi trạng thái được tính toán vào chủ đề Kafka cuối cùng; sau đó 3) người tiêu dùng chấp nhận trạng thái mới

Này, kho lưu trữ khóa-giá trị tích hợp này thực sự rất hữu ích!

Như đã đề cập ở trên, cấu trúc liên kết trạng thái chia sẻ của chúng tôi chứa một kho lưu trữ khóa-giá trị. Chúng tôi đã tìm thấy một số tùy chọn để sử dụng nó và hai trong số đó được mô tả bên dưới.

Tùy chọn số 1: Sử dụng kho lưu trữ khóa-giá trị để tính toán

Kho lưu trữ khóa-giá trị đầu tiên của chúng tôi chứa dữ liệu phụ trợ mà chúng tôi cần để tính toán. Ví dụ, trong một số trường hợp, trạng thái chia sẻ được xác định theo nguyên tắc "đa số phiếu". Kho lưu trữ có thể chứa tất cả các báo cáo mới nhất của tác nhân về trạng thái của một số đối tượng. Sau đó, khi chúng tôi nhận được báo cáo mới từ tác nhân này hay tác nhân khác, chúng tôi có thể lưu báo cáo đó, truy xuất báo cáo từ tất cả các tác nhân khác về trạng thái của cùng một đối tượng từ bộ lưu trữ và lặp lại phép tính.
Hình 4 bên dưới cho thấy cách chúng tôi hiển thị kho khóa/giá trị cho phương thức xử lý của bộ xử lý để sau đó có thể xử lý thông báo mới.

Không chỉ xử lý: Cách chúng tôi tạo cơ sở dữ liệu phân tán từ Kafka Streams và kết quả của nó

Hình minh họa 4: Chúng ta mở quyền truy cập vào kho khóa-giá trị cho phương thức xử lý của bộ xử lý (sau này, mọi tập lệnh hoạt động với trạng thái chia sẻ đều phải triển khai phương thức doProcess)

Tùy chọn # 2: Tạo API CRUD trên đầu Luồng Kafka

Sau khi thiết lập luồng nhiệm vụ cơ bản, chúng tôi bắt đầu thử viết API RESTful CRUD cho các dịch vụ vi mô trạng thái được chia sẻ của mình. Chúng tôi muốn có thể truy xuất trạng thái của một số hoặc tất cả các đối tượng, cũng như đặt hoặc xóa trạng thái của một đối tượng (hữu ích cho việc hỗ trợ phụ trợ).

Để hỗ trợ tất cả API Get State, bất cứ khi nào chúng tôi cần tính toán lại trạng thái trong quá trình xử lý, chúng tôi sẽ lưu trữ trạng thái đó trong một kho lưu trữ khóa-giá trị tích hợp sẵn trong một thời gian dài. Trong trường hợp này, việc triển khai API như vậy bằng cách sử dụng một phiên bản duy nhất của Luồng Kafka trở nên khá đơn giản, như trong danh sách bên dưới:

Không chỉ xử lý: Cách chúng tôi tạo cơ sở dữ liệu phân tán từ Kafka Streams và kết quả của nó

Hình 5: Sử dụng kho lưu trữ khóa-giá trị tích hợp để lấy trạng thái được tính toán trước của một đối tượng

Việc cập nhật trạng thái của một đối tượng thông qua API cũng dễ dàng thực hiện. Về cơ bản, tất cả những gì bạn cần làm là tạo một nhà sản xuất Kafka và sử dụng nó để tạo một bản ghi chứa trạng thái mới. Điều này đảm bảo rằng tất cả các thông báo được tạo thông qua API sẽ được xử lý giống như những thông báo nhận được từ các nhà sản xuất khác (ví dụ: đại lý).

Không chỉ xử lý: Cách chúng tôi tạo cơ sở dữ liệu phân tán từ Kafka Streams và kết quả của nó

Hình 6: Bạn có thể đặt trạng thái của một đối tượng bằng trình sản xuất Kafka

Sự phức tạp nhỏ: Kafka có nhiều phân vùng

Tiếp theo, chúng tôi muốn phân phối tải xử lý và cải thiện tính khả dụng bằng cách cung cấp một cụm vi dịch vụ ở trạng thái chia sẻ cho mỗi kịch bản. Việc thiết lập thật dễ dàng: khi chúng tôi đã định cấu hình tất cả các phiên bản để chạy trong cùng một ID ứng dụng (và cùng một máy chủ khởi động), hầu hết mọi thứ khác đều được thực hiện tự động. Chúng tôi cũng chỉ định rằng mỗi chủ đề nguồn sẽ bao gồm một số phân vùng, do đó mỗi phiên bản có thể được chỉ định một tập hợp con của các phân vùng đó.

Tôi cũng sẽ đề cập rằng thông lệ là tạo một bản sao lưu của kho lưu trữ trạng thái để chẳng hạn như trong trường hợp khôi phục sau lỗi, hãy chuyển bản sao này sang một phiên bản khác. Đối với mỗi cửa hàng trạng thái trong Luồng Kafka, một chủ đề sao chép sẽ được tạo bằng nhật ký thay đổi (theo dõi các bản cập nhật cục bộ). Vì vậy, Kafka liên tục sao lưu kho lưu trữ nhà nước. Do đó, trong trường hợp xảy ra lỗi ở một hoặc một phiên bản Kafka Streams khác, kho lưu trữ trạng thái có thể được khôi phục nhanh chóng trên một phiên bản khác, nơi các phân vùng tương ứng sẽ được chuyển đến. Các thử nghiệm của chúng tôi đã chỉ ra rằng việc này được thực hiện chỉ trong vài giây, ngay cả khi có hàng triệu bản ghi trong cửa hàng.

Việc chuyển từ một vi dịch vụ duy nhất có trạng thái được chia sẻ sang một cụm vi dịch vụ, việc triển khai API Get State trở nên dễ dàng hơn. Trong tình huống mới, kho lưu trữ trạng thái của từng vi dịch vụ chỉ chứa một phần của bức tranh tổng thể (những đối tượng có khóa được ánh xạ tới một phân vùng cụ thể). Chúng tôi phải xác định phiên bản nào chứa trạng thái của đối tượng chúng tôi cần và chúng tôi đã thực hiện việc này dựa trên siêu dữ liệu của luồng, như được hiển thị bên dưới:

Không chỉ xử lý: Cách chúng tôi tạo cơ sở dữ liệu phân tán từ Kafka Streams và kết quả của nó

Hình 7: Sử dụng siêu dữ liệu luồng, chúng tôi xác định phiên bản nào sẽ truy vấn trạng thái của đối tượng mong muốn; một cách tiếp cận tương tự đã được sử dụng với API GET ALL

Những phát hiện chính

Các cửa hàng trạng thái trong Luồng Kafka có thể đóng vai trò là cơ sở dữ liệu phân tán trên thực tế,

  • liên tục được nhân rộng ở Kafka
  • API CRUD có thể dễ dàng được xây dựng trên hệ thống như vậy
  • Xử lý nhiều phân vùng phức tạp hơn một chút
  • Cũng có thể thêm một hoặc nhiều kho lưu trữ trạng thái vào cấu trúc liên kết phát trực tuyến để lưu trữ dữ liệu phụ trợ. Tùy chọn này có thể được sử dụng cho:
  • Lưu trữ lâu dài dữ liệu cần thiết cho việc tính toán trong quá trình xử lý luồng
  • Lưu trữ dữ liệu dài hạn có thể hữu ích vào lần tiếp theo phiên bản phát trực tuyến được cung cấp
  • nhiều hơn nữa...

Những ưu điểm này và những ưu điểm khác làm cho Luồng Kafka rất phù hợp để duy trì trạng thái toàn cầu trong một hệ thống phân tán như của chúng tôi. Kafka Streams đã được chứng minh là rất đáng tin cậy trong quá trình sản xuất (chúng tôi hầu như không bị mất tin nhắn kể từ khi triển khai nó) và chúng tôi tin tưởng rằng khả năng của nó sẽ không dừng lại ở đó!

Nguồn: www.habr.com

Thêm một lời nhận xét