Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

Điều gì có thể buộc một công ty lớn như Lamoda, với quy trình hợp lý và hàng chục dịch vụ kết nối với nhau, thay đổi đáng kể cách tiếp cận của mình? Động lực có thể hoàn toàn khác nhau: từ lập pháp đến mong muốn thử nghiệm vốn có ở tất cả các lập trình viên.

Nhưng điều này không có nghĩa là bạn không thể tin tưởng vào những lợi ích bổ sung. Sergey Zaika sẽ cho bạn biết chính xác bạn có thể giành được gì nếu triển khai API hướng sự kiện trên Kafka (vài cánh đồng). Chắc chắn cũng sẽ có những cuộc thảo luận về những cú đột phá lớn và những khám phá thú vị - thí nghiệm không thể thực hiện được nếu không có chúng.

Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

Tuyên bố miễn trừ trách nhiệm: Bài viết này dựa trên tài liệu từ cuộc gặp mặt mà Sergey tổ chức vào tháng 2018 năm XNUMX trên HighLoad++. Trải nghiệm trực tiếp làm việc với Kafka của Lamoda đã thu hút người nghe không kém các phóng sự khác trong lịch trình. Chúng tôi nghĩ rằng đây là một ví dụ tuyệt vời về thực tế là bạn có thể và nên luôn tìm được những người có cùng chí hướng, và những người tổ chức HighLoad++ sẽ tiếp tục cố gắng tạo ra một bầu không khí thuận lợi cho việc này.

Về quá trình

Lamoda là một nền tảng thương mại điện tử lớn có trung tâm liên lạc, dịch vụ giao hàng (và nhiều chi nhánh), studio ảnh, nhà kho khổng lồ và tất cả đều chạy trên phần mềm riêng. Có hàng tá phương thức thanh toán, đối tác b2b có thể sử dụng một số hoặc tất cả các dịch vụ này và muốn biết thông tin cập nhật về sản phẩm của họ. Ngoài ra, Lamoda hoạt động ở ba quốc gia ngoài Liên bang Nga và mọi thứ ở đó có một chút khác biệt. Tổng cộng, có lẽ có hơn một trăm cách để định cấu hình một đơn hàng mới, những cách này phải được xử lý theo cách riêng của nó. Tất cả điều này hoạt động với sự trợ giúp của hàng chục dịch vụ đôi khi giao tiếp theo những cách không rõ ràng. Ngoài ra còn có một hệ thống trung tâm có trách nhiệm chính là trạng thái đơn hàng. Chúng tôi gọi cô ấy là BOB, tôi làm việc với cô ấy.

Công cụ hoàn tiền với API hướng sự kiện

Từ hướng theo sự kiện khá nhàm chán; xa hơn một chút chúng ta sẽ định nghĩa chi tiết hơn ý nghĩa của từ này. Tôi sẽ bắt đầu với bối cảnh mà chúng tôi quyết định thử cách tiếp cận API hướng sự kiện trong Kafka.

Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

Ở bất kỳ cửa hàng nào, ngoài những đơn hàng mà khách hàng phải thanh toán, có những lúc cửa hàng phải hoàn lại tiền vì sản phẩm không vừa ý khách hàng. Đây là một quá trình tương đối ngắn: chúng tôi làm rõ thông tin, nếu cần thiết và chuyển tiền.

Nhưng việc hoàn trả trở nên phức tạp hơn do những thay đổi về luật pháp và chúng tôi phải triển khai một vi dịch vụ riêng cho nó.

Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

Động lực của chúng tôi:

  1. Luật FZ-54 - tóm lại, luật yêu cầu phải báo cáo với cơ quan thuế về mọi giao dịch tiền tệ, dù là trả lại hay nhận, trong một SLA khá ngắn trong vài phút. Chúng tôi, với tư cách là một công ty thương mại điện tử, thực hiện khá nhiều hoạt động. Về mặt kỹ thuật, điều này có nghĩa là trách nhiệm mới (và do đó là một dịch vụ mới) và những cải tiến trong tất cả các hệ thống liên quan.
  2. chia BOB là một dự án nội bộ của công ty nhằm giảm bớt cho BOB một số lượng lớn các trách nhiệm không cốt lõi và giảm bớt độ phức tạp tổng thể của nó.

Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

Sơ đồ này cho thấy các hệ thống Lamoda chính. Bây giờ hầu hết trong số họ là nhiều hơn một nhóm gồm 5-10 vi dịch vụ xung quanh một khối nguyên khối đang thu hẹp lại. Chúng đang phát triển chậm, nhưng chúng tôi đang cố gắng làm cho chúng nhỏ hơn, bởi vì việc triển khai đoạn được chọn ở giữa thật đáng sợ - chúng tôi không thể cho phép nó rơi xuống. Chúng tôi buộc phải bảo lưu tất cả các trao đổi (mũi tên) và tính đến thực tế là bất kỳ trao đổi nào trong số đó có thể không có sẵn.

BOB cũng có khá nhiều sàn giao dịch: hệ thống thanh toán, hệ thống giao hàng, hệ thống thông báo, v.v.

Về mặt kỹ thuật BOB là:

  • ~150k dòng mã + ~100k dòng kiểm tra;
  • php7.2 + Zend 1 & Thành phần Symfony 3;
  • >100 API & ~50 tiện ích tích hợp bên ngoài;
  • 4 quốc gia có logic kinh doanh riêng.

Việc triển khai BOB rất tốn kém và vất vả, số lượng mã và các vấn đề mà nó giải quyết đến mức không ai có thể dồn hết vào đầu. Nói chung có nhiều lý do để đơn giản hóa nó.

Quá trình trả lại

Ban đầu, có hai hệ thống tham gia vào quá trình này: BOB và Thanh toán. Bây giờ hai cái nữa xuất hiện:

  • Dịch vụ tài chính hóa, sẽ giải quyết các vấn đề về tài chính hóa và liên lạc với các dịch vụ bên ngoài.
  • Công cụ hoàn tiền, chỉ đơn giản chứa các sàn giao dịch mới để không làm tăng BOB.

Bây giờ quá trình trông như thế này:

Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

  1. BOB nhận được yêu cầu hoàn lại tiền.
  2. BOB nói về Công cụ hoàn tiền này.
  3. Công cụ hoàn tiền thông báo cho Thanh toán: “Trả lại tiền”.
  4. Thanh toán trả lại tiền.
  5. Công cụ hoàn tiền và BOB đồng bộ hóa các trạng thái với nhau vì hiện tại cả hai đều cần nó. Chúng tôi vẫn chưa sẵn sàng chuyển hoàn toàn sang Công cụ hoàn tiền vì BOB có giao diện người dùng, báo cáo kế toán và nói chung là rất nhiều dữ liệu không thể chuyển dễ dàng như vậy. Bạn phải ngồi trên hai chiếc ghế.
  6. Yêu cầu tài chính hóa biến mất.

Kết quả là, chúng tôi đã tạo ra một loại xe buýt sự kiện trên Kafka - xe buýt sự kiện, trên đó mọi thứ bắt đầu. Hoan hô, bây giờ chúng ta có một điểm thất bại duy nhất (mỉa mai).

Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

Những ưu và nhược điểm là khá rõ ràng. Chúng tôi đã tạo ra một chiếc xe buýt, điều đó có nghĩa là bây giờ tất cả các dịch vụ đều phụ thuộc vào nó. Điều này giúp đơn giản hóa thiết kế nhưng lại tạo ra một điểm lỗi duy nhất trong hệ thống. Kafka sẽ gặp sự cố, quá trình sẽ dừng lại.

API hướng sự kiện là gì

Câu trả lời hay cho câu hỏi này có trong báo cáo của Martin Fowler (GOTO 2017) "Nhiều ý nghĩa của kiến ​​trúc hướng sự kiện".

Tóm tắt những gì chúng tôi đã làm:

  1. Kết thúc tất cả các trao đổi không đồng bộ thông qua lưu trữ sự kiện. Thay vì thông báo cho mọi người tiêu dùng quan tâm về việc thay đổi trạng thái qua mạng, chúng tôi viết một sự kiện về việc thay đổi trạng thái đối với bộ lưu trữ tập trung và những người tiêu dùng quan tâm đến chủ đề này sẽ đọc mọi thứ xuất hiện từ đó.
  2. Sự kiện trong trường hợp này là một thông báo (thông báo) rằng có điều gì đó đã thay đổi ở đâu đó. Ví dụ: trạng thái đơn hàng đã thay đổi. Người tiêu dùng quan tâm đến một số dữ liệu đi kèm với thay đổi trạng thái không có trong thông báo có thể tự mình tìm hiểu trạng thái của nó.
  3. Tùy chọn tối đa là tìm nguồn cung ứng sự kiện chính thức, chuyển giao nhà nước, trong sự kiện đó chứa tất cả thông tin cần thiết để xử lý: nó đến từ đâu và nó đã đi đến trạng thái nào, dữ liệu đã thay đổi chính xác như thế nào, v.v. Câu hỏi duy nhất là tính khả thi và lượng thông tin mà bạn có đủ khả năng lưu trữ.

Là một phần của việc ra mắt Công cụ hoàn tiền, chúng tôi đã sử dụng tùy chọn thứ ba. Quá trình xử lý sự kiện được đơn giản hóa này do không cần trích xuất thông tin chi tiết, đồng thời nó loại bỏ tình huống trong đó mỗi sự kiện mới tạo ra một loạt yêu cầu nhận thông tin rõ ràng từ người tiêu dùng.

Dịch vụ công cụ hoàn tiền không được tải, nên Kafka có nhiều sở thích về ngòi bút hơn là sự cần thiết. Tôi không nghĩ rằng nếu dịch vụ hoàn tiền trở thành một dự án có tải trọng cao thì hoạt động kinh doanh sẽ vui vẻ.

Trao đổi không đồng bộ NHƯ VẬY

Để trao đổi không đồng bộ, bộ phận PHP thường sử dụng RabbitMQ. Chúng tôi đã thu thập dữ liệu cho yêu cầu, xếp nó vào hàng đợi và người tiêu dùng của cùng một dịch vụ sẽ đọc và gửi nó (hoặc không gửi nó). Đối với API, Lamoda tích cực sử dụng Swagger. Chúng tôi thiết kế một API, mô tả nó trong Swagger và tạo mã máy khách và máy chủ. Chúng tôi cũng sử dụng JSON RPC 2.0 được cải tiến một chút.

Ở một số nơi, xe buýt ESB được sử dụng, một số hoạt động trên activeMQ, nhưng nói chung, RabbitMQ - tiêu chuẩn.

Trao đổi không đồng bộ SẼ ĐƯỢC

Khi thiết kế trao đổi thông qua bus sự kiện, có thể thấy một sự tương tự. Chúng tôi mô tả tương tự việc trao đổi dữ liệu trong tương lai thông qua mô tả cấu trúc sự kiện. Định dạng yaml, chúng tôi phải tự tạo mã, trình tạo tạo DTO theo đặc điểm kỹ thuật và hướng dẫn máy khách cũng như máy chủ làm việc với chúng. Thế hệ đi vào hai ngôn ngữ - golang và php. Điều này giúp giữ cho các thư viện nhất quán. Trình tạo được viết bằng golang, đó là lý do tại sao nó có tên gogi.

Tìm nguồn cung ứng sự kiện trên Kafka là một điều điển hình. Có một giải pháp từ phiên bản doanh nghiệp chính của Kafka Confluent, đó là nakadi, một giải pháp từ anh em miền Zalando của chúng tôi. Của chúng tôi động lực để bắt đầu với vanilla Kafka - điều này có nghĩa là để lại giải pháp miễn phí cho đến khi cuối cùng chúng tôi quyết định liệu chúng tôi có sử dụng nó ở mọi nơi hay không, đồng thời cũng để lại cho mình cơ hội vận động và cải tiến: chúng tôi muốn được hỗ trợ cho JSON RPC 2.0, trình tạo cho hai ngôn ngữ và hãy xem những gì khác.

Thật trớ trêu là ngay cả trong một trường hợp vui vẻ như vậy, khi có một doanh nghiệp gần tương tự, Zalando, đưa ra giải pháp gần tương tự, chúng ta lại không thể sử dụng nó một cách hiệu quả.

Mẫu kiến ​​trúc khi khởi chạy như sau: chúng tôi đọc trực tiếp từ Kafka nhưng chỉ viết thông qua events-bus. Có rất nhiều thứ sẵn sàng để đọc trong Kafka: công cụ môi giới, công cụ cân bằng và ít nhiều đã sẵn sàng cho việc mở rộng quy mô theo chiều ngang, tôi muốn giữ lại điều này. Chúng tôi muốn hoàn thành quá trình ghi thông qua một Cổng hay còn gọi là Xe buýt sự kiện và đây là lý do.

Xe buýt sự kiện

Hoặc một xe buýt sự kiện. Đây chỉ đơn giản là một cổng http không trạng thái, đảm nhận một số vai trò quan trọng:

  • Sản xuất xác nhận — chúng tôi kiểm tra xem các sự kiện có đáp ứng các thông số kỹ thuật của chúng tôi hay không.
  • Hệ thống chủ sự kiện, tức là đây là hệ thống chính và duy nhất trong công ty trả lời câu hỏi về những sự kiện nào với cấu trúc nào được coi là hợp lệ. Việc xác thực chỉ đơn giản bao gồm các kiểu dữ liệu và enum để chỉ định nghiêm ngặt nội dung.
  • Hàm băm để phân chia - cấu trúc thông báo Kafka là khóa-giá trị và sử dụng hàm băm của khóa, nó sẽ được tính toán vị trí đặt nó.

Tại sao

Chúng tôi làm việc trong một công ty lớn với quy trình hợp lý. Tại sao lại thay đổi bất cứ điều gì? Đây là một thử nghiệm, và chúng tôi hy vọng sẽ thu được một số lợi ích.

Trao đổi 1:n+1 (một đến nhiều)

Kafka giúp việc kết nối người tiêu dùng mới với API trở nên rất dễ dàng.

Giả sử bạn có một thư mục cần cập nhật trên nhiều hệ thống cùng lúc (và trong một số hệ thống mới). Trước đây, chúng tôi đã phát minh ra một gói triển khai set-API và hệ thống chính được thông báo về địa chỉ của người tiêu dùng. Bây giờ hệ thống chính sẽ gửi thông tin cập nhật về chủ đề và tất cả những ai quan tâm đều đọc nó. Một hệ thống mới đã xuất hiện - chúng tôi đã đăng ký nó cho chủ đề. Có, cũng có gói, nhưng đơn giản hơn.

Trong trường hợp công cụ hoàn tiền, là một phần của BOB, sẽ thuận tiện cho chúng tôi giữ chúng được đồng bộ hóa thông qua Kafka. Thanh toán cho biết số tiền đã được trả lại: BOB, RT phát hiện ra điều này, đã thay đổi trạng thái của họ, Dịch vụ tài chính phát hiện ra điều này và phát hành séc.

Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

Chúng tôi có kế hoạch tạo Dịch vụ thông báo thống nhất để thông báo cho khách hàng về tin tức liên quan đến đơn đặt hàng/trả lại của họ. Bây giờ trách nhiệm này được trải rộng giữa các hệ thống. Chỉ cần chúng tôi hướng dẫn Dịch vụ thông báo nắm bắt thông tin liên quan từ Kafka và phản hồi thông tin đó là đủ (và tắt các thông báo này trong các hệ thống khác). Sẽ không cần trao đổi trực tiếp mới.

Điều khiển dữ liệu

Thông tin giữa các hệ thống trở nên minh bạch - bất kể bạn có “doanh nghiệp đẫm máu” nào và cho dù lượng tồn đọng của bạn lớn đến đâu. Lamoda có bộ phận Phân tích dữ liệu thu thập dữ liệu từ hệ thống và chuyển dữ liệu đó sang dạng có thể sử dụng lại được, cho cả doanh nghiệp và hệ thống thông minh. Kafka cho phép bạn nhanh chóng cung cấp cho họ nhiều dữ liệu và cập nhật luồng thông tin đó.

Nhật ký sao chép

Tin nhắn không biến mất sau khi được đọc, như trong RabbitMQ. Khi một sự kiện chứa đủ thông tin để xử lý, chúng tôi có lịch sử các thay đổi gần đây đối với đối tượng và khả năng áp dụng những thay đổi này nếu muốn.

Thời gian lưu trữ của nhật ký sao chép phụ thuộc vào cường độ ghi vào chủ đề này; Kafka cho phép bạn linh hoạt đặt giới hạn về thời gian lưu trữ và dung lượng dữ liệu. Đối với các chủ đề chuyên sâu, điều quan trọng là tất cả người tiêu dùng phải có thời gian để đọc thông tin trước khi thông tin đó biến mất, ngay cả trong trường hợp không thể hoạt động trong thời gian ngắn. Thông thường có thể lưu trữ dữ liệu cho đơn vị ngày, như vậy là khá đủ để hỗ trợ.

Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

Tiếp theo kể lại một chút về tài liệu, dành cho những ai chưa biết về Kafka (hình ảnh cũng lấy từ tài liệu)

AMQP có hàng đợi: chúng tôi viết tin nhắn vào hàng đợi cho người tiêu dùng. Thông thường, một hàng đợi được xử lý bởi một hệ thống có cùng logic nghiệp vụ. Nếu cần thông báo cho một số hệ thống, bạn có thể hướng dẫn ứng dụng ghi vào một số hàng đợi hoặc định cấu hình trao đổi bằng cơ chế phân xuất, cơ chế này sẽ tự sao chép chúng.

Kafka có một sự trừu tượng tương tự chủ đề, trong đó bạn viết tin nhắn, nhưng chúng không biến mất sau khi đọc. Theo mặc định, khi kết nối với Kafka, bạn sẽ nhận được tất cả tin nhắn và có tùy chọn lưu ở nơi bạn đã dừng lại. Nghĩa là, bạn đọc tuần tự, bạn có thể không đánh dấu thư là đã đọc nhưng lưu id để sau đó bạn có thể tiếp tục đọc. Id bạn đã quyết định được gọi là offset và cơ chế là offset cam kết.

Theo đó, logic khác nhau có thể được thực hiện. Ví dụ: chúng tôi có BOB trong 4 trường hợp cho các quốc gia khác nhau - Lamoda ở Nga, Kazakhstan, Ukraine, Belarus. Vì chúng được triển khai riêng biệt nên chúng có cấu hình hơi khác nhau và logic nghiệp vụ riêng. Chúng tôi cho biết trong tin nhắn nó đề cập đến quốc gia nào. Mỗi người tiêu dùng BOB ở mỗi quốc gia đọc với một ID nhóm khác nhau và nếu thông báo không áp dụng cho họ, họ sẽ bỏ qua nó, tức là. ngay lập tức cam kết bù +1. Nếu Dịch vụ thanh toán của chúng tôi đọc cùng một chủ đề thì chủ đề đó sẽ được đọc với một nhóm riêng biệt và do đó các phần bù không giao nhau.

Yêu cầu sự kiện:

  • Tính đầy đủ của dữ liệu. Tôi muốn sự kiện có đủ dữ liệu để có thể xử lý.

  • Liêm chính Chúng tôi ủy quyền cho Events-bus xác minh rằng sự kiện này nhất quán và nó có thể xử lý nó.
  • Thứ tự là quan trọng. Trong trường hợp quay trở lại, chúng tôi buộc phải làm việc với lịch sử. Với thông báo thì thứ tự không quan trọng, nếu là thông báo đồng nhất thì email sẽ giống nhau bất kể đơn hàng nào đến trước. Trong trường hợp hoàn tiền, có một quy trình rõ ràng; nếu chúng tôi thay đổi đơn đặt hàng, các trường hợp ngoại lệ sẽ phát sinh, khoản tiền hoàn lại sẽ không được tạo hoặc xử lý - chúng tôi sẽ chuyển sang trạng thái khác.
  • Tính nhất quán. Chúng tôi có một cửa hàng và hiện chúng tôi tạo sự kiện thay vì API. Chúng tôi cần một cách để truyền tải thông tin nhanh chóng và ít tốn kém về các sự kiện mới cũng như những thay đổi đối với những sự kiện hiện có đối với dịch vụ của chúng tôi. Điều này đạt được thông qua một đặc điểm kỹ thuật chung trong kho lưu trữ git và trình tạo mã riêng biệt. Do đó, máy khách và máy chủ trong các dịch vụ khác nhau được phối hợp với nhau.

Kafka ở Lamoda

Chúng tôi có ba bản cài đặt Kafka:

  1. Nhật ký;
  2. Nghiên cứu và phát triển;
  3. Sự kiện-xe buýt.

Hôm nay chúng ta chỉ nói về điểm cuối cùng. Tại events-bus, chúng tôi không có số lượng cài đặt lớn - 3 nhà môi giới (máy chủ) và chỉ có 27 chủ đề. Theo quy định, một chủ đề là một quá trình. Nhưng đây là một điểm tinh tế và chúng ta sẽ đề cập đến nó ngay bây giờ.

Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

Trên đây là biểu đồ rps. Quá trình hoàn tiền được đánh dấu bằng đường màu ngọc lam (vâng, đường trên trục X) và đường màu hồng là quá trình cập nhật nội dung.

Danh mục Lamoda chứa hàng triệu sản phẩm và dữ liệu được cập nhật liên tục. Một số bộ sưu tập đã lỗi thời, những bộ sưu tập mới được tung ra để thay thế chúng và những mẫu mới liên tục xuất hiện trong danh mục. Chúng tôi cố gắng dự đoán những gì sẽ thú vị với khách hàng vào ngày mai, vì vậy chúng tôi liên tục mua những thứ mới, chụp ảnh chúng và cập nhật tủ trưng bày.

Đỉnh hồng là những cập nhật về sản phẩm, tức là những thay đổi về sản phẩm. Có thể thấy các anh chụp ảnh, chụp ảnh rồi chụp lại! - đã tải một gói sự kiện.

Các trường hợp sử dụng Sự kiện Lamoda

Chúng tôi sử dụng kiến ​​trúc được xây dựng cho các hoạt động sau:

  • Theo dõi trạng thái trả lại: kêu gọi hành động và theo dõi trạng thái từ tất cả các hệ thống liên quan. Thanh toán, trạng thái, tài chính hóa, thông báo. Tại đây, chúng tôi đã thử nghiệm phương pháp này, tạo ra các công cụ, thu thập tất cả các lỗi, viết tài liệu và hướng dẫn đồng nghiệp cách sử dụng nó.
  • Cập nhật thẻ sản phẩm: cấu hình, siêu dữ liệu, đặc điểm. Một hệ thống đọc (hiển thị) và một số hệ thống ghi.
  • Email, tin nhắn đẩy và tin nhắn: đơn hàng đã được thu thập, đơn hàng đã đến, đơn hàng đã được chấp nhận trả lại, v.v., có rất nhiều.
  • Đổi mới kho, kho — cập nhật số lượng mặt hàng, chỉ số lượng: đến kho, trả lại. Điều cần thiết là tất cả các hệ thống liên quan đến việc đặt hàng phải hoạt động với dữ liệu mới nhất. Hiện tại, hệ thống cập nhật chứng khoán khá phức tạp, Kafka sẽ đơn giản hóa nó.
  • Phân tích dữ liệu (Bộ phận R&D), công cụ ML, phân tích, thống kê. Chúng tôi muốn thông tin được minh bạch - Kafka rất phù hợp cho việc này.

Bây giờ là phần thú vị hơn về những biến động lớn và những khám phá thú vị đã xảy ra trong sáu tháng qua.

Vấn đề thiết kế

Giả sử chúng tôi muốn thực hiện một điều mới - ví dụ: chuyển toàn bộ quy trình phân phối sang Kafka. Bây giờ một phần của quy trình được triển khai trong Xử lý đơn hàng trong BOB. Có một mô hình trạng thái đằng sau việc chuyển đơn đặt hàng sang dịch vụ giao hàng, chuyển đến kho trung gian, v.v. Có cả một khối nguyên khối, thậm chí là hai khối, cùng với một loạt API dành riêng cho việc phân phối. Họ biết nhiều hơn về việc giao hàng.

Đây có vẻ là những khu vực tương tự nhau, nhưng Xử lý đơn hàng trong BOB và Hệ thống vận chuyển có các trạng thái khác nhau. Ví dụ: một số dịch vụ chuyển phát nhanh không gửi trạng thái trung gian mà chỉ gửi trạng thái cuối cùng: “đã giao” hoặc “mất”. Ngược lại, những người khác lại báo cáo rất chi tiết về sự di chuyển của hàng hóa. Mọi người đều có quy tắc xác thực của riêng mình: đối với một số người, email hợp lệ, có nghĩa là nó sẽ được xử lý; đối với những người khác, nó không hợp lệ, nhưng đơn hàng vẫn sẽ được xử lý vì có số điện thoại để liên hệ và sẽ có người nói rằng đơn hàng đó sẽ không được xử lý chút nào.

Dòng dữ liệu

Trong trường hợp của Kafka, câu hỏi về việc tổ chức luồng dữ liệu được đặt ra. Nhiệm vụ này liên quan đến việc lựa chọn một chiến lược dựa trên một số điểm; chúng ta hãy cùng điểm qua tất cả những điểm đó.

Trong một chủ đề hay trong những chủ đề khác nhau?

Chúng tôi có một đặc tả sự kiện. Trong BOB, chúng tôi viết rằng đơn hàng đó cần được giao và cho biết: số đơn hàng, thành phần của đơn hàng, một số SKU và mã vạch, v.v. Khi hàng đến kho, người giao hàng sẽ có thể nhận được trạng thái, dấu thời gian và mọi thứ cần thiết. Nhưng sau đó chúng tôi muốn nhận thông tin cập nhật về dữ liệu này trong BOB. Chúng tôi có quy trình ngược lại để nhận dữ liệu từ quá trình giao hàng. Đây có phải là cùng một sự kiện? Hay đây là một cuộc trao đổi riêng biệt xứng đáng có chủ đề riêng?

Rất có thể, chúng sẽ rất giống nhau và việc tạo ra một chủ đề không phải là không có cơ sở, bởi vì một chủ đề riêng biệt có nghĩa là những người tiêu dùng riêng biệt, cấu hình riêng biệt, một thế hệ riêng biệt của tất cả những điều này. Nhưng không phải là sự thật.

Lĩnh vực mới hay sự kiện mới?

Nhưng nếu bạn sử dụng các sự kiện tương tự thì một vấn đề khác sẽ phát sinh. Ví dụ: không phải tất cả hệ thống phân phối đều có thể tạo loại DTO mà BOB có thể tạo. Chúng tôi gửi cho họ id, nhưng họ không lưu nó vì họ không cần nó và từ quan điểm bắt đầu quá trình xe buýt sự kiện, trường này là bắt buộc.

Nếu chúng tôi đưa ra một quy tắc cho bus sự kiện mà trường này là bắt buộc thì chúng tôi buộc phải đặt các quy tắc xác thực bổ sung trong BOB hoặc trong trình xử lý sự kiện bắt đầu. Việc xác thực bắt đầu lan rộng khắp dịch vụ - điều này không thuận tiện lắm.

Một vấn đề khác là sự cám dỗ của sự phát triển gia tăng. Chúng ta được thông báo rằng cần phải thêm điều gì đó vào sự kiện này, và có lẽ, nếu chúng ta nghĩ về điều đó, thì lẽ ra nó phải là một sự kiện riêng biệt. Nhưng trong sơ đồ của chúng tôi, một sự kiện riêng biệt là một chủ đề riêng biệt. Một chủ đề riêng là toàn bộ quá trình mà tôi đã mô tả ở trên. Nhà phát triển chỉ muốn thêm một trường khác vào lược đồ JSON và tạo lại nó.

Trong trường hợp hoàn lại tiền, chúng tôi đã đến sự kiện diễn ra trong nửa năm. Chúng tôi có một sự kiện lớn gọi là cập nhật hoàn tiền, trong đó có trường loại mô tả bản cập nhật này thực sự là gì. Vì điều này, chúng tôi đã có những công tắc “tuyệt vời” với những người xác nhận đã cho chúng tôi biết cách xác thực sự kiện này bằng loại này.

Phiên bản sự kiện

Để xác thực tin nhắn trong Kafka, bạn có thể sử dụng Avro, nhưng cần phải ngay lập tức bắt tay vào sử dụng Confluent. Trong trường hợp của chúng tôi, chúng tôi phải cẩn thận với việc lập phiên bản. Không phải lúc nào cũng có thể đọc lại các tin nhắn từ nhật ký sao chép vì mô hình đã “trái”. Về cơ bản, hóa ra là xây dựng các phiên bản sao cho mô hình tương thích ngược: ví dụ: tạo một trường tạm thời tùy chọn. Nếu sự khác biệt quá lớn, chúng tôi bắt đầu viết chủ đề mới và chuyển khách hàng khi họ đọc xong chủ đề cũ.

Đảm bảo thứ tự đọc các phân vùng

Các chủ đề bên trong Kafka được chia thành các phân vùng. Điều này không quan trọng lắm khi chúng ta thiết kế các thực thể và sàn giao dịch, nhưng nó quan trọng khi quyết định cách sử dụng và mở rộng quy mô của nó.

Trong trường hợp thông thường, bạn viết một chủ đề bằng Kafka. Theo mặc định, một phân vùng được sử dụng và tất cả thư trong chủ đề này sẽ được chuyển đến phân vùng đó. Và người tiêu dùng do đó đọc các tin nhắn này một cách tuần tự. Giả sử bây giờ chúng ta cần mở rộng hệ thống để hai người tiêu dùng khác nhau có thể đọc tin nhắn. Ví dụ: nếu bạn đang gửi SMS, thì bạn có thể yêu cầu Kafka tạo một phân vùng bổ sung và Kafka sẽ bắt đầu chia tin nhắn thành hai phần - một nửa ở đây, một nửa ở đây.

Kafka phân chia chúng như thế nào? Mỗi tin nhắn có phần nội dung (trong đó chúng tôi lưu trữ JSON) và một khóa. Bạn có thể đính kèm hàm băm vào khóa này để xác định tin nhắn sẽ đi vào phân vùng nào.

Trong trường hợp hoàn tiền của chúng tôi, điều này rất quan trọng, nếu chúng tôi lấy hai phân vùng thì có khả năng người tiêu dùng song song sẽ xử lý sự kiện thứ hai trước sự kiện đầu tiên và sẽ gặp rắc rối. Hàm băm đảm bảo rằng các tin nhắn có cùng khóa sẽ nằm trong cùng một phân vùng.

Sự kiện và lệnh

Đây là một vấn đề khác mà chúng tôi gặp phải. Sự kiện là một sự kiện nhất định: chúng ta nói rằng điều gì đó đã xảy ra ở đâu đó (something_happened), ví dụ: một mặt hàng đã bị hủy hoặc đã xảy ra khoản tiền hoàn lại. Nếu ai đó lắng nghe những sự kiện này thì theo “mặt hàng đã bị hủy”, thực thể hoàn tiền sẽ được tạo và “hoàn tiền đã xảy ra” sẽ được ghi ở đâu đó trong phần thiết lập.

Nhưng thông thường, khi bạn thiết kế các sự kiện, bạn không muốn viết chúng một cách vô ích - bạn dựa vào thực tế là sẽ có ai đó đọc chúng. Có rất nhiều sự cám dỗ để viết không phải điều gì đó đã xảy ra (mặt hàng đã bị hủy, hoàn lại tiền), mà là điều gì đó nên làm. Ví dụ: mặt hàng đã sẵn sàng để được trả lại.

Một mặt, nó gợi ý cách sử dụng sự kiện. Mặt khác, nó nghe có vẻ không giống một tên sự kiện thông thường chút nào. Ngoài ra, cách đây không xa đến lệnh do_something. Nhưng bạn không có gì đảm bảo rằng có ai đó đã đọc sự kiện này; và nếu bạn đọc được thì bạn đã đọc thành công; và nếu bạn đọc thành công thì nghĩa là bạn đã làm được điều gì đó và điều đó đã thành công. Khi một sự kiện trở thành việc cần làm thì phản hồi trở nên cần thiết và đó chính là vấn đề.

Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

Trong trao đổi không đồng bộ trong RabbitMQ, khi bạn đọc tin nhắn, truy cập http, bạn có phản hồi - ít nhất là đã nhận được tin nhắn. Khi bạn viết thư cho Kafka, có một tin nhắn bạn đã viết cho Kafka, nhưng bạn không biết gì về cách nó được xử lý.

Do đó, trong trường hợp của chúng tôi, chúng tôi phải giới thiệu một sự kiện phản hồi và thiết lập giám sát để nếu có quá nhiều sự kiện được gửi đi thì sau một khoảng thời gian như vậy, số lượng sự kiện phản hồi đó sẽ đến. Nếu điều này không xảy ra thì có vẻ như đã xảy ra sự cố. Ví dụ: nếu chúng tôi gửi sự kiện “item_ready_to_refund”, chúng tôi hy vọng rằng khoản tiền hoàn lại sẽ được tạo, tiền sẽ được trả lại cho khách hàng và sự kiện “money_refunded” sẽ được gửi cho chúng tôi. Nhưng điều này không chắc chắn nên cần phải theo dõi.

Sự khác biệt

Có một vấn đề khá rõ ràng: nếu bạn đọc tuần tự một chủ đề và đưa ra một số thông điệp không hay, người tiêu dùng sẽ sa sút và bạn sẽ không thể tiến xa hơn. Bạn cần dừng tất cả người tiêu dùng, cam kết bù thêm để tiếp tục đọc.

Chúng tôi biết về điều đó, chúng tôi tin tưởng vào nó, nhưng nó đã xảy ra. Và điều này xảy ra vì sự kiện này hợp lệ theo quan điểm của bus sự kiện, sự kiện này hợp lệ theo quan điểm của trình xác thực ứng dụng, nhưng nó không hợp lệ theo quan điểm của PostgreSQL, bởi vì trong hệ thống MySQL của chúng tôi với UNSIGNED INT hệ thống chỉ có PostgreSQL với INT. Kích thước của anh ấy nhỏ hơn một chút và Id không vừa. Symfony chết với một ngoại lệ. Tất nhiên, chúng tôi đã bắt được ngoại lệ vì chúng tôi đã dựa vào nó và sẽ cam kết phần bù này, nhưng trước đó, chúng tôi muốn tăng bộ đếm sự cố vì thông báo đã được xử lý không thành công. Các bộ đếm trong dự án này cũng nằm trong cơ sở dữ liệu và Symfony đã đóng liên lạc với cơ sở dữ liệu và ngoại lệ thứ hai đã giết chết toàn bộ quá trình mà không có cơ hội thực hiện bù đắp.

Dịch vụ đã ngừng hoạt động một thời gian - may mắn thay, với Kafka, điều này không quá tệ vì các tin nhắn vẫn còn. Khi công việc được khôi phục, bạn có thể đọc xong chúng. Thật thoải mái.

Kafka có khả năng thiết lập độ lệch tùy ý thông qua công cụ. Nhưng để làm điều này, bạn cần phải dừng tất cả người tiêu dùng - trong trường hợp của chúng tôi, hãy chuẩn bị một bản phát hành riêng trong đó sẽ không có người tiêu dùng, triển khai lại. Sau đó, trong Kafka, bạn có thể thay đổi phần bù thông qua công cụ và thông báo sẽ được gửi đi.

Một sắc thái khác - nhật ký sao chép vs rdkafka.so - có liên quan đến các chi tiết cụ thể của dự án của chúng tôi. Chúng tôi sử dụng PHP và trong PHP, theo quy định, tất cả các thư viện đều giao tiếp với Kafka thông qua kho lưu trữ rdkafka.so và sau đó có một số loại trình bao bọc. Có thể đây là những khó khăn cá nhân của chúng tôi, nhưng hóa ra việc đọc lại một phần những gì chúng tôi đã đọc không phải là điều dễ dàng như vậy. Nói chung là có vấn đề về phần mềm.

Quay lại chi tiết cụ thể khi làm việc với các phân vùng, nó được viết ngay trong tài liệu người tiêu dùng >= phân vùng chủ đề. Nhưng tôi phát hiện ra điều này muộn hơn tôi mong muốn. Nếu bạn muốn mở rộng quy mô và có hai người tiêu dùng, bạn cần ít nhất hai phân vùng. Nghĩa là, nếu bạn có một phân vùng chứa 20 nghìn tin nhắn và bạn đã tạo một phân vùng mới, số lượng tin nhắn sẽ không sớm được cân bằng. Do đó, để có hai người tiêu dùng song song, bạn cần xử lý các phân vùng.

Giám sát

Tôi nghĩ cách chúng tôi giám sát sẽ còn rõ ràng hơn những vấn đề tồn tại trong cách tiếp cận hiện tại.

Ví dụ: chúng tôi tính toán có bao nhiêu sản phẩm trong cơ sở dữ liệu gần đây đã thay đổi trạng thái và theo đó, các sự kiện đáng lẽ phải xảy ra dựa trên những thay đổi này và chúng tôi gửi con số này đến hệ thống giám sát của mình. Sau đó từ Kafka chúng ta có được con số thứ hai, có bao nhiêu sự kiện thực sự được ghi lại. Rõ ràng, sự khác biệt giữa hai con số này phải luôn bằng XNUMX.

Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

Ngoài ra, bạn cần theo dõi hoạt động của nhà sản xuất, liệu xe buýt sự kiện có nhận được tin nhắn hay không và người tiêu dùng đang hoạt động như thế nào. Ví dụ: trong biểu đồ bên dưới, Công cụ hoàn tiền đang hoạt động tốt, nhưng BOB rõ ràng có một số vấn đề (đỉnh màu xanh lam).

Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

Tôi đã đề cập đến độ trễ của nhóm người tiêu dùng. Nói một cách đại khái, đây là số lượng tin nhắn chưa đọc. Nhìn chung, người tiêu dùng của chúng tôi làm việc nhanh nên độ trễ thường bằng 0, nhưng đôi khi có thể đạt đỉnh ngắn hạn. Kafka có thể thực hiện việc này ngay lập tức, nhưng bạn cần đặt ra một khoảng thời gian nhất định.

Có một dự án Hang thỏsẽ cung cấp cho bạn thêm thông tin về Kafka. Nó chỉ đơn giản sử dụng API nhóm người tiêu dùng để cung cấp trạng thái hoạt động của nhóm này. Ngoài OK và Fail, còn có một cảnh báo và bạn có thể phát hiện ra rằng người tiêu dùng của bạn không thể theo kịp tốc độ sản xuất - họ không có thời gian để đọc lại những gì được viết. Hệ thống này khá thông minh và dễ sử dụng.

Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

Phản hồi của API trông như thế này. Đây là nhóm bob-live-fifa, phân vùng hoàn lại.update.v1, trạng thái OK, độ trễ 0 - phần bù cuối cùng tương tự như vậy.

Kinh nghiệm phát triển dịch vụ Công cụ hoàn tiền với API không đồng bộ trên Kafka

Giám sát đã cập nhật_at SLA (bị kẹt) Tôi đã đề cập rồi. Ví dụ: sản phẩm đã chuyển sang trạng thái sẵn sàng trả lại. Chúng tôi cài đặt Cron, thông báo rằng nếu đối tượng này không hoàn lại tiền trong vòng 5 phút (chúng tôi sẽ trả lại tiền qua hệ thống thanh toán rất nhanh), thì chắc chắn đã xảy ra sự cố và đây chắc chắn là trường hợp cần được hỗ trợ. Do đó, chúng tôi chỉ cần lấy Cron để đọc những thứ như vậy và nếu chúng lớn hơn 0 thì nó sẽ gửi cảnh báo.

Tóm lại, sử dụng sự kiện sẽ thuận tiện khi:

  • thông tin cần thiết cho một số hệ thống;
  • kết quả xử lý không quan trọng;
  • có rất ít sự kiện hoặc sự kiện nhỏ.

Có vẻ như bài viết có một chủ đề rất cụ thể - API không đồng bộ trên Kafka, nhưng liên quan đến nó, tôi muốn đề xuất rất nhiều thứ cùng một lúc.
Đầu tiên, tiếp theo HighLoad ++ chúng ta cần đợi đến tháng XNUMX, vào tháng XNUMX sẽ có phiên bản St. Petersburg và vào tháng XNUMX, chúng ta sẽ nói về lượng tải cao ở Novosibirsk.
Thứ hai, tác giả của báo cáo, Sergei Zaika, là thành viên Ủy ban Chương trình của hội nghị mới của chúng tôi về quản lý kiến ​​thức Tri ThứcConf. Hội nghị kéo dài một ngày, sẽ diễn ra vào ngày 26 tháng XNUMX, nhưng chương trình của nó rất căng thẳng.
Và nó sẽ diễn ra vào tháng Năm PHP Nga и RIT++ (có kèm theo DevOpsConf) - bạn cũng có thể đề xuất chủ đề của mình ở đó, nói về trải nghiệm của bạn và phàn nàn về những chiếc nón nhồi bông của bạn.

Nguồn: www.habr.com

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