बर्स्टी ट्रैफ़िक से कैसे निपटें: लीकी बकेट और टोकन बकेट एल्गोरिदम

API7.ai

January 5, 2023

OpenResty (NGINX + Lua)

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

OpenResty का उपयोग अब मुख्य रूप से WAF और API गेटवे जैसे वेब एप्लिकेशन के एक्सेस लेयर के रूप में किया जाता है, जिन्हें ऊपर बताए गए सामान्य और असामान्य बर्स्ट ट्रैफ़िक से निपटना पड़ता है। आखिरकार, यदि आप बर्स्ट ट्रैफ़िक को संभाल नहीं सकते हैं, तो बैकएंड सेवाएं आसानी से डाउन हो सकती हैं, और व्यवसाय उचित प्रतिक्रिया नहीं देगा। इसलिए, आज हम बर्स्ट ट्रैफ़िक से निपटने के तरीकों पर नज़र डालेंगे।

ट्रैफ़िक कंट्रोल

ट्रैफ़िक कंट्रोल WAF (वेब एप्लिकेशन फ़ायरवॉल) और API गेटवे के लिए एक आवश्यक सुविधा है। यह सुनिश्चित करता है कि कुछ एल्गोरिदम के माध्यम से इनग्रेस ट्रैफ़िक को चैनलाइज़ और नियंत्रित करके अपस्ट्रीम सेवाएं उचित रूप से कार्य कर सकें, जिससे सिस्टम स्वस्थ रहता है।

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

वास्तव में, वास्तविक जीवन में कई "ट्रैफ़िक कंट्रोल" के मामले हैं। उदाहरण के लिए, चीनी नव वर्ष यात्रा के दौरान, लोग सबवे स्टेशन, ट्रेन स्टेशन, हवाई अड्डे और अन्य परिवहन केंद्रों में भीड़ करते हैं क्योंकि इन वाहनों की हैंडलिंग क्षमता सीमित होती है। इसलिए, यात्रियों को लाइन में खड़े होकर और बैचों में स्टेशन में प्रवेश करना पड़ता है ताकि उनकी सुरक्षा और यातायात का नियमित संचालन सुनिश्चित हो सके।

यह स्वाभाविक रूप से यात्री अनुभव को प्रभावित करता है, लेकिन समग्र रूप से, यह सिस्टम के कुशल और सुरक्षित संचालन को सुनिश्चित करता है। उदाहरण के लिए, यदि कोई कतार और बैचिंग नहीं होती, बल्कि हर किसी को झुंड में स्टेशन में प्रवेश करने की अनुमति दी जाती, तो परिणाम यह होता कि पूरा सिस्टम डाउन हो जाता।

तकनीक पर वापस जाएं, उदाहरण के लिए, मान लें कि एक अपस्ट्रीम सेवा को प्रति मिनट 10,000 अनुरोधों को संभालने के लिए डिज़ाइन किया गया है। चरम समय में, यदि प्रवेश बिंदु पर कोई फ्लो कंट्रोल नहीं है और कार्यों का ढेर प्रति मिनट 20,000 तक पहुंच जाता है, तो इस अपस्ट्रीम सेवा की प्रोसेसिंग प्रदर्शन शायद केवल 5,000 अनुरोध प्रति मिनट तक गिर जाएगा और लगातार खराब होता रहेगा, शायद अंततः सेवा अनुपलब्धता की ओर ले जाएगा। यह वह परिणाम नहीं है जिसे हम देखना चाहते हैं।

ऐसे बर्स्ट ट्रैफ़िक से निपटने के लिए हम जिन सामान्य ट्रैफ़िक कंट्रोल एल्गोरिदम का उपयोग करते हैं, वे हैं लीकी बकेट एल्गोरिदम और टोकन बकेट एल्गोरिदम।

लीकी बकेट एल्गोरिदम

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

लीकी बकेट एल्गोरिदम

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

पहला, चाहे बकेट में प्रवाह धीमा हो या भारी बाढ़, यह गारंटी है कि बकेट से बाहर निकलने वाले पानी की दर स्थिर होगी। यह स्थिर ट्रैफ़िक अपस्ट्रीम सेवाओं के लिए अनुकूल है, जो ट्रैफ़िक शेपिंग का मुख्य उद्देश्य है।

दूसरा, बकेट का अपना एक निश्चित आयतन होता है और यह एक निश्चित मात्रा में पानी जमा कर सकता है। यह क्लाइंट अनुरोधों के समान है जिन्हें तुरंत प्रोसेस नहीं किया जा सकता है, तो वे कतार में लग सकते हैं।

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

तो इस एल्गोरिदम को कैसे लागू किया जाना चाहिए? आइए OpenResty के साथ आने वाले resty.limit.req लाइब्रेरी को उदाहरण के रूप में लें। यह लीकी बकेट एल्गोरिदम द्वारा लागू किया गया एक रेट लिमिट मॉड्यूल है। हम इसके बारे में अगले लेख में और अधिक जानकारी देंगे। आज हम निम्नलिखित कोड की कुछ पंक्तियों पर संक्षेप में नज़र डालेंगे, जो मुख्य हैं:

local elapsed = now - tonumber(rec.last) excess = max(tonumber(rec.excess) - rate * abs(elapsed) / 1000 + 1000,0) if excess > self.burst then return nil, "rejected" end -- return the delay in seconds, as well as excess return excess / rate, excess / 1000

आइए इन कोड की पंक्तियों को संक्षेप में पढ़ें। जहां elapsed वर्तमान और पिछले अनुरोध के बीच मिलीसेकंड की संख्या है, और rate हमारे द्वारा सेट की गई प्रति सेकंड दर है। चूंकि rate की सबसे छोटी इकाई 0.001 s/r है, इसलिए ऊपर लागू किए गए कोड को इसकी गणना करने के लिए 1000 से गुणा करने की आवश्यकता है।

excess कतार में अभी भी मौजूद अनुरोधों की संख्या को दर्शाता है, 0 का अर्थ है कि बकेट खाली है, कतार में कोई अनुरोध नहीं है, और burst पूरे बकेट के आयतन को संदर्भित करता है। यदि excess burst से अधिक है, तो इसका अर्थ है कि बकेट भर गया है, और आने वाला ट्रैफ़िक सीधे छोड़ दिया जाएगा; यदि excess 0 से अधिक और burst से कम है, तो यह प्रोसेसिंग के लिए कतार में प्रवेश करेगा, और यहां वापस किया गया excess/rate प्रतीक्षा का समय है।

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

टोकन बकेट एल्गोरिदम

टोकन बकेट एल्गोरिदम और लीकी बकेट एल्गोरिदम का उद्देश्य एक ही है, यह सुनिश्चित करना कि बैकएंड सेवाएं ट्रैफ़िक के बर्स्ट से प्रभावित न हों, हालांकि दोनों को लागू करने का तरीका अलग है।

लीकी बकेट एल्गोरिदम एंडपॉइंट IP का उपयोग करके ट्रैफ़िक और रेट लिमिट के मूल सिद्धांतों को लागू करता है। इस तरह, प्रत्येक क्लाइंट के लिए लीकी बकेट एल्गोरिदम का निकास दर स्थिर होता है। हालांकि, यह एक समस्या पैदा करता है:

मान लें कि उपयोगकर्ता A के अनुरोध बार-बार आते हैं और अन्य उपयोगकर्ताओं के अनुरोध कम आते हैं। ऐसे में, लीकी बकेट एल्गोरिदम A के कुछ अनुरोधों को धीमा कर देगा या अस्वीकार कर देगा, भले ही सेवा उस समय उन्हें संभाल सकती हो, भले ही समग्र सेवा दबाव बहुत अधिक न हो।

यहीं पर टोकन बकेट काम आता है।

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

हालांकि, OpenResty ने अपनी लाइब्रेरी में टोकन बकेट को ट्रैफ़िक और रेट को सीमित करने के लिए लागू नहीं किया है। इसलिए यहां UPYUN द्वारा ओपन-सोर्स किए गए टोकन बकेट-आधारित रेट लिमिटिंग मॉड्यूल lua-resty-limit-rate का एक संक्षिप्त परिचय दिया गया है:

local limit_rate = require "resty.limit.rate" -- global 20r/s 6000r/5m local lim_global = limit_rate.new("my_limit_rate_store", 100, 6000, 2) -- single 2r/s 600r/5m local lim_single = limit_rate.new("my_limit_rate_store", 500, 600, 1) local t0, err = lim_global:take_available("__global__", 1) local t1, err = lim_single:take_available(ngx.var.arg_userid, 1) if t0 == 1 then return -- global bucket is not hungry else if t1 == 1 then return -- single bucket is not hungry else return ngx.exit(503) end end

इस कोड में, हमने दो टोकन बकेट सेट किए हैं: एक ग्लोबल टोकन बकेट, और एक टोकन बकेट जिसमें ngx.var.arg_userid को key के रूप में उपयोग किया गया है, जो उपयोगकर्ता द्वारा विभाजित है। दोनों टोकन बकेट का संयोजन है, जिसका मुख्य लाभ है:

  • यदि ग्लोबल टोकन बकेट में अभी भी टोकन हैं, तो उपयोगकर्ता के टोकन बकेट को निर्धारित करने की आवश्यकता नहीं है, और यदि बैकएंड सेवा सही ढंग से चल सकती है, तो उपयोगकर्ताओं से आने वाले बर्स्ट अनुरोधों को जितना संभव हो उतना सेवा दी जा सकती है।
  • ग्लोबल टोकन बकेट की अनुपस्थिति में, अनुरोधों को बिना किसी भेदभाव के अस्वीकार नहीं किया जा सकता है, इसलिए व्यक्तिगत उपयोगकर्ताओं के टोकन बकेट को निर्धारित करना और अधिक बर्स्ट अनुरोधों वाले उपयोगकर्ताओं के अनुरोधों को अस्वीकार करना आवश्यक है। इस तरह, यह सुनिश्चित किया जाता है कि अन्य उपयोगकर्ताओं के अनुरोध प्रभावित न हों।

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

NGINX का रेट लिमिट मॉड्यूल

इन दो एल्गोरिदम के बारे में जानने के बाद, आइए अंत में देखें कि NGINX में रेट लिमिट को कैसे लागू किया जाए। NGINX में, limit_req मॉड्यूल सबसे अधिक उपयोग किया जाने वाला रेट लिमिट मॉड्यूल है, और निम्नलिखित एक सरल कॉन्फ़िगरेशन है:

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { location /search/ { limit_req zone=one burst=5; } }

यह कोड क्लाइंट के IP पते को key के रूप में लेता है, one नामक 10M मेमोरी स्पेस का अनुरोध करता है, और दर को प्रति सेकंड 1 अनुरोध तक सीमित करता है।

सर्वर के लोकेशन में, one रेट-लिमिटिंग नियम को भी संदर्भित किया गया है, और brust को 5 पर सेट किया गया है। यदि दर 1r/s से अधिक हो जाती है, तो 5 अनुरोध एक साथ कतार में लग सकते हैं, जो एक निश्चित बफर क्षेत्र प्रदान करता है। यदि brust सेट नहीं किया गया है, तो दर से अधिक अनुरोधों को सीधे अस्वीकार कर दिया जाएगा।

यह NGINX मॉड्यूल लीकी बकेट पर आधारित है, इसलिए यह OpenResty में resty.limit.req के समान है, जिसका हमने ऊपर वर्णन किया है।

सारांश

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

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

आप इस लेख को अपने सहयोगियों और दोस्तों के साथ साझा करने के लिए स्वागत है ताकि सीखने और प्रगति करने में मदद मिल सके।