फ्लाइट रिकॉर्डर चल रहे एप्लिकेशन के आंतरिक कार्यप्रणाली का निदान करने के लिए Go डेवलपर के टूलबॉक्स में नवीनतम जोड़ है।फ्लाइट रिकॉर्डर चल रहे एप्लिकेशन के आंतरिक कार्यप्रणाली का निदान करने के लिए Go डेवलपर के टूलबॉक्स में नवीनतम जोड़ है।

फ्लाइट रिकॉर्डर: एक नया Go एक्जीक्यूशन ट्रेसर

2025/12/13 23:00

2024 में हमने दुनिया को अधिक शक्तिशाली Go निष्पादन ट्रेस से परिचित कराया। उस ब्लॉग पोस्ट में हमने कुछ नई कार्यक्षमताओं की झलक दिखाई थी जिन्हें हम अपने नए निष्पादन ट्रेसर के साथ अनलॉक कर सकते थे, जिसमें फ्लाइट रिकॉर्डिंग भी शामिल है। हमें यह घोषणा करते हुए खुशी हो रही है कि फ्लाइट रिकॉर्डिंग अब Go 1.25 में उपलब्ध है, और यह Go डायग्नोस्टिक्स टूलबॉक्स में एक शक्तिशाली नया उपकरण है।

निष्पादन ट्रेस

सबसे पहले, Go निष्पादन ट्रेस पर एक संक्षिप्त पुनरावलोकन।

\ Go रनटाइम को एक Go एप्लिकेशन के निष्पादन के दौरान होने वाली कई घटनाओं को रिकॉर्ड करने वाला लॉग लिखने के लिए बनाया जा सकता है। उस लॉग को रनटाइम निष्पादन ट्रेस कहा जाता है। Go निष्पादन ट्रेस में इस बारे में बहुत सारी जानकारी होती है कि गोरूटीन्स एक-दूसरे के साथ और अंतर्निहित सिस्टम के साथ कैसे इंटरैक्ट करते हैं। यह उन्हें लेटेंसी समस्याओं को डीबग करने के लिए बहुत उपयोगी बनाता है, क्योंकि वे आपको बताते हैं कि आपके गोरूटीन्स कब निष्पादित हो रहे हैं, और महत्वपूर्ण रूप से, कब नहीं हो रहे हैं।

\ runtime/trace पैकेज runtime/trace.Start और runtime/trace.Stop को कॉल करके एक निश्चित समय अवधि पर निष्पादन ट्रेस एकत्र करने के लिए एक API प्रदान करता है। यह अच्छी तरह से काम करता है यदि आप जिस कोड को ट्रेस कर रहे हैं वह सिर्फ एक टेस्ट, माइक्रोबेंचमार्क, या कमांड लाइन टूल है। आप पूरे एंड-टू-एंड निष्पादन का ट्रेस एकत्र कर सकते हैं, या सिर्फ वे भाग जिनकी आप परवाह करते हैं।

\ हालांकि, लंबे समय तक चलने वाली वेब सेवाओं में, जिस प्रकार के एप्लिकेशन के लिए Go जाना जाता है, यह पर्याप्त नहीं है। वेब सर्वर दिनों या यहां तक कि हफ्तों तक चल सकते हैं, और पूरे निष्पादन का ट्रेस एकत्र करने से बहुत अधिक डेटा उत्पन्न होगा जिसे छानना मुश्किल होगा। अक्सर प्रोग्राम के निष्पादन का सिर्फ एक हिस्सा गलत हो जाता है, जैसे कि एक अनुरोध का टाइम आउट होना या एक विफल हेल्थ चेक। जब तक यह होता है, तब तक Start को कॉल करने के लिए बहुत देर हो चुकी होती है!

\ इस समस्या से निपटने का एक तरीका है पूरे फ्लीट से निष्पादन ट्रेस को यादृच्छिक रूप से सैंपल करना। हालांकि यह दृष्टिकोण शक्तिशाली है, और समस्याओं को आउटेज बनने से पहले खोजने में मदद कर सकता है, इसे शुरू करने के लिए बहुत सारे इंफ्रास्ट्रक्चर की आवश्यकता होती है। निष्पादन ट्रेस डेटा की बड़ी मात्रा को स्टोर, ट्रायेज और प्रोसेस करने की आवश्यकता होगी, जिनमें से अधिकांश में कुछ भी दिलचस्प नहीं होगा। और जब आप किसी विशिष्ट समस्या का समाधान खोजने की कोशिश कर रहे हों, तो यह एक नॉन-स्टार्टर है।

फ्लाइट रिकॉर्डिंग

यह हमें फ्लाइट रिकॉर्डर तक लाता है।

\ एक प्रोग्राम अक्सर जानता है कि कब कुछ गलत हुआ है, लेकिन मूल कारण बहुत पहले हो चुका हो सकता है। फ्लाइट रिकॉर्डर आपको निष्पादन के अंतिम कुछ सेकंड का ट्रेस एकत्र करने देता है जो उस क्षण तक जाता है जब प्रोग्राम यह पता लगाता है कि कोई समस्या हुई है।

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

उदाहरण

आइए एक उदाहरण के साथ फ्लाइट रिकॉर्डर का उपयोग करना सीखें। विशेष रूप से, आइए इसका उपयोग एक HTTP सर्वर के साथ प्रदर्शन समस्या का निदान करने के लिए करें जो "संख्या अनुमान" खेल को लागू करता है। यह एक /guess-number एंडपॉइंट प्रदान करता है जो एक पूर्णांक स्वीकार करता है और कॉलर को सूचित करता है कि क्या उन्होंने सही संख्या का अनुमान लगाया है।

\ एक गोरूटीन भी है जो, प्रति मिनट एक बार, सभी अनुमानित संख्याओं की एक रिपोर्ट HTTP अनुरोध के माध्यम से दूसरी सेवा को भेजता है।

// bucket is a simple mutex-protected counter. type bucket struct { mu sync.Mutex guesses int } func main() { // Make one bucket for each valid number a client could guess. // The HTTP handler will look up the guessed number in buckets by // using the number as an index into the slice. buckets := make([]bucket, 100) // Every minute, we send a report of how many times each number was guessed. go func() { for range time.Tick(1 * time.Minute) { sendReport(buckets) } }() // Choose the number to be guessed. answer := rand.Intn(len(buckets)) http.HandleFunc("/guess-number", func(w http.ResponseWriter, r *http.Request) { start := time.Now() // Fetch the number from the URL query variable "guess" and convert it // to an integer. Then, validate it. guess, err := strconv.Atoi(r.URL.Query().Get("guess")) if err != nil || !(0 <= guess && guess < len(buckets)) { http.Error(w, "invalid 'guess' value", http.StatusBadRequest) return } // Select the appropriate bucket and safely increment its value. b := &buckets[guess] b.mu.Lock() b.guesses++ b.mu.Unlock() // Respond to the client with the guess and whether it was correct. fmt.Fprintf(w, "guess: %d, correct: %t", guess, guess == answer) log.Printf("HTTP request: endpoint=/guess-number guess=%d duration=%s", guess, time.Since(start)) }) log.Fatal(http.ListenAndServe(":8090", nil)) } // sendReport posts the current state of buckets to a remote service. func sendReport(buckets []bucket) { counts := make([]int, len(buckets)) for index := range buckets { b := &buckets[index] b.mu.Lock() defer b.mu.Unlock() counts[index] = b.guesses } // Marshal the report data into a JSON payload. b, err := json.Marshal(counts) if err != nil { log.Printf("failed to marshal report data: error=%s", err) return } url := "http://localhost:8091/guess-number-report" if _, err := http.Post(url, "application/json", bytes.NewReader(b)); err != nil { log.Printf("failed to send report: %s", err) } }

यहां सर्वर के लिए पूरा कोड है: https://go.dev/play/p/rX1eyKtVglF, और एक सरल क्लाइंट के लिए: https://go.dev/play/p/2PjQ-1ORPiw। एक तीसरी प्रक्रिया से बचने के लिए, "क्लाइंट" भी रिपोर्ट सर्वर को लागू करता है, हालांकि एक वास्तविक सिस्टम में यह अलग होगा।

\ मान लीजिए कि प्रोडक्शन में एप्लिकेशन को डिप्लॉय करने के बाद, हमें उपयोगकर्ताओं से शिकायतें मिलीं कि कुछ /guess-number कॉल अपेक्षा से अधिक समय ले रहे थे। जब हम अपने लॉग देखते हैं, तो हम देखते हैं कि कभी-कभी प्रतिक्रिया समय 100 मिलीसेकंड से अधिक होता है, जबकि अधिकांश कॉल माइक्रोसेकंड के क्रम में होते हैं।

2025/09/19 16:52:02 HTTP request: endpoint=/guess-number guess=69 duration=625ns 2025/09/19 16:52:02 HTTP request: endpoint=/guess-number guess=62 duration=458ns 2025/09/19 16:52:02 HTTP request: endpoint=/guess-number guess=42 duration=1.417µs 2025/09/19 16:52:02 HTTP request: endpoint=/guess-number guess=86 duration=115.186167ms 2025/09/19 16:52:02 HTTP request: endpoint=/guess-number guess=0 duration=127.993375ms

आगे बढ़ने से पहले, एक मिनट लें और देखें कि क्या आप पता लगा सकते हैं कि क्या गलत है!

\ चाहे आपने समस्या का पता लगाया हो या नहीं, आइए गहराई से देखें और देखें कि हम मूल सिद्धांतों से समस्या कैसे ढूंढ सकते हैं। विशेष रूप से, यह बहुत अच्छा होगा अगर हम देख सकें कि धीमी प्रतिक्रिया से पहले के समय में एप्लिकेशन क्या कर रहा था। यह बिल्कुल वही है जिसके लिए फ्लाइट रिकॉर्डर बनाया गया था! हम इसका उपयोग निष्पादन ट्रेस कैप्चर करने के लिए करेंगे जब हम पहली प्रतिक्रिया देखें जो 100 मिलीसेकंड से अधिक है।

\ सबसे पहले, main में, हम फ्लाइट रिकॉर्डर को कॉन्फ़िगर और शुरू करेंगे:

// Set up the flight recorder fr := trace.NewFlightRecorder(trace.FlightRecorderConfig{ MinAge: 200 * time.Millisecond, MaxBytes: 1 << 20, // 1 MiB }) fr.Start()

MinAge उस अवधि को कॉन्फ़िगर करता है जिसके लिए ट्रेस डेटा विश्वसनीय रूप से बनाए रखा जाता है, और हम इसे इवेंट के समय विंडो का लगभग 2x सेट करने का सुझाव देते हैं। उदाहरण के लिए, यदि आप 5-सेकंड के टाइमआउट को डीबग कर रहे हैं, तो इसे 10 सेकंड पर सेट करें। MaxBytes बफर्ड ट्रेस के आकार को कॉन्फ़िगर करता है ताकि आप अपने मेमोरी उपयोग को बढ़ा न दें। औसतन, आप निष्पादन के प्रति सेकंड कुछ MB ट्रेस डेटा उत्पन्न होने की उम्मीद कर सकते हैं, या एक व्यस्त सेवा के लिए 10 MB/s।

\ अगला, हम स्नैपशॉट कैप्चर करने और इसे एक फाइल में लिखने के लिए एक हेल्पर फंक्शन जोड़ेंगे:

var once sync.Once // captureSnapshot captures a flight recorder snapshot. func captureSnapshot(fr *trace.FlightRecorder) { // once.Do ensures that the provided function is executed only once. once.Do(func() { f, err := os.Create("snapshot.trace") if err != nil { log.Printf("opening snapshot file %s failed: %s", f.Name(), err) return } defer f.Close() // ignore error // WriteTo writes the flight recorder data to the provided io.Writer. _, err = fr.WriteTo(f) if err != nil { log.Printf("writing snapshot to file %s failed: %s", f.Name(), err) return } // Stop the flight recorder after the snapshot has been taken. fr.Stop() log.Printf("captured a flight recorder snapshot to %s", f.Name()) }) }

\ और अंत में, पूर्ण अनुरोध को लॉग करने से ठीक पहले, हम स्नैपशॉट को ट्रिगर करेंगे यदि अनुरोध में 100 मिलीसेकंड से अधिक समय लगा:

// Capture a snapshot if the response takes more than 100ms. // Only the first call has any effect. if fr.Enabled() && time.Since(start) > 100*time.Millisecond { go captureSnapshot(fr) }

\ यहां फ्लाइट रिकॉर्डर के साथ इंस्ट्रूमेंटेड सर्वर के लिए पूरा कोड है: https://go.dev/play/p/3V33gfIpmjG

\ अब, हम सर्वर को फिर से चलाते हैं और अनुरोध भेजते हैं जब तक हमें एक धीमा अनुरोध नहीं मिलता जो स्नैपशॉट को ट्रिगर करता है।

\ एक बार जब हमें ट्रेस मिल जाता है, तो हमें एक ऐसे टूल की आवश्यकता होगी जो हमें इसकी जांच करने में मदद करेगा। Go टूलचेन go tool trace कमांड के माध्यम से एक अंतर्निहित निष्पादन ट्रेस विश्लेषण टूल प्रदान करता है। टूल को लॉन्च करने के लिए go tool trace snapshot.trace चलाएं, जो एक स्थानीय वेब सर्वर शुरू करता है, फिर अपने ब्राउज़र में प्रदर्शित URL खोलें (यदि टूल स्वचालित रूप से आपका ब्राउज़र नहीं खोलता है)।

\ यह टूल हमें ट्रेस को देखने के कुछ तरीके देता है, लेकिन आइए ट्रेस को विज़ुअलाइज़ करने पर ध्यान केंद्रित करें ताकि हम समझ सकें कि क्या हो रहा है। ऐसा करने के लिए "View trace by proc" पर क्लिक करें।

\ इस दृश्य में, ट्रेस को घटनाओं की एक टाइमलाइन के रूप में प्रस्तुत किया जाता है। पेज के शीर्ष पर, "STATS" अनुभाग में, हम एप्लिकेशन की स्थिति का एक सारांश देख सकते हैं, जिसमें थ्रेड्स की संख्या, हीप आकार, और गोरूटीन काउंट शामिल हैं।

\ उसके नीचे, "PROCS" अनुभाग में, हम देख सकते हैं कि गोरूटीन्स का निष्पादन GOMAXPROCS (Go एप्लिकेशन द्वारा बनाए गए ऑपरेटिंग सिस्टम थ्रेड्स की संख्या) पर कैसे मैप किया जाता है। हम देख सकते हैं कि प्रत्येक गोरूटीन कब और कैसे शुरू होता है, चलता है, और अंत में निष्पादन बंद करता है।

\ अभी के लिए, आइए व्यूअर के दाईं ओर निष्पादन में इस विशाल अंतराल पर अपना ध्यान केंद्रित करें। एक समय अवधि के लिए, लगभग 100ms, कुछ भी नहीं हो रहा है!

zoom टूल का चयन करके (या 3 दबाकर), हम अधिक विवरण के साथ अंतराल के ठीक बाद ट्रेस के अनुभाग का निरीक्षण कर सकते हैं।

\ प्रत्येक व्यक्तिगत गोरूटीन की गतिविधि के अलावा, हम देख सकते हैं कि गोरूटीन्स "फ्लो इवेंट्स" के माध्यम से कैसे इंटरैक्ट करते हैं। एक इनकमिंग फ्लो इवेंट इंगित करता है कि एक गोरूटीन को चलाने के लिए क्या हुआ। एक आउटगोइंग फ्लो एज इंगित करता है कि एक गोरूटीन का दूसरे पर क्या प्रभाव पड़ा। सभी फ्लो इवेंट्स के विज़ुअलाइज़ेशन को सक्षम करने से अक्सर ऐसे संकेत मिलते हैं जो समस्या के स्रोत का संकेत देते हैं।

इस मामले में, हम देख सकते हैं कि कई गोरूटीन्स का गतिविधि में रुकावट के ठीक बाद एक एकल गोरूटीन से सीधा संबंध है।

\ एकल गोरूटीन पर क्लिक करने से आउटगोइंग फ्लो इवेंट्स से भरी एक इवेंट टेबल दिखाई देती है, जो हमने देखा था जब फ्लो व्यू सक्षम था।

\ जब यह गोरूटीन चला तो क्या हुआ? ट्रेस में संग्रहीत जानकारी का एक हिस्सा विभिन्न समय बिंदुओं पर स्टैक ट्रेस का एक दृश्य है। जब हम गोरूटीन को देखते हैं तो हम देख सकते हैं कि स्टार्ट स्टैक ट्रेस दिखाता है कि जब गोरूटीन को शेड्यूल किया गया था तब यह HTTP अनुरोध के पूरा होने की प्रतीक्षा कर रहा था। और एंड स्टैक ट्रेस दिखाता है कि sendReport फंक्शन पहले ही लौट चुका था और यह रिपोर्ट भेजने के लिए अगले निर्धारित समय के लिए टिकर की प्रतीक्षा कर रहा था।

\ इस गोरूटीन के चलने के शुरू और अंत के बीच, हम "आउटगोइंग फ्लो" की एक बड़ी संख्या देखते हैं, जहां यह अन्य गोरूटीन्स के साथ इंटरैक्ट करता है। Outgoing flow प्रविष्टियों में से एक पर क्लिक करने से हमें इंटरैक्शन का एक दृश्य मिलता है।

\ यह फ्लो sendReport में Unlock को इंगित करता है:

for index := range buckets { b := &buckets[index] b.mu.Lock() defer b.mu.Unlock() counts[index] = b.guesses }

\ sendReport में, हमारा इरादा प्रत्येक बकेट पर एक लॉक प्राप्त करने और मूल्य की कॉपी करने के बाद लॉक को रिलीज़ करने का था।

\ लेकिन यहां समस्या है: हम वास्तव में bucket.guesses में निहित मूल्य की कॉपी करने के तुरंत बाद लॉक को रिलीज़ नहीं करते हैं। क्योंकि हमने लॉक को रिलीज़ करने के लिए एक defer स्टेटमेंट का उपयोग किया, वह रिलीज़ तब तक नहीं होती जब तक फंक्शन रिटर्न नहीं होता। हम लॉक को न केवल लूप के अंत तक, बल्कि HTTP अनुरोध पूरा होने के बाद तक रखते हैं। यह एक सूक्ष्म त्रुटि है जिसे एक बड़े प्रोडक्शन सिस्टम में ट्रैक करना मुश्किल हो सकता है।

\ सौभाग्य से, निष्पादन ट्रेसिंग ने हमें समस्या को सटीक रूप से पहचानने में मदद की। हालांकि, यदि हम नए फ्लाइट-रिकॉर्डिंग मोड के बिना एक लंबे समय तक चलने वाले सर्वर में निष्पादन ट्रेसर का उपयोग करने की कोशिश करते, तो यह संभवतः निष्पादन ट्रेस डेटा की एक विशाल मात्रा जमा कर लेता, जिसे एक ऑपरेटर को स्टोर, ट्रांसमिट और छानना पड़ता। फ्लाइट रिकॉर्डर हमें पीछे देखने की शक्ति देता है। यह हमें केवल वही कैप्चर करने देता है जो गलत हुआ, उसके पहले से ही होने के बाद, और जल्दी से कारण पर ध्यान केंद्रित करता है।

\ फ्लाइट रिकॉर्डर चल रहे एप्लिकेशन के आंतरिक कार्यों का निदान करने के लिए Go डेवलपर के टूलबॉक्स में नवीनतम जोड़ है। हम पिछले कुछ रिलीज़ में ट्रेसिंग में लगातार सुधार कर रहे हैं। Go 1.21 ने ट्रेसिंग के रन-टाइम ओवरहेड को बहुत कम कर दिया। ट्रेस फॉर्मेट Go 1.22 रिलीज़ में अधिक मजबूत और विभाजनीय हो गया, जिससे फ्लाइट रिकॉर्डर जैसी सुविधाएँ सामने आईं। gotraceui जैसे ओपन-सोर्स टूल्स, और निष्पादन ट्रेस को प्रोग्रामेटिक रूप से पार्स करने की आगामी क्षमता निष्पादन ट्रेस की शक्ति का लाभ उठाने के अधिक तरीके हैं। डायग्नोस्टिक्स पेज आपके निपटान के लिए कई अतिरिक्त उपकरण सूचीबद्ध करता है। हम आशा करते हैं कि आप अपने Go एप्लिकेशन लिखते और परिष्कृत करते समय उनका उपयोग करेंगे।

धन्यवाद

हम उन समुदाय के सदस्यों को धन्यवाद देने के लिए एक क्षण लेना चाहते हैं जो डायग्नोस्टिक्स मीटिंग्स में सक्रिय रहे हैं, डिज़ाइन में योगदान दिया है, और वर्षों से फीडबैक प्रदान किया है: Felix Geisendörfer (@felixge.de), Nick Ripley (@nsrip-dd), Rhys Hiltner (@rhysh), Dominik Honnef (@dominikh), Bryan Boreham (@bboreham), और PJ Malloy (@thepudds)।

\ आप सभी के द्वारा दिए गए चर्चाओं, फीडबैक और काम ने हमें बेहतर डायग्नोस्टिक्स भविष्य की ओर धकेलने में महत्वपूर्ण भूमिका निभाई है। धन्यवाद!


Carlos Amedee और Michael Knyszek

\ यह लेख The Go Blog पर CC BY 4.0 DEED लाइसेंस के तहत उपलब्ध है।

\ फोटो Lukas Souza द्वारा Unsplash पर

\

अस्वीकरण: इस साइट पर बाहर से पोस्ट किए गए लेख, सार्वजनिक प्लेटफार्म से लिए गए हैं और केवल सूचना देने के उद्देश्यों के लिए उपलब्ध कराए गए हैं. वे निश्चित तौर पर MEXC के विचारों को नहीं दिखाते. सभी संबंधित अधिकार मूल लेखकों के पास ही हैं. अगर आपको लगता है कि कोई कॉन्टेंट तीसरे पक्ष के अधिकारों का उल्लंघन करता है, तो कृपया उसे हटाने के लिए service@support.mexc.com से संपर्क करें. MEXC किसी कॉन्टेंट की सटीकता, पूर्णता या समयबद्धता के संबंध में कोई गारंटी नहीं देता है और प्रदान की गई जानकारी के आधार पर की गई किसी भी कार्रवाई के लिए जिम्मेदार नहीं है. यह कॉन्टेंट वित्तीय, कानूनी या अन्य प्रोफ़ेशनल सलाह नहीं है, न ही इसे MEXC द्वारा अनुशंसा या समर्थन माना जाना चाहिए.

आपको यह भी पसंद आ सकता है

विभिन्न मोमबत्ती आकारों के लिए सही मोमबत्ती डस्ट कवर का चयन

विभिन्न मोमबत्ती आकारों के लिए सही मोमबत्ती डस्ट कवर का चयन

परिचय मोमबत्ती डस्ट कवर एक छोटा पैकेजिंग विवरण लग सकता है, लेकिन वे मोमबत्ती की गुणवत्ता को संरक्षित करने, सुगंध की शक्ति बनाए रखने में महत्वपूर्ण भूमिका निभाते हैं
शेयर करें
Techbullion2026/01/21 14:37
कोका-कोला (KO) स्टॉक; टैरिफ की आशंकाओं के बीच निवेशकों के डिफेंसिव स्टेपल्स में जाने से तेजी

कोका-कोला (KO) स्टॉक; टैरिफ की आशंकाओं के बीच निवेशकों के डिफेंसिव स्टेपल्स में जाने से तेजी

टीएलडीआर; टैरिफ की चिंताओं ने जोखिम वाली संपत्तियों से रक्षात्मक उपभोक्ता स्टेपल्स में बदलाव को बढ़ावा दिया, जिससे कोका-कोला के शेयरों में लगभग 2% की वृद्धि हुई। गिरते बाजार में स्टॉक ने बेहतर प्रदर्शन किया
शेयर करें
Coincentral2026/01/21 14:44
ट्रंप द्वारा व्यापार युद्ध की आशंकाओं को फिर से भड़काने के साथ क्रिप्टो व्यापक बाजारों के साथ बिकवाली का सामना कर रहा है

ट्रंप द्वारा व्यापार युद्ध की आशंकाओं को फिर से भड़काने के साथ क्रिप्टो व्यापक बाजारों के साथ बिकवाली का सामना कर रहा है

बिटकॉइन $88K से नीचे गिरा क्योंकि टैरिफ खतरों ने सोने और चांदी में रिस्क-ऑफ रोटेशन को ट्रिगर किया
शेयर करें
Blockhead2026/01/21 14:30