सभी को नमस्कार! मैं दिमित्री अपानासेविच हूँ, MY.GAMES में जावा डेवलपर, रश रॉयल गेम पर काम कर रहा हूँ, और मैं अपने जावा बैकएंड में ओपनटेलीमेट्री फ्रेमवर्क को एकीकृत करने के अपने अनुभव को साझा करना चाहता हूँ। यहाँ कवर करने के लिए बहुत कुछ है: हम इसे लागू करने के लिए आवश्यक कोड परिवर्तनों को कवर करेंगे, साथ ही नए घटकों को भी जिन्हें हमें इंस्टॉल और कॉन्फ़िगर करने की आवश्यकता थी - और, ज़ाहिर है, हम अपने कुछ परिणाम साझा करेंगे।
हमारा लक्ष्य: सिस्टम अवलोकनीयता प्राप्त करना
आइए अपने मामले को कुछ और संदर्भ दें। डेवलपर्स के रूप में, हम ऐसा सॉफ़्टवेयर बनाना चाहते हैं जिसे मॉनिटर करना, मूल्यांकन करना और समझना आसान हो (और ओपनटेलीमेट्री को लागू करने का यही उद्देश्य है - सिस्टम को अधिकतम करना
अनुप्रयोग प्रदर्शन के बारे में जानकारी एकत्र करने के पारंपरिक तरीकों में अक्सर घटनाओं, मीट्रिक्स और त्रुटियों को मैन्युअल रूप से लॉग करना शामिल होता है:
बेशक, ऐसे कई फ्रेमवर्क हैं जो हमें लॉग के साथ काम करने की अनुमति देते हैं, और मुझे यकीन है कि इस लेख को पढ़ने वाले प्रत्येक व्यक्ति के पास लॉग एकत्र करने, भंडारण और विश्लेषण करने के लिए एक कॉन्फ़िगर सिस्टम होगा।
हमारे लिए लॉगिंग भी पूरी तरह से कॉन्फ़िगर की गई थी, इसलिए हमने लॉग के साथ काम करने के लिए ओपनटेलीमेट्री द्वारा प्रदान की गई क्षमताओं का उपयोग नहीं किया।
सिस्टम की निगरानी करने का एक अन्य सामान्य तरीका मेट्रिक्स का लाभ उठाना है:
हमारे पास मेट्रिक्स एकत्रित करने और उन्हें प्रदर्शित करने के लिए एक पूर्णतः कॉन्फ़िगर प्रणाली भी थी, इसलिए यहां भी हमने मेट्रिक्स के साथ काम करने के मामले में ओपनटेलीमेट्री की क्षमताओं को नजरअंदाज कर दिया।
लेकिन इस तरह के सिस्टम डेटा को प्राप्त करने और उसका विश्लेषण करने के लिए एक कम सामान्य उपकरण है
ट्रेस उस पथ का प्रतिनिधित्व करता है जो एक अनुरोध अपने जीवनकाल के दौरान हमारे सिस्टम से होकर गुजरता है, और यह आमतौर पर तब शुरू होता है जब सिस्टम एक अनुरोध प्राप्त करता है और प्रतिक्रिया के साथ समाप्त होता है। ट्रेस में कई होते हैं
इस चर्चा में, हम ओपनटेलीमेट्री के ट्रेसिंग पहलू पर ध्यान केंद्रित करेंगे।
ओपनटेलीमेट्री पर कुछ और पृष्ठभूमि
आइए ओपनटेलीमेट्री परियोजना पर भी कुछ प्रकाश डालें, जो
ओपनटेलीमेट्री अब एक मानक के आधार पर घटकों की एक व्यापक श्रृंखला प्रदान करता है जो विभिन्न प्रोग्रामिंग भाषाओं के लिए एपीआई, एसडीके और उपकरणों के एक सेट को परिभाषित करता है, और परियोजना का प्राथमिक लक्ष्य डेटा उत्पन्न करना, एकत्र करना, प्रबंधित करना और निर्यात करना है।
जैसा कि कहा गया है, ओपनटेलीमेट्री डेटा भंडारण या विज़ुअलाइज़ेशन टूल के लिए बैकएंड प्रदान नहीं करता है।
चूंकि हम केवल ट्रेसिंग में रुचि रखते थे, इसलिए हमने ट्रेस को संग्रहीत करने और दृश्यमान करने के लिए सबसे लोकप्रिय ओपन-सोर्स समाधानों की खोज की:
- शुद्ध ऊनी कपड़ा
- ज़िपकिन
- ग्राफाना टेम्पो
अंततः, हमने ग्राफाना टेम्पो को इसकी प्रभावशाली विज़ुअलाइज़ेशन क्षमताओं, तेज़ विकास गति और मेट्रिक्स विज़ुअलाइज़ेशन के लिए हमारे मौजूदा ग्राफाना सेटअप के साथ एकीकरण के कारण चुना। एकल, एकीकृत उपकरण होना भी एक महत्वपूर्ण लाभ था।
ओपनटेलीमेट्री घटक
आइये ओपनटेलीमेट्री के घटकों का भी थोड़ा विश्लेषण करें।
विनिर्देश:
API — डेटा के प्रकार, ऑपरेशन, enums
SDK — विनिर्देशन कार्यान्वयन, विभिन्न प्रोग्रामिंग भाषाओं पर API. एक अलग भाषा का मतलब है एक अलग SDK स्थिति, अल्फा से स्थिर तक.
डेटा प्रोटोकॉल (ओटीएलपी) और
अर्थगत परंपराएँ
जावा एपीआई एसडीके:
- कोड इंस्ट्रूमेंटेशन लाइब्रेरीज़
- निर्यातक - उत्पन्न ट्रेस को बैकएंड पर निर्यात करने के लिए उपकरण
- क्रॉस सर्विस प्रोपेगेटर्स - प्रक्रिया के बाहर निष्पादन संदर्भ को स्थानांतरित करने के लिए एक उपकरण (JVM)
ओपनटेलीमेट्री कलेक्टर एक महत्वपूर्ण घटक है, एक प्रॉक्सी जो डेटा प्राप्त करता है, उसे संसाधित करता है, और आगे भेजता है - आइए इस पर करीब से नज़र डालें।
ओपनटेलीमेट्री कलेक्टर
प्रति सेकंड हज़ारों अनुरोधों को संभालने वाले उच्च-लोड सिस्टम के लिए, डेटा वॉल्यूम का प्रबंधन करना महत्वपूर्ण है। ट्रेस डेटा अक्सर वॉल्यूम में व्यावसायिक डेटा से आगे निकल जाता है, जिससे यह प्राथमिकता देना ज़रूरी हो जाता है कि कौन सा डेटा इकट्ठा और संग्रहीत किया जाए। यहीं पर हमारा डेटा प्रोसेसिंग और फ़िल्टरिंग टूल काम आता है और आपको यह निर्धारित करने में सक्षम बनाता है कि कौन सा डेटा संग्रहीत करने योग्य है। आम तौर पर, टीमें ऐसे ट्रेस संग्रहीत करना चाहती हैं जो विशिष्ट मानदंडों को पूरा करते हों, जैसे:
- एक निश्चित सीमा से अधिक प्रतिक्रिया समय वाले ट्रेस।
- प्रसंस्करण के दौरान त्रुटियों का सामना करने वाले निशान।
- ऐसे ट्रेस जिनमें विशिष्ट विशेषताएं होती हैं, जैसे कि वे जो किसी निश्चित माइक्रोसर्विस से होकर गुजरे हों या जिन्हें कोड में संदिग्ध के रूप में चिह्नित किया गया हो।
- नियमित ट्रेसों का एक यादृच्छिक चयन जो सिस्टम के सामान्य संचालन का एक सांख्यिकीय स्नैपशॉट प्रदान करता है, जिससे आपको विशिष्ट व्यवहार को समझने और प्रवृत्तियों की पहचान करने में मदद मिलती है।
यहां दो मुख्य नमूनाकरण विधियां दी गई हैं जिनका उपयोग यह निर्धारित करने के लिए किया जाता है कि किन ट्रेसों को बचाना है और किन को त्यागना है:
- हेड सैंपलिंग - ट्रेस के शुरू में ही यह निर्णय ले लेता है कि उसे रखना है या नहीं
- टेल सैंपलिंग - पूरा ट्रेस उपलब्ध होने के बाद ही निर्णय लेता है। यह तब आवश्यक होता है जब निर्णय उस डेटा पर निर्भर करता है जो ट्रेस में बाद में दिखाई देता है। उदाहरण के लिए, त्रुटि अवधि सहित डेटा। इन मामलों को हेड सैंपलिंग द्वारा नियंत्रित नहीं किया जा सकता क्योंकि इनमें पहले पूरे ट्रेस का विश्लेषण करने की आवश्यकता होती है
ओपनटेलीमेट्री कलेक्टर डेटा संग्रह प्रणाली को कॉन्फ़िगर करने में मदद करता है ताकि यह केवल आवश्यक डेटा को सहेज सके। हम इसके कॉन्फ़िगरेशन पर बाद में चर्चा करेंगे, लेकिन अभी के लिए, आइए इस सवाल पर चलते हैं कि कोड में क्या बदलाव करने की आवश्यकता है ताकि यह ट्रेस उत्पन्न करना शुरू कर दे।
शून्य-कोड उपकरण
ट्रेस जेनरेशन प्राप्त करने के लिए वास्तव में न्यूनतम कोडिंग की आवश्यकता थी - यह केवल एक जावा-एजेंट के साथ हमारे अनुप्रयोगों को लॉन्च करने के लिए आवश्यक था,
-javaagent:/opentelemetry-javaagent-1.29.0.jar
-Dotel.javaagent.configuration-file=/otel-config.properties
ओपनटेलीमेट्री बहुत बड़ी संख्या में समर्थन करता है
हमारे एजेंट कॉन्फ़िगरेशन में, हमने उन लाइब्रेरीज़ को अक्षम कर दिया जिनका हम उपयोग कर रहे थे और जिनके स्पैन को हम ट्रेस में नहीं देखना चाहते थे, और हमारे कोड ने कैसे काम किया, इसके बारे में डेटा प्राप्त करने के लिए, हमने इसे चिह्नित किया
@WithSpan("acquire locks") public CompletableFuture<Lock> acquire(SortedSet<Object> source) { var traceLocks = source.stream().map(Object::toString).collect(joining(", ")); Span.current().setAttribute("locks", traceLocks); return CompletableFuture.supplyAsync(() -> /* async job */); }
इस उदाहरण में, विधि के लिए @WithSpan
एनोटेशन का उपयोग किया जाता है, जो " acquire locks
" नामक एक नया स्पैन बनाने की आवश्यकता का संकेत देता है, और " locks
" विशेषता को विधि निकाय में बनाए गए स्पैन में जोड़ा जाता है।
जब विधि काम करना समाप्त कर देती है, तो स्पैन बंद हो जाता है, और एसिंक्रोनस कोड के लिए इस विवरण पर ध्यान देना महत्वपूर्ण है। यदि आपको एनोटेटेड विधि से कॉल किए गए लैम्ब्डा फ़ंक्शन में एसिंक्रोनस कोड के काम से संबंधित डेटा प्राप्त करने की आवश्यकता है, तो आपको इन लैम्ब्डा को अलग-अलग विधियों में अलग करना होगा और उन्हें एक अतिरिक्त एनोटेशन के साथ चिह्नित करना होगा।
हमारा ट्रेस संग्रह सेटअप
अब, आइए बात करते हैं कि संपूर्ण ट्रेस संग्रह प्रणाली को कैसे कॉन्फ़िगर किया जाए। हमारे सभी JVM एप्लिकेशन एक जावा एजेंट के साथ लॉन्च किए जाते हैं जो OpenTelemetry कलेक्टर को डेटा भेजता है।
हालाँकि, एक एकल कलेक्टर एक बड़े डेटा प्रवाह को संभाल नहीं सकता है और सिस्टम के इस हिस्से को स्केल किया जाना चाहिए। यदि आप प्रत्येक JVM एप्लिकेशन के लिए एक अलग कलेक्टर लॉन्च करते हैं, तो टेल सैंपलिंग टूट जाएगी, क्योंकि ट्रेस विश्लेषण एक कलेक्टर पर होना चाहिए, और यदि अनुरोध कई JVM से होकर जाता है, तो एक ट्रेस का स्पैन अलग-अलग कलेक्टरों पर समाप्त हो जाएगा और उनका विश्लेषण असंभव होगा।
यहां एक
परिणामस्वरूप, हमें निम्न सिस्टम मिलता है: प्रत्येक JVM एप्लिकेशन एक ही बैलेंसर कलेक्टर को डेटा भेजता है, जिसका एकमात्र कार्य अलग-अलग एप्लिकेशन से प्राप्त डेटा को वितरित करना है, लेकिन किसी दिए गए ट्रेस से संबंधित, उसी कलेक्टर-प्रोसेसर को। फिर, कलेक्टर-प्रोसेसर डेटा को ग्राफाना टेम्पो को भेजता है।
आइये इस प्रणाली के घटकों के विन्यास पर करीब से नज़र डालें।
लोड संतुलन संग्राहक
कलेक्टर-बैलेंसर कॉन्फ़िगरेशन में, हमने निम्नलिखित मुख्य भागों को कॉन्फ़िगर किया है:
receivers: otlp: protocols: grpc: exporters: loadbalancing: protocol: otlp: tls: insecure: true resolver: static: hostnames: - collector-1.example.com:4317 - collector-2.example.com:4317 - collector-3.example.com:4317 service: pipelines: traces: receivers: [otlp] exporters: [loadbalancing]
- रिसीवर - जहाँ विधियाँ (जिसके माध्यम से कलेक्टर द्वारा डेटा प्राप्त किया जा सकता है) कॉन्फ़िगर की जाती हैं। हमने डेटा रिसेप्शन को केवल OTLP प्रारूप में कॉन्फ़िगर किया है। (डेटा के रिसेप्शन को कॉन्फ़िगर करना संभव है
कई अन्य प्रोटोकॉल (उदाहरण के लिए जिपकिन, जैगर.) - निर्यातक - कॉन्फ़िगरेशन का वह भाग जहाँ डेटा संतुलन कॉन्फ़िगर किया जाता है। इस अनुभाग में निर्दिष्ट कलेक्टर-प्रोसेसरों के बीच, डेटा ट्रेस पहचानकर्ता से गणना किए गए हैश के आधार पर वितरित किया जाता है।
- सेवा अनुभाग यह निर्दिष्ट करता है कि सेवा किस प्रकार कार्य करेगी: केवल ट्रेस के साथ, शीर्ष पर कॉन्फ़िगर किए गए OTLP रिसीवर का उपयोग करके और बैलेंसर के रूप में डेटा संचारित करना, अर्थात प्रसंस्करण के बिना।
डेटा प्रोसेसिंग के साथ कलेक्टर
कलेक्टर-प्रोसेसर का विन्यास अधिक जटिल है, तो आइए वहां एक नजर डालते हैं:
receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:14317 processors: tail_sampling: decision_wait: 10s num_traces: 100 expected_new_traces_per_sec: 10 policies: [ { name: latency500-policy, type: latency, latency: {threshold_ms: 500} }, { name: error-policy, type: string_attribute, string_attribute: {key: error, values: [true, True]} }, { name: probabilistic10-policy, type: probabilistic, probabilistic: {sampling_percentage: 10} } ] resource/delete: attributes: - key: process.command_line action: delete - key: process.executable.path action: delete - key: process.pid action: delete - key: process.runtime.description action: delete - key: process.runtime.name action: delete - key: process.runtime.version action: delete exporters: otlp: endpoint: tempo:4317 tls: insecure: true service: pipelines: traces: receivers: [otlp] exporters: [otlp]
कलेक्टर-बैलेंसर कॉन्फ़िगरेशन के समान, प्रोसेसिंग कॉन्फ़िगरेशन में रिसीवर, एक्सपोर्टर्स और सर्विस सेक्शन शामिल हैं। हालाँकि, हम प्रोसेसर सेक्शन पर ध्यान केंद्रित करेंगे, जो बताता है कि डेटा कैसे प्रोसेस किया जाता है।
सबसे पहले, tail_sampling अनुभाग एक प्रदर्शन करता है
latency500-policy : यह नियम 500 मिलीसेकंड से अधिक विलंबता वाले ट्रेस का चयन करता है।
error-policy : यह नियम उन ट्रेस का चयन करता है, जिनमें प्रोसेसिंग के दौरान त्रुटियाँ आईं। यह ट्रेस स्पैन में "true" या "True" मानों के साथ "error" नामक स्ट्रिंग विशेषता की खोज करता है।
probabilistic10-policy : यह नियम सामान्य अनुप्रयोग संचालन, त्रुटियों और लंबे अनुरोध प्रसंस्करण में अंतर्दृष्टि प्रदान करने के लिए सभी ट्रेसों में से 10% का यादृच्छिक रूप से चयन करता है।
tail_sampling के अतिरिक्त, यह उदाहरण डेटा विश्लेषण और भंडारण के लिए आवश्यक नहीं अनावश्यक विशेषताओं को हटाने के लिए resource/delete अनुभाग दिखाता है।
परिणाम
परिणामी ग्राफ़ाना ट्रेस खोज विंडो आपको विभिन्न मानदंडों के अनुसार डेटा फ़िल्टर करने में सक्षम बनाती है। इस उदाहरण में, हम केवल लॉबी सेवा से प्राप्त ट्रेस की एक सूची प्रदर्शित करते हैं, जो गेम मेटाडेटा को संसाधित करती है। कॉन्फ़िगरेशन विलंबता, त्रुटियों और यादृच्छिक नमूने जैसी विशेषताओं द्वारा भविष्य में फ़िल्टरिंग की अनुमति देता है।
ट्रेस दृश्य विंडो लॉबी सेवा की निष्पादन समयरेखा प्रदर्शित करती है, जिसमें अनुरोध को बनाने वाले विभिन्न स्पैन शामिल होते हैं।
जैसा कि आप चित्र में देख सकते हैं, घटनाओं का क्रम इस प्रकार है - लॉक प्राप्त किए जाते हैं, फिर कैश से ऑब्जेक्ट प्राप्त किए जाते हैं, उसके बाद अनुरोधों को संसाधित करने वाले लेनदेन का निष्पादन होता है, जिसके बाद ऑब्जेक्ट को पुनः कैश में संग्रहीत किया जाता है और लॉक जारी कर दिए जाते हैं।
मानक पुस्तकालयों के उपकरण के कारण डेटाबेस अनुरोधों से संबंधित स्पैन स्वचालित रूप से उत्पन्न किए गए थे। इसके विपरीत, लॉक प्रबंधन, कैश संचालन और लेनदेन आरंभ से संबंधित स्पैन को उपर्युक्त एनोटेशन का उपयोग करके मैन्युअल रूप से व्यवसाय कोड में जोड़ा गया था।
किसी स्पैन को देखते समय, आप वे विशेषताएं देख सकते हैं जो आपको प्रसंस्करण के दौरान क्या हुआ, इसे बेहतर ढंग से समझने में मदद करती हैं, उदाहरण के लिए, डेटाबेस में कोई क्वेरी देखना।
ग्राफाना टेम्पो की दिलचस्प विशेषताओं में से एक है
ऊपर लपेटकर
जैसा कि हमने देखा है, ओपनटेलीमेट्री ट्रेसिंग के साथ काम करने से हमारी अवलोकन क्षमताएँ काफी अच्छी तरह से बढ़ी हैं। न्यूनतम कोड परिवर्तन और एक अच्छी तरह से संरचित कलेक्टर सेटअप के साथ, हमें गहरी अंतर्दृष्टि मिली - साथ ही, हमने देखा कि कैसे ग्राफ़ाना टेम्पो की विज़ुअलाइज़ेशन क्षमताओं ने हमारे सेटअप को और अधिक पूरक बनाया। पढ़ने के लिए धन्यवाद!