Tác giả: Danica Fine & Nikoleta Verbeck | Ngày đăng: 10/11/2022
Link bài viết gốc | Link series tiếng Việt
Khi gặp sự cố với Apache Kafka® - ví dụ như số lượng kết nối tới các broker tăng đột biến hoặc việc gom nhóm bản ghi (record batching) hoạt động không bình thường - các bạn sẽ rất dễ coi các vấn đề này như những vấn đề cần được giải quyết riêng lẻ. Tuy nhiên, bạn sẽ sớm nhận ra, phần lớn các vấn đề này thực chất chỉ là triệu chứng của một vấn đề lớn hơn. Thay vì chỉ xử lý từng triệu chứng riêng lẻ, chẳng phải sẽ tốt hơn nếu ta đi đến tận gốc vấn đề bằng một chẩn đoán chuẩn chỉ?
Nếu bạn đang muốn nâng cao kỹ năng debug Kafka của mình và hiểu sâu hơn về các vấn đề phổ biến cũng như các “bệnh lý” tiềm ẩn đằng sau từng triệu chứng, thì loạt bài viết này là dành cho bạn.
Các triệu chứng có trong loạt bài viết
Trong loạt bài viết này, chúng ta sẽ cùng tìm hiểu một số triệu chứng phổ biến bạn có thể gặp phải khi sử dụng Kafka, bao gồm:
- Giảm thông lượng gửi message
- Tăng request rate, thời gian response và/hoặc tăng tải trên broker (bài viết này)
- Tăng thời gian tái cân bằng (rebalance) consumer
- Tăng số lượng kết nối
Những vấn đề này khá phổ biến, đến mức đôi khi bạn sẽ không để ý tới chúng nếu chúng chưa gây ảnh hưởng nghiêm trọng tới hoạt động thông thường. Hãy cùng khám phá từng triệu chứng cụ thể, tìm hiểu xem chúng là gì và tác động của chúng, cũng như đặt ra những câu hỏi giúp bạn xác định gốc rễ vấn đề.
Nội dung bài viết này: Tăng request rate, thời gian response và/hoặc tăng tải trên broker (Increased request rate, request response time, and/or broker load)
Requests là thành phần rất quan trọng trong Kafka – chúng là cách các client (producer và consumer) tương tác với dữ liệu khi nó đi vào hoặc đi ra khỏi Kafka topic. Trong một số trường hợp, quá nhiều request có thể gây quá tải cho các broker. Để hiểu điều này, ta cần biết chuyện gì xảy ra "phía sau hậu trường" khi một request được gửi đi.
Vòng đời của một request từ client
Mặc dù tất cả những gì chúng ta thấy là một lệnh gọi producer.send()
hoặc consumer.poll()
, nhưng thực tế là những lệnh này được Kafka chuyển đổi thành request gửi tới broker để xử lý. Quá trình xử lý này chi tiết và có thể được theo dõi qua các bước:
Hãy xem xét một request, trong ví dụ này là một lệnh gọi producer.send()
:
-
Producer nhận các record để gửi → xác định topic-partition thông qua bộ phân vùng (partitioner) → gom nhóm (batching) và nén (tùy chọn) các record theo
batch.size
vàlinger.ms
→ gọi producer.send(). Ở đây, chúng ta sẽ giả định rằng có một số record được gửi đến một topic-partition duy nhất. -
Request được gửi tới broker, nằm tại bộ đệm nhận dữ liệu của socket (socket receive buffer) trong broker → chờ cho đến khi được network thread xử lý → định dạng request thành produce request object và đưa vào request queue.
-
Thread I/O lấy request ra → thực hiện xác minh và ghi dữ liệu vào commit log vật lý tương ứng với topic-partition.
-
Sự bền vững của dữ liệu (Durability) là yếu tố quan trọng. Kafka sao chép (replicate) dữ liệu đến nhiều broker trong cluster để đảm bảo tính nhất quán và chống mất mát. Trong cấu hình mặc định, broker không phản hồi ngay cho producer mà chờ việc sao chép (replication) hoàn tất. Trong lúc đó, request được lưu trong cấu trúc dạng map gọi là "purgatory".
-
Sau khi replication xong → broker lấy request khỏi purgatory, tạo response object → đẩy vào response queue → gửi lại cho producer.
Giờ đây, bạn có thể thấy rõ có bao nhiêu bước liên quan trong một lệnh gọi producer.send()
, và consumer.poll()
cũng tương tự vậy. Mặc dù Kafka được xây dựng để xử lý thông lượng cao, nhưng một lượng lớn request đột ngột có thể gây áp lực lên các broker và dẫn đến lỗi. Vì vậy, điều rất quan trọng là chúng ta cần giám sát được khi nào hệ thống đang gặp tình trạng quá tải request để xử lý kịp thời.
Các chỉ số cần theo dõi trong broker
Một phần thiết yếu để duy trì một Kafka Cluster ổn định là giám sát các broker thông qua JMX metrics. Việc theo dõi này giúp bạn phát hiện sớm dấu hiệu quá tải. Mặc dù có rất nhiều chỉ số, chỉ cần nắm bắt một vài metric trọng yếu là bạn đã có thể duy trì tình trạng "khỏe mạnh" cho broker.
-
RequestsPerSec
kafka.network:type=RequestMetrics,name=RequestsPerSec,request={Produce|FetchConsumer|FetchFollower}
Số lượng request Kafka nhận được mỗi giây. Đây là chỉ số cơ bản nhất để xem broker có đang bị quá tải không. Nếu chỉ số này tăng đột biến, có thể một client nào đó đang gửi request không hiệu quả.
Bạn có thể thay đổi loại request (request={OffsetFetch|CommitOffsets}
) để kiểm tra sức khỏe của consumer hoặc producer (đặc biệt với chế độ exactly-once). -
NetworkProcessorAvgIdlePercent
kafka.network:type=SocketServer,name=NetworkProcessorAvgIdlePercent
Tỷ lệ phần trăm thời gian các network thread nhàn rỗi (0: quá tải, 1: rảnh rỗi). Càng cao càng tốt. Tối thiểu nên trên 30% cho cluster tự host, còn với Confluent Cloud thì nên cao hơn nữa. -
RequestHandlerAvgIdlePercent
kafka.server:type=KafkaRequestHandlerPool,name=RequestHandlerAvgIdlePercent
Tỷ lệ phần trăm thời gian nhàn rỗi của các request handler thread (I/O thread). Chỉ số này phản ánh mức độ tải của broker. Giống như chỉ số trên, nên duy trì cao, cũng nên trên 30% cho cluster tự host, còn với Confluent Cloud thì nên cao hơn nữa. -
RequestQueueSize
kafka.network:type=RequestChannel,name=RequestQueueSize
Số lượng request đang nằm chờ trong request queue. Càng thấp càng tốt – giá trị cao có nghĩa là queue đang bị tắc nghẽn. Dùng kết hợp vớiNetworkProcessorAvgIdlePercent
để đánh giá tình trạng toàn bộ pipeline xử lý request. -
TotalTimeMs
kafka.network:type=RequestMetrics,name=TotalTimeMs,request={Produce|FetchConsumer|FetchFollower}
Tổng thời gian để xử lý toàn bộ một request (bao gồm các thời gian gửi). Càng thấp càng tốt → phản ánh độ trễ của hệ thống.
Liên quan đến TotalTimeMs
, các số liệu sau đây có thể giúp tìm hiểu sâu hơn về nguyên nhân làm tăng thời gian xử lý request của bạn:
-
RequestQueueTimeMs
kafka.network:type=RequestMetrics,name=RequestQueueTimeMs,request={Produce|FetchConsumer|FetchFollower}
Thời gian request nằm trong hàng đợi trước khi được I/O thread xử lý → cao quá → I/O thread quá tải không theo kịp các request được gửi đến. -
LocalTimeMs
kafka.network:type=RequestMetrics,name=LocalTimeMs,request={Produce|FetchConsumer|FetchFollower}
Thời gian leader broker mất để xử lý request → cao có thể liên quan đến hiệu suất disk hoặc page cache. -
RemoteTimeMs
kafka.network:type=RequestMetrics,name=RemoteTimeMs,request={Produce|FetchConsumer|FetchFollower}
Thời gian chờ các follower broker xử lý và sao chép (replicate) → cao cho thấy vấn đề về hiệu suất replication. -
ResponseQueueTimeMs
kafka.network:type=RequestMetrics,name=ResponseQueueTimeMs,request={Produce|FetchConsumer|FetchFollower}
Thời gian response chờ trong response queue trước khi gửi → giúp phát hiện I/O thread có đang bị quá tải hay không. -
ResponseSendTimeMs
kafka.network:type=RequestMetrics,name=ResponseSendTimeMs,request={Produce|FetchConsumer|FetchFollower}
Thời gian để gửi response về cho client.- Với
Produce
: thường rất thấp, phản ánh về hiệu suất network. - Với
FetchConsumer
: phản ánh thời gian gửi toàn bộ record cho consumer. - Với
FetchFollower
: phản ánh thời gian gửi record cần replicate cho follower.
- Với
Những chỉ số trên là điểm khởi đầu tuyệt vời để giám sát hiệu suất của Kafka. Tuy nhiên, chúng chỉ là phần nổi của tảng băng – Kafka còn rất nhiều metric khác để bạn khai thác khi cần.
Tiếp tục chẩn đoán
Tăng request rate có thể là triệu chứng của nhiều vấn đề khác nhau, tùy vào những gì đang diễn ra trong cluster của bạn tại thời điểm đó. Khi đi qua các câu hỏi dưới đây, bạn sẽ nhận thấy nhiều điểm tương đồng với các câu hỏi trong bài trước về tình trạng giảm thông lượng. Batching kém hiệu quả thường dẫn đến số lượng request tăng. Nhưng ngược lại không phải lúc nào đúng: tăng request chưa chắc do giảm throughput hay batching kém.
Ngoài việc thấy request rate tăng và tải cao trên các broker...
-
... bạn có thấy các record có được nén hiệu quả không?
Có thểlinger.ms
đang để quá thấp hoặc chưa được cấu hình. Trong vòng đời của request, nén (compression) xảy ra trước khi gửi đến broker, và chỉ thực hiện trên từng batch riêng lẻ. Nén sẽ hiệu quả hơn nếu batch chứa nhiều bản ghi và đầy đủ. -
... bạn có thấy tần suất yêu cầu (request rate) quá cao?
Nếu Kafka của bạn chạy trên cloud, hãy kiểm tra lại cấu hình workload của KafkaProducer. Khi mới triển khai cloud, thường bắt đầu với cấu hình nhỏ. Nhưng khi nhu cầu tăng dần, ít ai nghĩ đến việc scale workload. → Mở rộng theo chiều dọc (vertical scaling) giúp tập trung dữ liệu hơn và cải thiện batching.Cũng cần chú ý rằng, tùy thuộc vào ngôn ngữ lập trình, mà client tạo nhiều metadata request hơn những ngôn ngữ khác. Nghe có vẻ vô hại, nhưng các metadata request dư thừa nhiều có thể thực sự tạo gánh nặng lớn lên các broker theo thời gian. Nếu bạn thấy sự bùng nổ request bất thường, hãy kiểm tra lại cách client được triển khai (kiểm tra lại code xử lý ở client).
-
... bạn có thấy số lượng kết nối tăng và hiệu quả batching giảm?
Dấu hiệu cho thấy bạn có thể đang dùng nhiều KafkaProducer instance trong cùng một service/process. Lý do phổ biến: Vừa di chuyển (migrate) từ công nghệ messaging khác sang và cố gắng thay đổi code ít nhất có thể. Không hiểu rõ KafkaProducer là thread-safe, nên tạo nhiều instance thay vì dùng chung. → Nên rà soát lại code ở client. -
... bạn có thấy consumer của bạn commit quá thường xuyên?
Có thể bạn đang gây quá tải cho broker vì commit quá nhiều. Mỗi lần consumer commit, một record được gửi đến consumer offset topic để lưu lại offset gần nhất.
Consumer offset topic là một compacted topic: càng nhiều commit → I/O thread trong broker phải xử lý nhiều request hơn.
Consumer offset topic này chứa nhiều record hơn cần được nén (compact). Mặc dù quá trình nén (compaction) chạy nền, không ảnh hưởng trực tiếp đến việc sản xuất/tiêu thụ record, nhưng nó tiêu tốn CPU và bộ nhớ heap. Điều này có thể làm giảm hiệu suất của broker, đặc biệt khi commit dày đặc. -
... bạn có thấy Kafka bị mất dữ liệu gần đây?
Có thể bạn chưa xử lý callback của producer một cách hợp lý – hoặc không xử lý callback gì cả!
Note từ dịch giả:
Callback (hàm gọi lại) là một hàm được truyền như tham số vào một hàm khác, và sẽ được gọi lại sau khi một hành động cụ thể hoàn tất – thường dùng để xử lý kết quả bất đồng bộ (asynchronous). Giúp bạn kiểm tra lỗi, retry, hoặc xác nhận gửi thành công. Không xử lý callback = bỏ lỡ cơ hội đảm bảo tính bền vững của dữ liệu.
Kết luận
Lần tới khi Kafka cluster của bạn “có vấn đề”, hãy dành thời gian để chẩn đoán trước khi thay đổi. Đừng vội vàng điều chỉnh cấu hình hay code nếu chưa hiểu rõ nguyên nhân sâu xa. Với các chỉ số và câu hỏi ở trên, bạn đã có trong tay công cụ để phát hiện và xử lý vấn đề liên quan đến request và tải của broker một cách hiệu quả.