Go में रेट लिमिटिंग और थ्रॉटलिंग: API को कैसे सुरक्षित करें और परेशान न हों
\n\nकल्पना करें: आपने Go में एक परफेक्ट API लिखा है। यह तेज़ है, मिलीसेकंड में अनुरोधों को प्रोसेस करता है, और अचानक — बैम! — सर्वर पर अनुरोधों का तूफ़ान आ जाता है। हो सकता है यह कोई बॉट हो, क्लाइंट में कोई बग हो, या आपके प्रोडक्ट के आसपास सिर्फ हाइप हो। सुरक्षा के बिना सर्वर डाउन हो जाएगा, उपयोगकर्ता चले जाएंगे, और आप सुबह तक लॉग्स में खोजबीन करते रहेंगे।
\n\nपरिचित लगता है? तो रेट लिमिटिंग (गति सीमा) और थ्रॉटलिंग (नियंत्रण) — आपके नए सबसे अच्छे दोस्त हैं। pythonlib.ru पर इस लेख में, हम जानेंगे कि उन्हें Go में कैसे लागू किया जाए — एक ऐसी भाषा जो उच्च-लोड सिस्टम के लिए बनाई गई है। कोई बकवास नहीं, केवल वास्तविक कोड और वास्तविक मामले।
\n\nरेट लिमिटिंग बनाम थ्रॉटलिंग: क्या अंतर है?
\n\nशुरू करने के लिए, आइए शब्दों को स्पष्ट करें। बहुत से लोग इन अवधारणाओं को भ्रमित करते हैं, लेकिन अंतर है।
\n\n- \n
- रेट लिमिटिंग — यह एक सख्त सीमा है: "प्रति मिनट 100 से अधिक अनुरोध नहीं।" यदि सीमा पार हो जाती है — सर्वर 429 Too Many Requests के साथ जवाब देता है। सब कुछ ईमानदार और सख्त है। \n
- थ्रॉटलिंग — यह एक नरम नियमन है: "हम आपको धीमा कर देंगे, लेकिन ब्लॉक नहीं करेंगे।" उदाहरण के लिए, सभी अनुरोधों का तुरंत जवाब देने के बजाय, हम उन्हें एक कतार में रखते हैं और एक निश्चित गति से प्रोसेस करते हैं। \n
Go में, दोनों दृष्टिकोण गोरूटीन और चैनल के कारण सुंदर ढंग से लागू किए जाते हैं। आइए विशिष्ट उदाहरण देखें।
\n\nचैनल पर सबसे सरल रेट लिमिटर
\n\nगति को सीमित करने का सबसे बुनियादी तरीका time पैकेज से टिकर का उपयोग करना है। यह एक मेट्रोनोम की तरह है: प्रत्येक 'टिक' एक अनुरोध की अनुमति देता है।
package main\n\nimport (\n "fmt"\n "time"\n)\n\nfunc main() {\n // एक चैनल बनाएँ जो हर 200 मिलीसेकंड में टोकन भेजेगा\n limiter := time.Tick(200 * time.Millisecond)\n\n // 10 अनुरोधों का अनुकरण करें\n for i := 1; i <= 10; i++ {\n <-limiter // अनुमति की प्रतीक्षा करें\n fmt.Printf("अनुरोध %d %s पर संसाधित हुआ\\n", i, time.Now().Format("15:04:05.000"))\n }\n}\n\nयहाँ क्या हो रहा है? time.Tick एक चैनल बनाता है जो हर 200 मिलीसेकंड में एक मान भेजता है। हमारी गोरूटीन <-limiter पर तब तक ब्लॉक रहती है जब तक उसे टोकन नहीं मिल जाता। परिणामस्वरूप — प्रति सेकंड ठीक 5 अनुरोध, बिना किसी विकल्प के।
नुकसान: यदि टिक से कम अनुरोध हैं, तो हम बस प्रतीक्षा करते हैं। अकुशल? हाँ। लेकिन सरल परिदृश्यों के लिए — यह आदर्श है।
\n\nबफर के साथ बर्स्ट-लिमिटिंग
\n\nलेकिन क्या होगा अगर हमें ट्रैफिक के 'स्पाइक्स' को पास करने की आवश्यकता हो? उदाहरण के लिए, तुरंत 10 अनुरोधों की अनुमति दें, और फिर प्रति सेकंड 5 तक सीमित करें। इसके लिए हम बफ़र्ड चैनल का उपयोग करते हैं — क्लासिक टोकन बकेट।
\n\npackage main\n\nimport (\n "fmt"\n "time"\n)\n\nfunc main() {\n // 5 टोकन के लिए बफ़र्ड चैनल\n tokens := make(chan struct{}, 5)\n\n // भरने वाली गोरूटीन: हर 200 मिलीसेकंड में एक टोकन जोड़ता है\n go func() {\n ticker := time.NewTicker(200 * time.Millisecond)\n defer ticker.Stop()\n for range ticker.C {\n select {\n case tokens <- struct{}{}:\n default:\n // चैनल भरा हुआ है — कुछ न करें\n }\n }\n }()\n\n // 15 अनुरोधों का अनुकरण\n for i := 1; i <= 15; i++ {\n <-tokens // टोकन लें (खाली होने पर प्रतीक्षा करें)\n fmt.Printf("अनुरोध %d %s पर संसाधित हुआ\\n", i, time.Now().Format("15:04:05.000"))\n time.Sleep(50 * time.Millisecond) // अनुरोधों के बीच थोड़ी देरी\n }\n}\n\nपहले 5 अनुरोध तुरंत पास हो जाएंगे (बफर भरा हुआ है), और फिर — हर 200 मिलीसेकंड में एक। यह पैटर्न उन API के लिए आदर्श है जहां क्लाइंट अचानक बहुत सारे अनुरोध भेजना शुरू कर सकता है (उदाहरण के लिए, रीस्टार्ट के बाद)।
\n\nWaitGroup और वर्कर पूल के माध्यम से थ्रॉटलिंग
\n\nअब थ्रॉटलिंग पर आते हैं। मान लीजिए, हमारे पास कार्यों की एक कतार है (उदाहरण के लिए, ईमेल न्यूज़लेटर भेजना)। हम क्लाइंट को ब्लॉक नहीं करना चाहते, लेकिन सर्वर को ओवरलोड भी नहीं करना चाहते।