Hiểu các nhà môi giới tin nhắn. Tìm hiểu cơ chế nhắn tin với ActiveMQ và Kafka. Chương 3. Kafka

Tiếp tục dịch một cuốn sách nhỏ:
Hiểu các nhà môi giới tin nhắn
tác giả: Jakub Korab, nhà xuất bản: O'Reilly Media, Inc., ngày xuất bản: tháng 2017 năm 9781492049296, ISBN: XNUMX.

Phần dịch trước: Hiểu các nhà môi giới tin nhắn. Tìm hiểu cơ chế nhắn tin với ActiveMQ và Kafka. Chương 1 Giới thiệu

CHƯƠNG 3

Kafka

Kafka được LinkedIn phát triển để khắc phục một số hạn chế của trình trung chuyển tin nhắn truyền thống và tránh phải thiết lập nhiều trình trung gian tin nhắn cho các tương tác điểm-điểm khác nhau, điều này được mô tả trong cuốn sách này ở phần "Mở rộng và mở rộng" trên trang 28 . Các trường hợp sử dụng LinkedIn chủ yếu dựa vào việc nhập một chiều lượng dữ liệu rất lớn, chẳng hạn như số lần nhấp vào trang và nhật ký truy cập, trong khi vẫn cho phép nhiều hệ thống sử dụng dữ liệu đó mà không ảnh hưởng đến năng suất của nhà sản xuất hoặc người tiêu dùng khác. Trên thực tế, lý do Kafka tồn tại là để có được loại kiến ​​trúc nhắn tin mà Đường ống dữ liệu chung mô tả.

Với mục tiêu cuối cùng này, các yêu cầu khác tự nhiên nảy sinh. Kafka nên:

  • Hãy cực kỳ nhanh chóng
  • Cung cấp thêm băng thông khi làm việc với tin nhắn
  • Hỗ trợ các mô hình Publisher-Subscriber và Point-to-Point
  • Đừng chậm lại với việc thêm người tiêu dùng. Ví dụ: hiệu suất của cả hàng đợi và chủ đề trong ActiveMQ đều giảm khi số lượng người tiêu dùng trên đích tăng lên.
  • Có thể mở rộng theo chiều ngang; nếu một nhà môi giới duy trì thông báo chỉ có thể làm như vậy ở tốc độ đĩa tối đa, thì việc vượt qua một phiên bản nhà môi giới duy nhất để tăng hiệu suất là điều hợp lý
  • Giới hạn quyền truy cập để lưu trữ và truy xuất lại tin nhắn

Để đạt được tất cả những điều này, Kafka đã áp dụng một kiến ​​trúc xác định lại vai trò và trách nhiệm của khách hàng và nhà môi giới nhắn tin. Mô hình JMS rất hướng đến người môi giới, trong đó người môi giới chịu trách nhiệm phân phối tin nhắn và khách hàng chỉ phải lo lắng về việc gửi và nhận tin nhắn. Mặt khác, Kafka lấy khách hàng làm trung tâm, trong đó khách hàng đảm nhận nhiều tính năng của một nhà môi giới truyền thống, chẳng hạn như phân phối công bằng các thông điệp có liên quan tới người tiêu dùng, để đổi lấy một nhà môi giới cực kỳ nhanh và có khả năng mở rộng. Đối với những người đã làm việc với các hệ thống nhắn tin truyền thống, làm việc với Kafka đòi hỏi một sự thay đổi cơ bản trong suy nghĩ.
Hướng kỹ thuật này đã dẫn đến việc tạo ra một cơ sở hạ tầng nhắn tin có khả năng tăng thông lượng theo nhiều bậc độ lớn so với một nhà môi giới thông thường. Như chúng ta sẽ thấy, cách tiếp cận này đi kèm với sự đánh đổi, nghĩa là Kafka không phù hợp với một số loại khối lượng công việc và phần mềm đã cài đặt.

Mô hình điểm đến thống nhất

Để đáp ứng các yêu cầu được mô tả ở trên, Kafka đã kết hợp xuất bản-đăng ký và gửi tin nhắn điểm tới điểm dưới một loại đích - đề tài. Điều này gây nhầm lẫn cho những người đã từng làm việc với các hệ thống nhắn tin, trong đó từ "chủ đề" đề cập đến một cơ chế phát sóng mà từ đó (từ chủ đề) việc đọc không bền. Các chủ đề Kafka nên được coi là một loại đích kết hợp, như được định nghĩa trong phần giới thiệu của cuốn sách này.

Trong phần còn lại của chương này, trừ khi chúng tôi nói rõ ràng khác đi, thuật ngữ "chủ đề" sẽ đề cập đến một chủ đề Kafka.

Để hiểu đầy đủ cách các chủ đề hoạt động và những đảm bảo mà chúng cung cấp, trước tiên chúng ta cần xem cách chúng được triển khai trong Kafka.
Mỗi chủ đề trong Kafka có nhật ký riêng.
Các nhà sản xuất gửi tin nhắn tới Kafka ghi vào nhật ký này và người tiêu dùng đọc từ nhật ký bằng cách sử dụng các con trỏ liên tục di chuyển về phía trước. Theo định kỳ, Kafka xóa các phần cũ nhất của nhật ký, cho dù các thông báo trong các phần đó đã được đọc hay chưa. Một phần trung tâm trong thiết kế của Kafka là nhà môi giới không quan tâm liệu tin nhắn có được đọc hay không - đó là trách nhiệm của khách hàng.

Thuật ngữ "log" và "con trỏ" không xuất hiện trong tài liệu Kafka. Những thuật ngữ nổi tiếng này được sử dụng ở đây để hỗ trợ sự hiểu biết.

Mô hình này hoàn toàn khác với ActiveMQ, nơi các tin nhắn từ tất cả các hàng đợi được lưu trữ trong cùng một nhật ký và nhà môi giới đánh dấu các tin nhắn là đã xóa sau khi chúng được đọc.
Bây giờ chúng ta hãy tìm hiểu sâu hơn một chút và xem nhật ký chủ đề chi tiết hơn.
Nhật ký Kafka bao gồm một số phân vùng (Hình 3-1). Kafka đảm bảo thứ tự nghiêm ngặt trong mỗi phân vùng. Điều này có nghĩa là các thông báo được ghi vào phân vùng theo một thứ tự nhất định sẽ được đọc theo cùng một thứ tự. Mỗi phân vùng được triển khai dưới dạng tệp nhật ký cuộn có chứa tập hợp con (tập hợp con) của tất cả các tin nhắn được gửi đến chủ đề bởi nhà sản xuất của nó. Theo mặc định, chủ đề đã tạo chứa một phân vùng. Ý tưởng về các phân vùng là ý tưởng trung tâm của Kafka cho việc mở rộng theo chiều ngang.

Hiểu các nhà môi giới tin nhắn. Tìm hiểu cơ chế nhắn tin với ActiveMQ và Kafka. Chương 3. Kafka
Hình 3-1. Phân vùng Kafka

Khi nhà sản xuất gửi tin nhắn đến một chủ đề Kafka, nó sẽ quyết định phân vùng nào sẽ gửi tin nhắn đến. Chúng ta sẽ xem xét điều này chi tiết hơn sau.

đọc tin nhắn

Máy khách muốn đọc tin nhắn sẽ quản lý một con trỏ có tên là nhóm người tiêu dùng, trỏ đến bù lại tin nhắn trong phân vùng. Phần bù là một vị trí gia tăng bắt đầu từ 0 khi bắt đầu phân vùng. Nhóm người tiêu dùng này, được tham chiếu trong API thông qua group_id do người dùng xác định, tương ứng với một người tiêu dùng hợp lý hoặc hệ thống.

Hầu hết các hệ thống nhắn tin đọc dữ liệu từ đích bằng cách sử dụng nhiều phiên bản và luồng để xử lý song song các tin nhắn. Do đó, thường sẽ có nhiều phiên bản người tiêu dùng chia sẻ cùng một nhóm người tiêu dùng.

Bài toán đọc có thể biểu diễn như sau:

  • Chủ đề có nhiều phân vùng
  • Nhiều nhóm người tiêu dùng có thể sử dụng một chủ đề cùng một lúc
  • Một nhóm người tiêu dùng có thể có nhiều trường hợp riêng biệt

Đây là một vấn đề nhiều-nhiều không tầm thường. Để hiểu cách Kafka xử lý các mối quan hệ giữa các nhóm người tiêu dùng, phiên bản người tiêu dùng và phân vùng, hãy xem xét một loạt các kịch bản đọc phức tạp hơn.

Người tiêu dùng và nhóm người tiêu dùng

Hãy bắt đầu một chủ đề với một phân vùng (Hình 3-2).

Hiểu các nhà môi giới tin nhắn. Tìm hiểu cơ chế nhắn tin với ActiveMQ và Kafka. Chương 3. Kafka
Hình 3-2. Người tiêu dùng đọc từ phân vùng

Khi một phiên bản người tiêu dùng kết nối với group_id của chính nó với chủ đề này, nó sẽ được chỉ định một phân vùng đọc và một phần bù trong phân vùng đó. Vị trí của phần bù này được định cấu hình trong máy khách dưới dạng con trỏ tới vị trí gần đây nhất (tin nhắn mới nhất) hoặc vị trí sớm nhất (tin nhắn cũ nhất). Người tiêu dùng yêu cầu (thăm dò ý kiến) tin nhắn từ chủ đề, khiến chúng được đọc tuần tự từ nhật ký.
Vị trí bù đắp thường xuyên được cam kết trở lại Kafka và được lưu trữ dưới dạng tin nhắn trong một chủ đề nội bộ _consumer_offsets. Các tin nhắn đã đọc vẫn không bị xóa, không giống như một nhà môi giới thông thường và khách hàng có thể tua lại phần bù để xử lý lại các tin nhắn đã xem.

Khi người tiêu dùng logic thứ hai kết nối bằng cách sử dụng một group_id khác, nó sẽ quản lý con trỏ thứ hai độc lập với con trỏ thứ nhất (Hình 3-3). Do đó, một chủ đề Kafka hoạt động giống như một hàng đợi trong đó có một người tiêu dùng và giống như một chủ đề đăng ký xuất bản (pub-sub) bình thường mà nhiều người tiêu dùng đăng ký, với lợi ích bổ sung là tất cả các tin nhắn được lưu trữ và có thể được xử lý nhiều lần.

Hiểu các nhà môi giới tin nhắn. Tìm hiểu cơ chế nhắn tin với ActiveMQ và Kafka. Chương 3. Kafka
Hình 3-3. Hai người tiêu dùng trong các nhóm người tiêu dùng khác nhau đọc từ cùng một phân vùng

Người tiêu dùng trong nhóm người tiêu dùng

Khi một phiên bản người tiêu dùng đọc dữ liệu từ một phân vùng, nó có toàn quyền kiểm soát con trỏ và xử lý các thông báo như được mô tả trong phần trước.
Nếu một số phiên bản người tiêu dùng được kết nối với cùng một nhóm_id với một chủ đề bằng một phân vùng, thì phiên bản được kết nối cuối cùng sẽ được cấp quyền kiểm soát con trỏ và từ thời điểm đó trở đi, phiên bản đó sẽ nhận được tất cả thông báo (Hình 3-4).

Hiểu các nhà môi giới tin nhắn. Tìm hiểu cơ chế nhắn tin với ActiveMQ và Kafka. Chương 3. Kafka
Hình 3-4. Hai người tiêu dùng trong cùng một nhóm người tiêu dùng đọc từ cùng một phân vùng

Chế độ xử lý này, trong đó số lượng phiên bản người tiêu dùng vượt quá số lượng phân vùng, có thể được coi là một loại người tiêu dùng độc quyền. Điều này có thể hữu ích nếu bạn cần nhóm các phiên bản người tiêu dùng "chủ động-thụ động" (hoặc "nóng-ấm") mặc dù việc chạy song song nhiều người tiêu dùng ("chủ động-hoạt động" hoặc "nóng-nóng") là điển hình hơn nhiều so với người tiêu dùng.Ở chế độ chờ.

Hành vi phân phối thông báo được mô tả ở trên có thể gây ngạc nhiên so với cách hoạt động của hàng đợi JMS bình thường. Trong mô hình này, các tin nhắn được gửi đến hàng đợi sẽ được phân phối đồng đều giữa hai người tiêu dùng.

Thông thường, khi chúng tôi tạo nhiều phiên bản người tiêu dùng, chúng tôi thực hiện việc này để xử lý thư song song hoặc để tăng tốc độ đọc hoặc để tăng tính ổn định của quá trình đọc. Vì mỗi lần chỉ có một phiên bản người tiêu dùng có thể đọc dữ liệu từ một phân vùng, làm thế nào để đạt được điều này trong Kafka?

Một cách để làm điều này là sử dụng một phiên bản người tiêu dùng duy nhất để đọc tất cả các tin nhắn và chuyển chúng đến nhóm luồng. Mặc dù cách tiếp cận này làm tăng thông lượng xử lý, nhưng nó làm tăng độ phức tạp của logic người tiêu dùng và không làm gì để tăng độ bền của hệ thống đọc. Nếu một bản sao của người tiêu dùng bị hỏng do mất điện hoặc sự kiện tương tự, thì phép trừ sẽ dừng lại.

Cách kinh điển để giải quyết vấn đề này trong Kafka là sử dụng bОnhiều phân vùng hơn.

phân vùng

Các phân vùng là cơ chế chính để song song hóa việc đọc và mở rộng một chủ đề vượt quá băng thông của một phiên bản trình môi giới. Để hiểu rõ hơn về điều này, hãy xem xét tình huống có một chủ đề có hai phân vùng và một người tiêu dùng đăng ký chủ đề này (Hình 3-5).

Hiểu các nhà môi giới tin nhắn. Tìm hiểu cơ chế nhắn tin với ActiveMQ và Kafka. Chương 3. Kafka
Hình 3-5. Một người tiêu dùng đọc từ nhiều phân vùng

Trong trường hợp này, người tiêu dùng được cấp quyền kiểm soát con trỏ tương ứng với group_id của nó trong cả hai phân vùng và bắt đầu đọc thư từ cả hai phân vùng.
Khi một người tiêu dùng bổ sung cho cùng một nhóm_id được thêm vào chủ đề này, Kafka sẽ phân bổ lại một trong các phân vùng từ người tiêu dùng thứ nhất sang người tiêu dùng thứ hai. Sau đó, mỗi phiên bản của người tiêu dùng sẽ đọc từ một phân vùng của chủ đề (Hình 3-6).

Để đảm bảo rằng các thư được xử lý song song trong 20 luồng, bạn cần có ít nhất 20 phân vùng. Nếu có ít phân vùng hơn, bạn sẽ chỉ còn lại những người tiêu dùng không có gì để làm, như đã mô tả trước đó trong cuộc thảo luận về những người tiêu dùng độc quyền.

Hiểu các nhà môi giới tin nhắn. Tìm hiểu cơ chế nhắn tin với ActiveMQ và Kafka. Chương 3. Kafka
Hình 3-6. Hai người tiêu dùng trong cùng một nhóm người tiêu dùng đọc từ các phân vùng khác nhau

Sơ đồ này làm giảm đáng kể độ phức tạp của trình môi giới Kafka so với việc phân phối thông báo cần thiết để duy trì hàng đợi JMS. Ở đây bạn không cần phải lo lắng về những điểm sau:

  • Người tiêu dùng nào sẽ nhận được tin nhắn tiếp theo, dựa trên phân bổ vòng tròn, dung lượng hiện tại của bộ đệm tìm nạp trước hoặc tin nhắn trước đó (như đối với các nhóm tin nhắn JMS).
  • Những tin nhắn nào được gửi đến người tiêu dùng nào và liệu chúng có nên được gửi lại trong trường hợp thất bại hay không.

Tất cả những gì nhà môi giới Kafka phải làm là chuyển các tin nhắn một cách tuần tự cho người tiêu dùng khi người tiêu dùng yêu cầu chúng.

Tuy nhiên, các yêu cầu đối với việc đọc song song và gửi lại các tin nhắn bị lỗi không biến mất - trách nhiệm đối với chúng chỉ đơn giản là chuyển từ nhà môi giới sang khách hàng. Điều này có nghĩa là chúng phải được tính đến trong mã của bạn.

Gửi tin nhắn

Nhà sản xuất tin nhắn đó có trách nhiệm quyết định phân vùng nào sẽ gửi tin nhắn đến. Để hiểu cơ chế mà điều này được thực hiện, trước tiên chúng ta cần xem xét chính xác những gì chúng ta đang thực sự gửi.

Trong khi trong JMS, chúng tôi sử dụng cấu trúc thông báo có siêu dữ liệu (tiêu đề và thuộc tính) và phần thân chứa tải trọng (payload), thì ở Kafka, thông báo là cặp "khóa-giá trị". Tải trọng tin nhắn được gửi dưới dạng một giá trị. Mặt khác, khóa chủ yếu được sử dụng để phân vùng và phải chứa khóa cụ thể logic nghiệp vụđể đặt các tin nhắn liên quan trong cùng một phân vùng.

Trong Chương 2, chúng ta đã thảo luận về kịch bản cá cược trực tuyến trong đó các sự kiện liên quan cần được xử lý theo thứ tự bởi một người tiêu dùng:

  1. Tài khoản người dùng được cấu hình.
  2. Tiền được ghi có vào tài khoản.
  3. Một cược được thực hiện để rút tiền từ tài khoản.

Nếu mỗi sự kiện là một thông báo được đăng lên một chủ đề, thì khóa tự nhiên sẽ là ID tài khoản.
Khi một tin nhắn được gửi bằng API Kafka Producer, nó sẽ được chuyển đến một hàm phân vùng, với tin nhắn và trạng thái hiện tại của cụm Kafka, trả về ID của phân vùng mà tin nhắn sẽ được gửi đến. Tính năng này được thực hiện trong Java thông qua giao diện Partitioner.

Giao diện này trông như thế này:

interface Partitioner {
    int partition(String topic,
        Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster);
}

Việc triển khai Trình phân vùng sử dụng thuật toán băm mục đích chung mặc định trên khóa để xác định phân vùng hoặc quay vòng nếu không có khóa nào được chỉ định. Giá trị mặc định này hoạt động tốt trong hầu hết các trường hợp. Tuy nhiên, trong tương lai bạn sẽ muốn viết của riêng bạn.

Viết chiến lược phân vùng của riêng bạn

Hãy xem một ví dụ mà bạn muốn gửi siêu dữ liệu cùng với nội dung tin nhắn. Tải trọng trong ví dụ của chúng tôi là hướng dẫn gửi tiền vào tài khoản trò chơi. Hướng dẫn là thứ mà chúng tôi muốn được đảm bảo không bị sửa đổi khi truyền và muốn chắc chắn rằng chỉ một hệ thống ngược dòng đáng tin cậy mới có thể bắt đầu hướng dẫn đó. Trong trường hợp này, các hệ thống gửi và nhận đồng ý về việc sử dụng chữ ký để xác thực tin nhắn.
Trong JMS bình thường, chúng tôi chỉ cần xác định thuộc tính "chữ ký thư" và thêm thuộc tính đó vào thư. Tuy nhiên, Kafka không cung cấp cho chúng ta cơ chế truyền siêu dữ liệu, chỉ có khóa và giá trị.

Vì giá trị là một tải trọng chuyển khoản ngân hàng mà chúng tôi muốn duy trì tính toàn vẹn, nên chúng tôi không có lựa chọn nào khác ngoài việc xác định cấu trúc dữ liệu sẽ sử dụng trong khóa. Giả sử rằng chúng tôi cần ID tài khoản để phân vùng, vì tất cả các thông báo liên quan đến tài khoản phải được xử lý theo thứ tự, chúng tôi sẽ đưa ra cấu trúc JSON sau:

{
  "signature": "541661622185851c248b41bf0cea7ad0",
  "accountId": "10007865234"
}

Bởi vì giá trị của chữ ký sẽ thay đổi tùy thuộc vào tải trọng, chiến lược băm mặc định của giao diện Trình phân vùng sẽ không nhóm các thư liên quan một cách đáng tin cậy. Do đó, chúng tôi sẽ cần viết chiến lược của riêng mình để phân tích cú pháp khóa này và phân vùng giá trị accountId.

Kafka bao gồm tổng kiểm tra để phát hiện tham nhũng của thư trong cửa hàng và có đầy đủ các tính năng bảo mật. Mặc dù vậy, các yêu cầu cụ thể của ngành, chẳng hạn như yêu cầu ở trên, đôi khi xuất hiện.

Chiến lược phân vùng của người dùng phải đảm bảo rằng tất cả các thông báo liên quan đều kết thúc trong cùng một phân vùng. Mặc dù điều này có vẻ đơn giản nhưng yêu cầu có thể phức tạp do tầm quan trọng của việc sắp xếp thứ tự các thư liên quan và số lượng phân vùng cố định trong một chủ đề là bao nhiêu.

Số lượng phân vùng trong một chủ đề có thể thay đổi theo thời gian, vì chúng có thể được thêm vào nếu lưu lượng truy cập vượt quá mong đợi ban đầu. Do đó, các khóa thông báo có thể được liên kết với phân vùng ban đầu chúng được gửi đến, ngụ ý một phần trạng thái được chia sẻ giữa các phiên bản của nhà sản xuất.

Một yếu tố khác cần xem xét là sự phân bố đều các thông báo trên các phân vùng. Thông thường, các khóa không được phân phối đồng đều trên các thông báo và các hàm băm không đảm bảo phân phối thông báo công bằng cho một nhóm khóa nhỏ.
Điều quan trọng cần lưu ý là dù bạn chọn tách thư như thế nào, thì bản thân dấu tách có thể cần được sử dụng lại.

Xem xét yêu cầu sao chép dữ liệu giữa các cụm Kafka ở các vị trí địa lý khác nhau. Với mục đích này, Kafka đi kèm với một công cụ dòng lệnh có tên MirrorMaker, được sử dụng để đọc tin nhắn từ một cụm và chuyển chúng sang cụm khác.

MirrorMaker phải hiểu các khóa của chủ đề được sao chép để duy trì thứ tự tương đối giữa các thông báo khi sao chép giữa các cụm, vì số lượng phân vùng cho chủ đề đó có thể không giống nhau trong hai cụm.

Các chiến lược phân vùng tùy chỉnh tương đối hiếm, vì băm mặc định hoặc quay vòng hoạt động tốt trong hầu hết các tình huống. Tuy nhiên, nếu bạn yêu cầu đảm bảo đặt hàng mạnh mẽ hoặc cần trích xuất siêu dữ liệu từ tải trọng, thì việc phân vùng là điều bạn nên xem xét kỹ hơn.

Lợi ích về khả năng mở rộng và hiệu suất của Kafka đến từ việc chuyển một số trách nhiệm của nhà môi giới truyền thống sang khách hàng. Trong trường hợp này, một quyết định được đưa ra để phân phối các thông báo có khả năng liên quan giữa một số người tiêu dùng làm việc song song.

Các nhà môi giới JMS cũng cần phải giải quyết các yêu cầu như vậy. Thật thú vị, cơ chế gửi các tin nhắn có liên quan đến cùng một người tiêu dùng, được triển khai thông qua Nhóm tin nhắn JMS (một biến thể của chiến lược cân bằng tải cố định (SLB)), cũng yêu cầu người gửi đánh dấu các tin nhắn là có liên quan. Trong trường hợp của JMS, nhà môi giới chịu trách nhiệm gửi nhóm tin nhắn có liên quan này tới một người tiêu dùng trong số nhiều người tiêu dùng và chuyển quyền sở hữu nhóm nếu người tiêu dùng ngừng hoạt động.

Thỏa thuận nhà sản xuất

Phân vùng không phải là điều duy nhất cần xem xét khi gửi tin nhắn. Chúng ta hãy xem các phương thức send() của lớp Producer trong Java API:

Future < RecordMetadata > send(ProducerRecord < K, V > record);
Future < RecordMetadata > send(ProducerRecord < K, V > record, Callback callback);

Cần lưu ý ngay rằng cả hai phương thức đều trả về Tương lai, điều này cho biết rằng thao tác gửi không được thực hiện ngay lập tức. Kết quả là một thông báo (ProducerRecord) được ghi vào bộ đệm gửi cho từng phân vùng đang hoạt động và được gửi đến nhà môi giới dưới dạng một luồng nền trong thư viện máy khách Kafka. Mặc dù điều này làm cho mọi thứ trở nên cực kỳ nhanh, nhưng điều đó có nghĩa là một ứng dụng thiếu kinh nghiệm có thể mất tin nhắn nếu quá trình của nó bị dừng.

Như mọi khi, có một cách để làm cho hoạt động gửi tin cậy hơn với chi phí hiệu suất. Kích thước của bộ đệm này có thể được đặt thành 0 và chuỗi ứng dụng gửi sẽ buộc phải đợi cho đến khi quá trình chuyển thông báo tới nhà môi giới hoàn tất, như sau:

RecordMetadata metadata = producer.send(record).get();

Tìm hiểu thêm về đọc tin nhắn

Việc đọc tin nhắn có những phức tạp bổ sung cần được suy đoán. Không giống như API JMS, có thể chạy trình nghe tin nhắn để phản hồi tin nhắn, Người tiêu dùng Kafka chỉ thăm dò ý kiến. Chúng ta hãy xem xét kỹ hơn về phương pháp thăm dò ý kiến()được sử dụng cho mục đích này:

ConsumerRecords < K, V > poll(long timeout);

Giá trị trả về của phương thức là một cấu trúc container chứa nhiều đối tượng kỷ lục người tiêu dùng từ một số phân vùng có khả năng. kỷ lục người tiêu dùng bản thân nó là một đối tượng chứa cho một cặp khóa-giá trị với siêu dữ liệu được liên kết, chẳng hạn như phân vùng mà từ đó nó được dẫn xuất.

Như đã thảo luận trong Chương 2, chúng ta phải ghi nhớ điều gì sẽ xảy ra với các tin nhắn sau khi chúng được xử lý thành công hoặc không thành công, ví dụ: nếu máy khách không thể xử lý tin nhắn hoặc nếu nó bị hủy. Trong JMS, điều này được xử lý thông qua chế độ xác nhận. Người môi giới sẽ xóa tin nhắn đã xử lý thành công hoặc gửi lại tin nhắn thô hoặc giả mạo (giả sử các giao dịch đã được sử dụng).
Kafka hoạt động rất khác. Các tin nhắn không bị xóa trong trình môi giới sau khi hiệu đính và những gì xảy ra khi lỗi là trách nhiệm của chính mã hiệu đính.

Như chúng tôi đã nói, nhóm người tiêu dùng được liên kết với phần bù trong nhật ký. Vị trí nhật ký được liên kết với phần bù này tương ứng với thông báo tiếp theo sẽ được phát hành để phản hồi thăm dò ý kiến(). Thời điểm khi phần bù này tăng lên có ý nghĩa quyết định đối với việc đọc.

Quay trở lại mô hình đọc đã thảo luận trước đó, quá trình xử lý thông báo bao gồm ba giai đoạn:

  1. Lấy một tin nhắn để đọc.
  2. Xử lý tin nhắn.
  3. Tin nhắn xác nhận.

Người tiêu dùng Kafka đi kèm với tùy chọn cấu hình kích hoạt.auto.commit. Đây là cài đặt mặc định được sử dụng thường xuyên, giống như cài đặt có chứa từ "tự động".

Trước Kafka 0.10, một khách hàng sử dụng tùy chọn này sẽ gửi phần bù của tin nhắn cuối cùng được đọc trong cuộc gọi tiếp theo thăm dò ý kiến() sau khi chế biến. Điều này có nghĩa là bất kỳ tin nhắn nào đã được tìm nạp đều có thể được xử lý lại nếu khách hàng đã xử lý chúng nhưng bị hủy bất ngờ trước khi gọi thăm dò ý kiến(). Vì người môi giới không giữ bất kỳ trạng thái nào về số lần tin nhắn đã được đọc, nên người tiêu dùng tiếp theo truy xuất tin nhắn đó sẽ không biết rằng có điều gì đó tồi tệ đã xảy ra. Hành vi này là giả giao dịch. Phần bù chỉ được cam kết nếu tin nhắn được xử lý thành công, nhưng nếu khách hàng bị hủy bỏ, nhà môi giới sẽ gửi lại tin nhắn tương tự cho khách hàng khác. Hành vi này phù hợp với đảm bảo gửi tin nhắn "ít nhất một lần".

Trong Kafka 0.10, mã máy khách đã được thay đổi để thư viện máy khách kích hoạt cam kết định kỳ, như đã định cấu hình auto.commit.interval.ms. Hành vi này nằm ở đâu đó giữa các chế độ JMS AUTO_ACKNOWLEDGE và DUPS_OK_ACKNOWLEDGE. Khi sử dụng autocommit, các tin nhắn có thể được cam kết bất kể chúng có thực sự được xử lý hay không - điều này có thể xảy ra trong trường hợp người tiêu dùng chậm. Nếu một người tiêu dùng hủy bỏ, các tin nhắn sẽ được người tiêu dùng tiếp theo tìm nạp, bắt đầu từ vị trí đã cam kết, điều này có thể dẫn đến một tin nhắn bị bỏ lỡ. Trong trường hợp này, Kafka không làm mất tin nhắn, mã đọc không xử lý chúng.

Chế độ này có cùng lời hứa như trong phiên bản 0.9: thư có thể được xử lý nhưng nếu không thành công, phần bù có thể không được cam kết, có khả năng khiến quá trình phân phối bị nhân đôi. Càng nhiều tin nhắn bạn tìm nạp khi thực thi thăm dò ý kiến(), vấn đề này càng nhiều.

Như đã thảo luận trong phần “Đọc tin nhắn từ hàng đợi” ở trang 21, không có chuyện gửi tin nhắn một lần trong hệ thống nhắn tin khi tính đến các chế độ lỗi.

Trong Kafka, có hai cách để cam kết (commit) một phần bù (offset): tự động và thủ công. Trong cả hai trường hợp, thông báo có thể được xử lý nhiều lần nếu thông báo đã được xử lý nhưng không thành công trước khi xác nhận. Bạn cũng có thể chọn hoàn toàn không xử lý thông báo nếu cam kết xảy ra trong nền và mã của bạn đã được hoàn thành trước khi có thể xử lý (có thể trong Kafka 0.9 trở về trước).

Bạn có thể kiểm soát quy trình cam kết bù thủ công trong API người tiêu dùng Kafka bằng cách đặt tham số kích hoạt.auto.commit thành false và gọi rõ ràng một trong các phương thức sau:

void commitSync();
void commitAsync();

Nếu bạn muốn xử lý thông báo "ít nhất một lần", bạn phải cam kết bù thủ công với cam kết()bằng cách thực hiện lệnh này ngay sau khi xử lý tin nhắn.

Các phương pháp này không cho phép thông báo được xác nhận trước khi chúng được xử lý, nhưng chúng không làm gì để loại bỏ sự chậm trễ xử lý tiềm ẩn trong khi tạo ra vẻ ngoài của giao dịch. Không có giao dịch nào trong Kafka. Khách hàng không có khả năng thực hiện những việc sau:

  • Tự động khôi phục một tin nhắn giả mạo. Bản thân người tiêu dùng phải xử lý các trường hợp ngoại lệ phát sinh từ tải trọng có vấn đề và sự cố ngừng hoạt động phụ trợ, vì họ không thể dựa vào nhà môi giới để gửi lại tin nhắn.
  • Gửi tin nhắn đến nhiều chủ đề trong một hoạt động nguyên tử. Như chúng ta sẽ sớm thấy, quyền kiểm soát các chủ đề và phân vùng khác nhau có thể nằm trên các máy khác nhau trong cụm Kafka không điều phối các giao dịch khi được gửi. Tại thời điểm viết bài này, một số công việc đã được thực hiện để thực hiện điều này với KIP-98.
  • Liên kết việc đọc một tin nhắn từ một chủ đề với việc gửi một tin nhắn khác đến một chủ đề khác. Một lần nữa, kiến ​​trúc của Kafka phụ thuộc vào nhiều máy độc lập chạy như một xe buýt và không có nỗ lực nào để che giấu điều này. Ví dụ: không có thành phần API nào cho phép bạn liên kết người tiêu dùng и Nhà sản xuất trong một giao dịch. Trong JMS, điều này được cung cấp bởi đối tượng Phiêntừ đó được tạo ra tin nhắnnhà sản xuất и Tin nhắnNgười tiêu dùng.

Nếu chúng ta không thể dựa vào các giao dịch, làm thế nào chúng ta có thể cung cấp ngữ nghĩa gần hơn với ngữ nghĩa được cung cấp bởi các hệ thống nhắn tin truyền thống?

Nếu có khả năng phần bù của người tiêu dùng có thể tăng lên trước khi thông báo được xử lý, chẳng hạn như trong sự cố của người tiêu dùng, thì người tiêu dùng không có cách nào biết liệu nhóm người tiêu dùng của họ có bỏ lỡ thông báo khi được chỉ định một phân vùng hay không. Vì vậy, một chiến lược là tua lại phần bù về vị trí trước đó. API người tiêu dùng Kafka cung cấp các phương pháp sau cho việc này:

void seek(TopicPartition partition, long offset);
void seekToBeginning(Collection < TopicPartition > partitions);

Phương thức tìm kiếm() có thể được sử dụng với phương pháp
offsetsForTimes(Bản đồ dấu thời gianTìm kiếm) để tua lại trạng thái tại một số điểm cụ thể trong quá khứ.

Ngầm định, sử dụng phương pháp này có nghĩa là rất có khả năng một số thư đã được xử lý trước đó sẽ được đọc và xử lý lại. Để tránh điều này, chúng ta có thể sử dụng tính năng đọc tạm thời, như được mô tả trong Chương 4, để theo dõi các thư đã xem trước đó và loại bỏ các thư trùng lặp.

Ngoài ra, mã người tiêu dùng của bạn có thể được giữ đơn giản, miễn là chấp nhận được việc mất hoặc trùng lặp thông báo. Khi chúng tôi xem xét các trường hợp sử dụng mà Kafka thường được sử dụng, chẳng hạn như xử lý các sự kiện nhật ký, số liệu, theo dõi lần nhấp, v.v., chúng tôi hiểu rằng việc mất các thư riêng lẻ khó có thể gây ảnh hưởng đáng kể đến các ứng dụng xung quanh. Trong những trường hợp như vậy, các giá trị mặc định là hoàn toàn chấp nhận được. Mặt khác, nếu ứng dụng của bạn cần gửi thanh toán, bạn phải cẩn thận chăm sóc từng tin nhắn riêng lẻ. Tất cả bắt nguồn từ bối cảnh.

Các quan sát cá nhân cho thấy rằng khi cường độ của các thông điệp tăng lên, giá trị của từng thông điệp riêng lẻ sẽ giảm đi. Thông điệp lớn có xu hướng có giá trị khi được xem ở dạng tổng hợp.

Tính khả dụng cao

Cách tiếp cận tính sẵn sàng cao của Kafka rất khác với cách tiếp cận của ActiveMQ. Kafka được thiết kế xung quanh các cụm mở rộng trong đó tất cả các phiên bản môi giới nhận và phân phối tin nhắn cùng một lúc.

Một cụm Kafka bao gồm nhiều phiên bản môi giới chạy trên các máy chủ khác nhau. Kafka được thiết kế để chạy trên phần cứng độc lập thông thường, trong đó mỗi nút có bộ lưu trữ riêng. Không nên sử dụng bộ lưu trữ gắn mạng (SAN) vì nhiều nút tính toán có thể tranh giành thời gian.Ыe khoảng thời gian lưu trữ và tạo xung đột.

Kafka là luôn luôn hệ thống. Nhiều người dùng Kafka lớn không bao giờ tắt cụm của họ và phần mềm luôn cập nhật bằng cách khởi động lại tuần tự. Điều này đạt được bằng cách đảm bảo khả năng tương thích với phiên bản trước cho các tin nhắn và tương tác giữa các nhà môi giới.

Các nhà môi giới được kết nối với một cụm máy chủ Vườn bách thú, hoạt động như một sổ đăng ký dữ liệu cấu hình và được sử dụng để điều phối vai trò của từng nhà môi giới. Bản thân ZooKeeper là một hệ thống phân tán cung cấp tính sẵn sàng cao thông qua việc sao chép thông tin bằng cách thiết lập đại biểu.

Trong trường hợp cơ sở, một chủ đề được tạo trong cụm Kafka với các thuộc tính sau:

  • Số lượng phân vùng. Như đã thảo luận trước đó, giá trị chính xác được sử dụng ở đây phụ thuộc vào mức độ đọc song song mong muốn.
  • Hệ số sao chép (yếu tố) xác định có bao nhiêu phiên bản môi giới trong cụm sẽ chứa nhật ký cho phân vùng này.

Sử dụng ZooKeepers để phối hợp, Kafka cố gắng phân phối công bằng các phân vùng mới giữa các nhà môi giới trong cụm. Điều này được thực hiện bởi một phiên bản duy nhất hoạt động như một Bộ điều khiển.

Trong thời gian chạy cho mỗi phân vùng chủ đề Bộ điều khiển chỉ định vai trò cho một nhà môi giới lãnh đạo (lãnh đạo, chủ, người trình bày) và người theo dõi (tín đồ, nô lệ, cấp dưới). Người môi giới, đóng vai trò là người lãnh đạo phân vùng này, chịu trách nhiệm nhận tất cả các thông báo do nhà sản xuất gửi đến và phân phối thông báo đến người tiêu dùng. Khi các tin nhắn được gửi đến một phân vùng chủ đề, chúng sẽ được sao chép tới tất cả các nút môi giới đóng vai trò là người theo dõi cho phân vùng đó. Mỗi nút chứa nhật ký cho một phân vùng được gọi là bản sao. Người môi giới có thể đóng vai trò là người dẫn đầu đối với một số phân vùng và là người theo dõi đối với những phân vùng khác.

Một người theo dõi chứa tất cả các tin nhắn do người lãnh đạo nắm giữ được gọi là bản sao được đồng bộ hóa (bản sao ở trạng thái đồng bộ, bản sao không đồng bộ). Nếu một nhà môi giới đóng vai trò là người lãnh đạo cho một phân vùng bị hỏng, thì bất kỳ nhà môi giới nào được cập nhật hoặc đồng bộ hóa cho phân vùng đó đều có thể đảm nhận vai trò người lãnh đạo. Đó là một thiết kế vô cùng bền vững.

Một phần của cấu hình nhà sản xuất là tham số ách, xác định có bao nhiêu bản sao phải xác nhận (xác nhận) đã nhận được tin nhắn trước khi chuỗi ứng dụng tiếp tục gửi: 0, 1 hoặc tất cả. Nếu được đặt thành tất cả các, sau đó khi nhận được tin nhắn, người lãnh đạo sẽ gửi xác nhận lại cho nhà sản xuất ngay khi nhận được xác nhận (xác nhận) của bản ghi từ một số tín hiệu (bao gồm cả chính nó) được xác định bởi cài đặt chủ đề min.insync.replicas (mặc định 1). Nếu thông báo không thể được sao chép thành công, thì nhà sản xuất sẽ đưa ra một ngoại lệ ứng dụng (Bản sao không đủ hoặc Bản sao Không đủ Sau khi Nối).

Cấu hình điển hình tạo một chủ đề có hệ số sao chép là 3 (1 người dẫn đầu, 2 người theo dõi trên mỗi phân vùng) và tham số min.insync.replicas được đặt thành 2. Trong trường hợp này, cụm sẽ cho phép một trong các nhà môi giới quản lý phân vùng chủ đề ngừng hoạt động mà không ảnh hưởng đến các ứng dụng khách.

Điều này đưa chúng ta trở lại với sự đánh đổi quen thuộc giữa hiệu suất và độ tin cậy. Việc sao chép xảy ra với chi phí là thời gian chờ đợi thêm để xác nhận (xác nhận) từ những người theo dõi. Mặc dù, vì nó chạy song song, nên việc sao chép tới ít nhất ba nút có cùng hiệu suất như hai nút (bỏ qua việc tăng mức sử dụng băng thông mạng).

Bằng cách sử dụng lược đồ sao chép này, Kafka đã khéo léo tránh được việc phải ghi từng thông báo vào đĩa một cách vật lý bằng thao tác đồng bộ hóa(). Mỗi tin nhắn được gửi bởi nhà sản xuất sẽ được ghi vào nhật ký phân vùng, nhưng như đã thảo luận trong Chương 2, việc ghi vào một tệp ban đầu được thực hiện trong bộ đệm của hệ điều hành. Nếu thông báo này được sao chép sang một phiên bản Kafka khác và nằm trong bộ nhớ của nó, thì việc mất đường dẫn đầu không có nghĩa là chính thông báo đó đã bị mất - nó có thể bị mất bởi một bản sao được đồng bộ hóa.
Từ chối thực hiện thao tác đồng bộ hóa() có nghĩa là Kafka có thể nhận tin nhắn nhanh nhất có thể ghi chúng vào bộ nhớ. Ngược lại, bạn có thể tránh xóa bộ nhớ vào đĩa càng lâu thì càng tốt. Vì lý do này, không có gì lạ khi các trình môi giới Kafka được phân bổ bộ nhớ 64 GB trở lên. Việc sử dụng bộ nhớ này có nghĩa là một phiên bản Kafka đơn lẻ có thể dễ dàng chạy với tốc độ nhanh hơn gấp nhiều nghìn lần so với một nhà môi giới tin nhắn truyền thống.

Kafka cũng có thể được cấu hình để áp dụng thao tác đồng bộ hóa() đến các gói tin nhắn. Vì mọi thứ trong Kafka đều được định hướng theo gói nên nó thực sự hoạt động khá tốt trong nhiều trường hợp sử dụng và là một công cụ hữu ích cho những người dùng yêu cầu sự đảm bảo rất cao. Phần lớn hiệu suất thuần túy của Kafka đến từ các tin nhắn được gửi đến nhà môi giới dưới dạng các gói và những tin nhắn này được đọc từ nhà môi giới trong các khối tuần tự bằng cách sử dụng không sao chép hoạt động (các hoạt động trong đó nhiệm vụ sao chép dữ liệu từ vùng bộ nhớ này sang vùng bộ nhớ khác không được thực hiện). Cái sau là hiệu suất lớn và mức tăng tài nguyên và chỉ có thể thực hiện được thông qua việc sử dụng cấu trúc dữ liệu nhật ký cơ bản xác định lược đồ phân vùng.

Có thể có hiệu suất tốt hơn nhiều trong một cụm Kafka so với một nhà môi giới Kafka duy nhất, bởi vì các phân vùng chủ đề có thể mở rộng trên nhiều máy riêng biệt.

Kết quả

Trong chương này, chúng ta đã xem xét cách kiến ​​trúc Kafka mô phỏng lại mối quan hệ giữa khách hàng và nhà môi giới để cung cấp một đường dẫn nhắn tin cực kỳ mạnh mẽ, với thông lượng lớn hơn nhiều lần so với một nhà môi giới tin nhắn thông thường. Chúng tôi đã thảo luận về chức năng mà nó sử dụng để đạt được điều này và xem xét ngắn gọn kiến ​​trúc của các ứng dụng cung cấp chức năng này. Trong chương tiếp theo, chúng ta sẽ xem xét các vấn đề phổ biến mà các ứng dụng dựa trên nhắn tin cần giải quyết và thảo luận các chiến lược để giải quyết chúng. Chúng tôi sẽ kết thúc chương này bằng cách phác thảo cách nói về các công nghệ nhắn tin nói chung để bạn có thể đánh giá mức độ phù hợp của chúng đối với các trường hợp sử dụng của mình.

Phần dịch trước: Hiểu các nhà môi giới tin nhắn. Tìm hiểu cơ chế nhắn tin với ActiveMQ và Kafka. Chương 1

Dịch xong: tele.gg/middle_java

Để được tiếp tục ...

Chỉ những người dùng đã đăng ký mới có thể tham gia khảo sát. Đăng nhập, xin vui lòng.

Kafka có được sử dụng trong tổ chức của bạn không?

  • vâng

  • Không

  • Trước đây đã sử dụng, bây giờ không

  • Chúng tôi dự định sử dụng

38 người dùng bình chọn. 8 người dùng bỏ phiếu trắng.

Nguồn: www.habr.com

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