Kafka क्लस्टर को कैसे मैनेज करता है और कंज्यूमर को सही जगह कैसे रूट करता है

Kafka क्लस्टर को कैसे मैनेज करता है और कंज्यूमर को सही जगह कैसे रूट करता है

Kafka बाहर से देखने पर बेहद सरल लगता है — आप किसी टॉपिक पर पब्लिश करते हैं, कोई उसे पढ़ता है। पर्दे के पीछे यह एक काफी जटिल वितरित प्रणाली है जहाँ कई घटकों को यह तय करना होता है कि कौन किसका मालिक है, इससे पहले कि एक भी बाइट डिलीवर हो। मैंने इसे सुलझाने में काफी समय लगाया, और अधिकांश लेख “पार्टिशन आपको समानांतरता देते हैं” पर रुक जाते हैं, बिना वास्तविक हैंडशेक समझाए। आइए गहराई में जाएँ।

Kafka क्लस्टर वास्तव में क्या है

Kafka क्लस्टर ब्रोकर्स का एक समूह है — सामान्य JVM प्रोसेस, प्रत्येक अपनी मशीन (या कंटेनर) पर चलता है। हर ब्रोकर डेटा का एक हिस्सा स्टोर करता है और बाकी क्लस्टर के बारे में जानता है [1]।

टॉपिक वह लॉजिकल यूनिट है जिसके साथ आप इंटरैक्ट करते हैं। भौतिक रूप से, एक टॉपिक पार्टिशन में विभाजित होता है, और प्रत्येक पार्टिशन कई ब्रोकर्स में रेप्लिका के रूप में फैला होता है। उनमें से एक रेप्लिका लीडर होता है — वही एकमात्र जो उस पार्टिशन के रीड और राइट संभालता है। बाकी फॉलोअर हैं जो चुपचाप लीडर के डेटा को रेप्लिकेट करते हैं [1]।

तो जब लोग कहते हैं “Kafka क्षैतिज रूप से स्केल करता है,” इसका अर्थ है: एक टॉपिक के लिए 100 पार्टिशन हो सकते हैं, और वे 100 पार्टिशन N ब्रोकर्स में फैले हो सकते हैं — प्रत्येक ब्रोकर एक अलग स्लाइस संभालता है, आपके ऐप का प्रत्येक कंज्यूमर समानांतर में एक अलग स्लाइस पढ़ता है।

kafka cluster overview

क्लस्टर को कौन कोऑर्डिनेट करता है — ZooKeeper बनाम KRaft

यहीं पर बहुत से पुराने लेख लोगों को भ्रमित करते हैं। ऐतिहासिक रूप से, Kafka ने अपना क्लस्टर कोऑर्डिनेशन ZooKeeper को आउटसोर्स किया था। हर ब्रोकर ZooKeeper में एक एफेमेरल नोड बनाने की दौड़ में लगता था; पहला सफल होने वाला Controller ब्रोकर बन जाता था — वह जो क्लस्टर भर में पार्टिशन लीडर असाइन करने के लिए जिम्मेदार था [2]।

वह आर्किटेक्चर अब नहीं रहा। Kafka 3.3 से इसे डिप्रेकेट किया गया है, और Kafka 4.0 से यह बस सपोर्टेड नहीं है [3]। इसका रिप्लेसमेंट KRaft (Kafka Raft) कहलाता है।

KRaft: Kafka अपना कंसेंसस खुद चलाता है

KRaft मोड में, ब्रोकर्स का एक उपसमूह (या डेडिकेटेड नोड्स) कंट्रोलर के रूप में कार्य करते हैं और एक Raft quorum बनाते हैं। वे Raft कंसेंसस एल्गोरिदम का उपयोग करके अपने बीच एक लीडर चुनते हैं — बहुमत वोट, पुराने लीडर को रोकने के लिए टर्म नंबर, और quorum को consistent रखने के लिए लॉग रेप्लिकेशन [4]।

सभी क्लस्टर मेटाडेटा — टॉपिक कॉन्फिग, पार्टिशन असाइनमेंट, ब्रोकर रजिस्ट्रेशन — __cluster_metadata नामक एक आंतरिक Kafka टॉपिक में रहता है। सक्रिय कंट्रोलर लीडर इस लॉग में इवेंट लिखता है; फॉलोअर कंट्रोलर उन्हें रीप्ले करते हैं [3]। ब्रोकर इस मेटाडेटा स्ट्रीम को सब्सक्राइब करते हैं और अपना लोकल व्यू अपडेट रखते हैं।

यह क्यों मायने रखता है? क्योंकि:

  • फेलओवर तेज है — कोई बाहरी ZooKeeper कोऑर्डिनेशन राउंड-ट्रिप नहीं [2]
  • मेटाडेटा लॉग खुद Kafka की गारंटी के साथ रेप्लिकेट होता है
  • आप कम चलते-पुर्जे डिप्लॉय करते हैं

पार्टिशन लीडर कैसे चुने जाते हैं

जब एक ब्रोकर मर जाता है, तो किसी को उसकी पार्टिशन लीडरशिप लेनी होती है। यह काम KRaft कंट्रोलर का है।

कंट्रोलर ISR लिस्ट (In-Sync Replicas) का उपयोग करके तय करता है कि नया लीडर कौन हो सकता है [5]। ISR उन फॉलोअर रेप्लिका का सेट है जो लीडर के साथ पूरी तरह अप-टू-डेट हैं — पिछड़े हुए रेप्लिका ISR से बाहर कर दिए जाते हैं। तो अगर कोई लीडर फेल होता है:

  1. कंट्रोलर पता लगाता है कि ब्रोकर गया
  2. वह मृत ब्रोकर द्वारा लीड किए जाने वाले प्रत्येक पार्टिशन के लिए ISR देखता है
  3. वह एक ISR सदस्य को चुनकर लीडर बनाता है
  4. वह उस निर्णय को __cluster_metadata में लिखता है
  5. अन्य सभी ब्रोकर मेटाडेटा स्ट्रीम से नया लीडर जान लेते हैं

लीडर चुनाव के लिए केवल ISR सदस्य पात्र हैं। यह महत्वपूर्ण है — यह गारंटी देता है कि नए लीडर के पास सभी कमिटेड डेटा है और कंज्यूमर को कोई गैप नहीं दिखता [5]।

कंज्यूमर वास्तव में सही ब्रोकर कैसे खोजता है

यह वह हिस्सा है जिसे ज्यादातर लोग नजरअंदाज कर देते हैं। कंज्यूमर बस जादुई तरीके से सही ब्रोकर से नहीं जुड़ता। इसके लिए एक विशेष हैंडशेक होता है।

चरण 1 — बूटस्ट्रैप (एकमुश्त क्लस्टर डिस्कवरी)

आप bootstrap.servers को एक या अधिक ब्रोकर पतों के साथ कॉन्फिगर करते हैं। यह सूची केवल प्रारंभिक डिस्कवरी के लिए है, चल रहे ट्रैफिक के लिए नहीं [6]।

क्लाइंट सूची से कोई भी पता चुनता है, TCP कनेक्शन खोलता है, और एक Metadata रिक्वेस्ट भेजता है। कोई भी ब्रोकर इसका जवाब दे सकता है — यह पूरी ब्रोकर लिस्ट और क्लाइंट को जिन टॉपिक की परवाह है उनके लिए पार्टिशन-टू-लीडर मैपिंग लौटाता है [6]। उस एकल रिक्वेस्ट के बाद, क्लाइंट के पास क्लस्टर का पूरा नक्शा होता है और वह सीधे जरूरत के लीडर से जुड़ता है।

अगर आपका बूटस्ट्रैप ब्रोकर एक घंटे बाद मर जाए, क्लाइंट पहले से ठीक है — उसे बाकी क्लस्टर के बारे में पता है [6]।

चरण 2 — ग्रुप कोऑर्डिनेटर खोजना

कंज्यूमर बस किसी ब्रोकर से नहीं जुड़ते। हर कंज्यूमर ग्रुप एक विशेष ग्रुप कोऑर्डिनेटर ब्रोकर से जुड़ा होता है।

मैपिंग निर्धारित है: Kafka group.id स्ट्रिंग को आंतरिक __consumer_offsets टॉपिक के एक पार्टिशन पर हैश करता है, और उस पार्टिशन का लीडर ग्रुप कोऑर्डिनेटर होता है [7]। एक ही group.id वाले सभी कंज्यूमर एक ही कोऑर्डिनेटर से बात करते हैं। अलग-अलग ग्रुप अलग-अलग कोऑर्डिनेटर में वितरित होते हैं।

कंज्यूमर किसी भी ब्रोकर को FindCoordinator रिक्वेस्ट भेजता है (जिसमें उसकी group.id होती है)। वह ब्रोकर हैश रिज़ॉल्व करता है और कोऑर्डिनेटर का होस्ट और पोर्ट वापस भेजता है [7]।

चरण 3 — JoinGroup और पार्टिशन असाइनमेंट

एक बार जब ग्रुप का हर कंज्यूमर कोऑर्डिनेटर को ढूंढ लेता है, तो rebalance प्रोटोकॉल शुरू होता है:

  1. प्रत्येक कंज्यूमर कोऑर्डिनेटर को JoinGroup रिक्वेस्ट भेजता है
  2. कोऑर्डिनेटर एक कंज्यूमर को ग्रुप लीडर नामांकित करता है — आमतौर पर पहले जुड़ने वाला
  3. कोऑर्डिनेटर ग्रुप लीडर को पूरी सदस्य सूची भेजता है
  4. ग्रुप लीडर लोकली एक पार्टिशन असाइनमेंट स्ट्रेटेजी चलाता है और परिणाम वापस भेजता है
  5. कोऑर्डिनेटर SyncGroup रिस्पॉन्स के जरिए हर सदस्य को असाइनमेंट वितरित करता है [7][8]

ग्रुप लीडर केवल एक अस्थायी लॉजिकल रोल है, स्थायी नोड नहीं। यह हर rebalance पर बदलता है [7]।

पार्टिशन असाइनमेंट स्ट्रेटेजी

Kafka कुछ बिल्ट-इन स्ट्रेटेजी के साथ आता है, जिन्हें partition.assignment.strategy के जरिए कॉन्फिगर किया जा सकता है [8]:

स्ट्रेटेजीयह कैसे काम करती हैकिसके लिए सबसे अच्छी
RangeAssignorकंज्यूमर्स में पार्टिशन को संख्यात्मक रूप से विभाजित करता हैसरल, प्रति टॉपिक अनुमानित विभाजन
RoundRobinAssignorकंज्यूमर्स में सर्कुलर वितरणटॉपिक्स में समान वितरण
StickyAssignorपूर्व असाइनमेंट रखता है, rebalance पर मूवमेंट कम करता हैवार्म कैश वाले stateful कंज्यूमर
CooperativeStickyAssignorSticky जैसा पर incremental rebalance की अनुमति देता है (3.0+ में डिफ़ॉल्ट)प्रोडक्शन — stop-the-world rebalance से बचाता है

CooperativeStickyAssignor Kafka 3.0 से डिफ़ॉल्ट है क्योंकि यह incremental rebalance करता है — केवल वे पार्टिशन जिन्हें वास्तव में मूव होना है, रिवोक किए जाते हैं, एक साथ सभी नहीं [8]।

कंज्यूमर क्या पढ़ सकते हैं और क्या नहीं

एक सूक्ष्म बात: कंज्यूमर पार्टिशन लीडर से पढ़ते हैं, फॉलोअर से नहीं [5]। तो भले ही एक फॉलोअर रेप्लिका आपके कंज्यूमर के भौतिक रूप से करीब ब्रोकर पर हो, रीड फिर भी लीडर के पास जाता है।

एक अपवाद है — Follower Fetching (Kafka 2.4 में पेश किया गया), जहाँ कंज्यूमर को निकटतम रेप्लिका से पढ़ने के लिए कॉन्फिगर किया जा सकता है। लेकिन डिफ़ॉल्ट रूप से, केवल लीडर रीड।

कंज्यूमर हाई-वॉटर मार्क से परे का डेटा भी नहीं पढ़ सकते — वह नवीनतम ऑफसेट जिसे सभी ISR रेप्लिका ने स्वीकार किया है [5]। यह कंज्यूमर को ऐसा डेटा पढ़ने से रोकता है जो रोलबैक हो सकता है अगर फॉलोअर के कैच अप होने से पहले लीडर मर जाए।

कंज्यूम करते समय ब्रोकर के फेल होने पर क्या होता है

मान लें एक कंज्यूमर पार्टिशन 1 पढ़ रहा है, और उसका लीडर ब्रोकर क्रैश हो जाता है:

  1. KRaft कंट्रोलर ब्रोकर की विफलता का पता लगाता है
  2. वह पार्टिशन 1 के लिए ISR से नया लीडर चुनता है
  3. वह __cluster_metadata अपडेट करता है
  4. कंज्यूमर का अगला fetch रिक्वेस्ट पुराने ब्रोकर से NOT_LEADER_OR_FOLLOWER एरर (या टाइमआउट) पाता है
  5. कंज्यूमर स्वचालित रूप से अपना मेटाडेटा रिफ्रेश करता है और fetch को retry करता है — अब नए लीडर के पास [6]

कंज्यूमर को मैनुअल हस्तक्षेप की जरूरत नहीं। क्लाइंट लाइब्रेरी retry और मेटाडेटा रिफ्रेश को पारदर्शी रूप से संभालती है। आपके एप्लिकेशन कोड के नजरिए से, एक संक्षिप्त विराम हो सकता है, लेकिन मेसेज खोए या डुप्लिकेट नहीं होते (सही ऑफसेट कमिट सेटिंग मानते हुए)।

एक फ्लो में पूरी तस्वीर

kafka consumer flow

  • बूटस्ट्रैप ब्रोकर — केवल एक बार उपयोग, पूरा क्लस्टर मैप पाने के लिए
  • ग्रुप कोऑर्डिनेटर — कंज्यूमर ग्रुप के लिए join/sync/heartbeat संभालता है
  • पार्टिशन लीडर — वास्तविक ब्रोकर जिससे कंज्यूमर मेसेज fetch करता है

ये तीन अलग-अलग ब्रोकर हो सकते हैं, या एक ही। Kafka को परवाह नहीं।

यह डिज़ाइन टिकाऊ क्यों है

आर्किटेक्चर वास्तव में सुंदर है जब आप इसे पूरा देखते हैं। मेटाडेटा डिस्कवरी, कोऑर्डिनेशन से अलग है, जो डेटा ट्रांसफर से अलग है। प्रत्येक लेयर का एक स्पष्ट मालिक है — क्लस्टर स्टेट के लिए KRaft, कंज्यूमर ग्रुप स्टेट के लिए ग्रुप कोऑर्डिनेटर, डेटा के लिए पार्टिशन लीडर।

किसी भी लेयर पर विफलताओं के लिए परिभाषित fallback पथ हैं। मृत बूटस्ट्रैप ब्रोकर स्टार्टअप के बाद अप्रासंगिक है। मृत ग्रुप कोऑर्डिनेटर FindCoordinator retry को ट्रिगर करता है। मृत पार्टिशन लीडर मेटाडेटा रिफ्रेश और reconnect को ट्रिगर करता है।

लागू करने में सरल नहीं — लेकिन एक ऑपरेटर के रूप में तर्क करने में बहुत साफ।

समाप्त

स्रोत

  1. Kafka Topics, Partitions, and Brokers: Core Architecture — Conduktor
  2. ZooKeeper and Apache Kafka® Explained: From Legacy to KRaft — Confluent
  3. KRaft vs ZooKeeper — Apache Kafka Official Docs
  4. KRaft Explained: How Kafka Implements Raft Consensus — Medium
  5. Understanding In-Sync Replicas (ISR) in Apache Kafka — GeeksforGeeks
  6. What is a Kafka Bootstrap Server? — Confluent
  7. Apache Kafka® Internal Architecture — Consumer Group Protocol — Confluent Developer
  8. How to Implement Kafka Consumer Assignment Strategies — OneUptime