Apache APISIX कैसे तेज़ है?
June 12, 2023
"हाई स्पीड," "न्यूनतम विलंबता," और "अंतिम प्रदर्शन" अक्सर Apache APISIX को चरित्रित करने के लिए उपयोग किए जाते हैं। जब कोई मुझसे APISIX के बारे में पूछता है, तो मेरा जवाब हमेशा "हाई-परफॉर्मेंस क्लाउड नेटिव API गेटवे" शामिल होता है।
प्रदर्शन बेंचमार्क (vs. Kong, Envoy) इन विशेषताओं की पुष्टि करते हैं (खुद टेस्ट करें)।

टेस्ट चलाए गए 10 राउंड के लिए 5000 यूनिक रूट्स पर Standard D8s v3 (8 vCPUs, 32 GiB मेमोरी) पर।
लेकिन APISIX यह कैसे हासिल करता है?
इस सवाल का जवाब देने के लिए, हमें तीन चीजों को देखना होगा: etcd, हैश टेबल्स, और रेडिक्स ट्रीज़।
इस लेख में, हम APISIX के अंदरूनी हिस्से को देखेंगे और यह समझेंगे कि ये क्या हैं और ये सभी कैसे मिलकर APISIX को भारी ट्रैफ़िक को संभालते हुए शीर्ष प्रदर्शन बनाए रखने में मदद करते हैं।
etcd कॉन्फ़िगरेशन सेंटर के रूप में
APISIX कॉन्फ़िगरेशन को स्टोर और सिंक्रनाइज़ करने के लिए etcd का उपयोग करता है।
etcd को बड़े पैमाने पर वितरित सिस्टम्स के कॉन्फ़िगरेशन के लिए एक की-वैल्यू स्टोर के रूप में काम करने के लिए डिज़ाइन किया गया है। APISIX को शुरू से ही वितरित और अत्यधिक स्केलेबल बनाने का इरादा है, और पारंपरिक डेटाबेस की बजाय etcd का उपयोग करना इसे सुविधाजनक बनाता है।

API गेटवे के लिए एक और महत्वपूर्ण अनिवार्य विशेषता है उच्च उपलब्धता, डाउनटाइम और डेटा हानि से बचना। आप इसे etcd के कई इंस्टेंस को डिप्लॉय करके प्रभावी ढंग से हासिल कर सकते हैं ताकि एक फॉल्ट-टॉलरेंट, क्लाउड नेटिव आर्किटेक्चर सुनिश्चित हो सके।
APISIX etcd से कॉन्फ़िगरेशन को न्यूनतम विलंबता के साथ पढ़/लिख सकता है। कॉन्फ़िगरेशन फाइल्स में परिवर्तन तुरंत सूचित किए जाते हैं, जिससे APISIX को केवल etcd अपडेट्स की निगरानी करने की आवश्यकता होती है, बजाय डेटाबेस को बार-बार पोल करने के, जो प्रदर्शन ओवरहेड जोड़ सकता है।
यह चार्ट etcd की अन्य डेटाबेस के साथ तुलना को सारांशित करता है।
IP पतों के लिए हैश टेबल्स
IP पते-आधारित अलाउलिस्ट/डिनाइलिस्ट API गेटवे के लिए एक सामान्य उपयोग मामला है।
उच्च प्रदर्शन हासिल करने के लिए, APISIX IP पतों की सूची को एक हैश टेबल में स्टोर करता है और मिलान के लिए इसका उपयोग करता है (O(1)) बजाय सूची के माध्यम से इटरेट करने के (O(N))।
जैसे-जैसे सूची में IP पतों की संख्या बढ़ती है, हैश टेबल्स का उपयोग करने का प्रदर्शन प्रभाव स्पष्ट हो जाता है।
अंदरूनी हिस्से में, APISIX इस कार्यक्षमता को लागू करने के लिए lua-resty-ipmatcher लाइब्रेरी का उपयोग करता है। नीचे दिया गया उदाहरण दिखाता है कि लाइब्रेरी का उपयोग कैसे किया जाता है:
local ipmatcher = require("resty.ipmatcher") local ip = ipmatcher.new({ "162.168.46.72", "17.172.224.47", "216.58.32.170", }) ngx.say(ip:match("17.172.224.47")) -- true ngx.say(ip:match("176.24.76.126")) -- false
लाइब्रेरी Lua टेबल्स का उपयोग करती है जो हैश टेबल्स हैं। IP पतों को हैश किया जाता है और एक टेबल में इंडेक्स के रूप में स्टोर किया जाता है, और किसी दिए गए IP पते को खोजने के लिए, आपको केवल टेबल को इंडेक्स करना होता है और यह जांचना होता है कि यह nil है या नहीं।

एक IP पते को खोजने के लिए, यह पहले हैश (इंडेक्स) की गणना करता है और उसके मान की जांच करता है। यदि यह गैर-खाली है, तो हमारे पास एक मिलान है। यह स्थिर समय O(1) में किया जाता है।
रूटिंग के लिए रेडिक्स ट्रीज़
कृपया मुझे माफ़ करें कि मैंने आपको डेटा स्ट्रक्चर्स के पाठ में धोखा दिया! लेकिन मेरी बात सुनें; यहीं पर यह दिलचस्प हो जाता है।
APISIX प्रदर्शन को अनुकूलित करने का एक प्रमुख क्षेत्र है रूट मिलान।
APISIX एक रूट को अनुरोध के URI, HTTP मेथड्स, होस्ट, और अन्य जानकारी से मिलाता है (राउटर)। और यह कुशल होना चाहिए।
यदि आपने पिछला सेक्शन पढ़ा है, तो एक स्पष्ट जवाब होगा कि हैश एल्गोरिदम का उपयोग करें। लेकिन रूट मिलान मुश्किल है क्योंकि कई अनुरोध एक ही रूट से मिल सकते हैं।
उदाहरण के लिए, यदि हमारे पास एक रूट /api/* है, तो /api/create और /api/destroy दोनों को रूट से मिलना चाहिए। लेकिन यह हैश एल्गोरिदम के साथ संभव नहीं है।
रेगुलर एक्सप्रेशन एक वैकल्पिक समाधान हो सकता है। रूट्स को एक रेगेक्स में कॉन्फ़िगर किया जा सकता है, और यह प्रत्येक अनुरोध को हार्डकोड किए बिना कई अनुरोधों से मिल सकता है।
यदि हम अपने पिछले उदाहरण को लें, तो हम रेगेक्स /api/[A-Za-z0-9]+ का उपयोग कर सकते हैं ताकि /api/create और /api/destroy दोनों से मिल सके। अधिक जटिल रेगेक्स अधिक जटिल रूट्स से मिल सकते हैं।
लेकिन रेगेक्स धीमा है! और हम जानते हैं कि APISIX तेज़ है। इसलिए, APISIX रेडिक्स ट्रीज़ का उपयोग करता है जो कंप्रेस्ड प्रीफ़िक्स ट्रीज़ (ट्राई) हैं जो तेज़ लुकअप के लिए बहुत अच्छे हैं।
आइए एक सरल उदाहरण देखें। मान लें कि हमारे पास निम्नलिखित शब्द हैं:
- romane
- romanus
- romulus
- rubens
- ruber
- rubicon
- rubicundus
एक प्रीफ़िक्स ट्री इसे इस तरह स्टोर करेगा:

हाइलाइटेड ट्रैवर्सल शब्द "rubens" दिखाता है।
एक रेडिक्स ट्री एक प्रीफ़िक्स ट्री को ऑप्टिमाइज़ करता है यदि एक नोड के पास केवल एक चाइल्ड नोड है। हमारा उदाहरण ट्राई एक रेडिक्स ट्री के रूप में इस तरह दिखेगा:

हाइलाइटेड ट्रैवर्सल अभी भी शब्द "rubens" दिखाता है। लेकिन ट्री बहुत छोटी दिखती है!
जब आप APISIX में रूट्स बनाते हैं, APISIX उन्हें इन ट्रीज़ में स्टोर करता है।
APISIX तब निर्दोष रूप से काम कर सकता है क्योंकि रूट मिलान में लगने वाला समय केवल अनुरोध के URI की लंबाई पर निर्भर करता है और रूट्स की संख्या से स्वतंत्र होता है (O(K), K कुंजी/URI की लंबाई है)।
इसलिए APISIX उतना ही तेज़ होगा जब आप शुरू में 10 रूट्स से मिलान करते हैं और जब आप स्केल करते हैं तो 5000 रूट्स से मिलान करते हैं।
यह क्रूड उदाहरण दिखाता है कि APISIX रेडिक्स ट्रीज़ का उपयोग करके रूट्स को कैसे स्टोर और मिलान कर सकता है:

हाइलाइटेड ट्रैवर्सल रूट /user/* दिखाता है जहां * एक प्रीफ़िक्स का प्रतिनिधित्व करता है। इसलिए /user/navendu जैसा URI इस रूट से मिलेगा। नीचे दिया गया उदाहरण कोड इन विचारों को और स्पष्ट करेगा।
APISIX lua-resty-radixtree लाइब्रेरी का उपयोग करता है, जो rax के चारों ओर लपेटता है, जो C में एक रेडिक्स ट्री इम्प्लीमेंटेशन है। यह शुद्ध Lua में लाइब्रेरी को लागू करने की तुलना में प्रदर्शन को सुधारता है।
नीचे दिया गया उदाहरण दिखाता है कि लाइब्रेरी का उपयोग कैसे किया जाता है:
local radix = require("resty.radixtree") local rx = radix.new({ { paths = { "/api/*action" }, metadata = { "metadata /api/action" } }, { paths = { "/user/:name" }, metadata = { "metadata /user/name" }, methods = { "GET" }, }, { paths = { "/admin/:name" }, metadata = { "metadata /admin/name" }, methods = { "GET", "POST", "PUT" }, filter_fun = function(vars, opts) return vars["arg_access"] == "admin" end } }) local opts = { matched = {} } -- पहले रूट से मिलान करता है ngx.say(rx:match("/api/create", opts)) -- metadata /api/action ngx.say("action: ", opts.matched.action) -- action: create ngx.say(rx:match("/api/destroy", opts)) -- metadata /api/action ngx.say("action: ", opts.matched.action) -- action: destroy local opts = { method = "GET", matched = {} } -- दूसरे रूट से मिलान करता है ngx.say(rx:match("/user/bobur", opts)) -- metadata /user/name ngx.say("name: ", opts.matched.name) -- name: bobur local opts = { method = "POST", var = ngx.var, matched = {} } -- तीसरे रूट से मिलान करता है -- `arg_access` का मान `ngx.var` से प्राप्त किया जाता है ngx.say(rx:match("/admin/nicolas", opts)) -- metadata /admin/name ngx.say("admin name: ", opts.matched.name) -- admin name: nicolas
बड़ी संख्या में रूट्स को कुशलता से प्रबंधित करने की क्षमता ने APISIX को कई बड़े पैमाने के प्रोजेक्ट्स के लिए API गेटवे का पसंदीदा विकल्प बना दिया है।
अंदरूनी हिस्से को देखें
एक लेख में APISIX के अंदरूनी कामकाज के बारे में केवल इतना ही समझाया जा सकता है।
लेकिन सबसे अच्छी बात यह है कि यहां उल्लिखित लाइब्रेरीज़ और Apache APISIX पूरी तरह से ओपन सोर्स हैं, जिसका अर्थ है कि आप अंदरूनी हिस्से को देख सकते हैं और चीजों को खुद संशोधित कर सकते हैं।
और यदि आप APISIX को उस अंतिम प्रदर्शन बिट को प्राप्त करने के लिए सुधार सकते हैं, तो आप परिवर्तनों को योगदान कर सकते हैं और अपने काम से सभी को लाभान्वित कर सकते हैं।