भाग 3: OpenResty का उपयोग करके एक Microservices API Gateway कैसे बनाएं

API7.ai

February 3, 2023

OpenResty (NGINX + Lua)

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

NGINX कॉन्फ़िगरेशन और आरंभीकरण

हम जानते हैं कि API गेटवे का उपयोग ट्रैफ़िक प्रवेश को संभालने के लिए किया जाता है, इसलिए हमें पहले nginx.conf में एक सरल कॉन्फ़िगरेशन करने की आवश्यकता है ताकि सभी ट्रैफ़िक गेटवे के Lua कोड के माध्यम से संभाला जाए।

server { listen 9080; init_worker_by_lua_block { apisix.http_init_worker() } location / { access_by_lua_block { apisix.http_access_phase() } header_filter_by_lua_block { apisix.http_header_filter_phase() } body_filter_by_lua_block { apisix.http_body_filter_phase() } log_by_lua_block { apisix.http_log_phase() } } }

यहां हम ओपन-सोर्स API गेटवे Apache APISIX का उदाहरण के रूप में उपयोग करते हैं, इसलिए उपरोक्त कोड उदाहरण में apisix कीवर्ड है। इस उदाहरण में, हम पोर्ट 9080 पर सुनते हैं और location / द्वारा इस पोर्ट पर सभी अनुरोधों को इंटरसेप्ट करते हैं, और उन्हें access, rewrite, header filter, body filter और log चरणों के माध्यम से संसाधित करते हैं, प्रत्येक चरण में संबंधित प्लगइन फ़ंक्शन को कॉल करते हैं। rewrite चरण को apisix.http_access_phase फ़ंक्शन में संयोजित किया गया है।

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

function _M.http_init_worker() -- आरंभीकरण रूटिंग, सेवाएं, और प्लगइन्स - तीन सबसे महत्वपूर्ण भाग router.init_worker() require("apisix.http.service").init_worker() require("apisix.plugins.ext-plugin.init").init_worker() end

इस कोड से आप देख सकते हैं कि रूटर और प्लगइन भागों का आरंभीकरण थोड़ा अधिक जटिल है, मुख्य रूप से कॉन्फ़िगरेशन पैरामीटर्स को पढ़ने और उनके आधार पर कुछ विकल्प बनाने से संबंधित है। चूंकि इसमें etcd से डेटा पढ़ना शामिल है, हम ngx.timer का उपयोग करते हैं ताकि "init_worker चरण में cosocket का उपयोग नहीं कर सकते" प्रतिबंध को दरकिनार किया जा सके। यदि आप इस भाग में रुचि रखते हैं, तो हम स्रोत कोड को पढ़ने की सलाह देते हैं ताकि इसे बेहतर ढंग से समझ सकें।

रूट मिलान

access चरण की शुरुआत में, हमें पहले अनुरोध के साथ लाए गए uri, host, args, cookies आदि के आधार पर रूट को मिलान करने की आवश्यकता है, जो पहले से सेट किए गए रूटिंग नियमों से मेल खाता है।

router.router_http.match(api_ctx)

सार्वजनिक रूप से उजागर किया गया एकमात्र कोड उपरोक्त पंक्ति है, जहां api_ctx अनुरोध के uri, host, args, और cookie जानकारी को संग्रहीत करता है। मिलान फ़ंक्शन का विशिष्ट कार्यान्वयन हमारे द्वारा पहले उल्लेखित lua-resty-radixtree का उपयोग करता है। यदि कोई रूट मिलान नहीं होता है, तो अनुरोध का कोई अपस्ट्रीम नहीं होता है, और यह 404 वापस करेगा।

local router = require("resty.radixtree") local match_opts = {} function _M.match(api_ctx) -- अनुरोध के पैरामीटर्स को ctx से प्राप्त करें और इसे रूट के लिए निर्णय स्थिति के रूप में उपयोग करें match_opts.method = api_ctx.var.method match_opts.host = api_ctx.var.host match_opts.remote_addr = api_ctx.var.remote_addr match_opts.vars = api_ctx.var -- रूट के निर्णय फ़ंक्शन को कॉल करें local ok = uri_router:dispatch(api_ctx.var.uri, match_opts, api_ctx) -- यदि कोई रूट मिलान नहीं होता है, तो यह 404 वापस करता है if not ok then core.log.info("not find any matched route") return core.response.exit(404) end return true end

प्लगइन्स लोड करना

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

local plugins = core.tablepool.fetch("plugins", 32, 0) -- etcd में प्लगइन्स की सूची और स्थानीय कॉन्फ़िगरेशन फ़ाइल में प्लगइन्स की सूची का प्रतिच्छेदन api_ctx.plugins = plugin.filter(route, plugins) -- rewrite और access चरणों में माउंट किए गए प्लगइन के फ़ंक्शन को क्रम में चलाएं run_plugin("rewrite", plugins, api_ctx) run_plugin("access", plugins, api_ctx)

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

यहां फ़िल्टरिंग स्थानीय कॉन्फ़िगरेशन के साथ तुलना में की जाती है, निम्नलिखित दो कारणों से:

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

इसलिए access चरण के अंत में, हम इन फ़िल्टर किए गए प्लगइन्स को लेते हैं और उन्हें प्राथमिकता के क्रम में एक-एक करके चलाते हैं, जैसा कि निम्नलिखित कोड में दिखाया गया है।

local function run_plugin(phase, plugins, api_ctx) for i = 1, #plugins, 2 do local phase_fun = plugins[i][phase] if phase_fun then -- मुख्य कॉलिंग कोड phase_fun(plugins[i + 1], api_ctx) end end return api_ctx end

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

phase_fun(plugins[i + 1], api_ctx)

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

limit_count_plugin_rewrite_function(conf_of_plugin, api_ctx)

इस बिंदु पर, हम API गेटवे के समग्र प्रवाह के साथ लगभग समाप्त हो चुके हैं। यह सभी कोड एक ही फ़ाइल में है, जिसमें 400 से अधिक पंक्तियाँ हैं, लेकिन कोड का मूल वह कुछ दर्जन पंक्तियाँ हैं जिनका हमने ऊपर वर्णन किया है।

प्लगइन्स लिखना

अब, एक पूर्ण डेमो चलाने से पहले एक चीज बाकी है, और वह है एक प्लगइन लिखना। आइए limit-count प्लगइन को उदाहरण के रूप में लें। पूर्ण कार्यान्वयन सिर्फ 60 से अधिक पंक्तियों का कोड है, जिसे आप लिंक पर क्लिक करके देख सकते हैं। यहां, मैं मुख्य कोड पंक्तियों को विस्तार से समझाऊंगा:

पहले, हम lua-resty-limit-traffic को अनुरोधों की संख्या को सीमित करने के लिए आधार लाइब्रेरी के रूप में पेश करेंगे।

local limit_count_new = require("resty.limit.count").new

फिर, rapidjson में json schema का उपयोग करके यह परिभाषित करें कि इस प्लगइन के पैरामीटर्स क्या हैं:

local schema = { type = "object", properties = { count = {type = "integer", minimum = 0}, time_window = {type = "integer", minimum = 0}, key = {type = "string", enum = {"remote_addr", "server_addr"}, }, rejected_code = {type = "integer", minimum = 200, maximum = 600}, }, additionalProperties = false, required = {"count", "time_window", "key", "rejected_code"}, }

इस प्लगइन के ये पैरामीटर्स resty.limit.count के अधिकांश पैरामीटर्स से मेल खाते हैं, जिसमें सीमा की कुंजी, समय विंडो का आकार, और सीमित किए जाने वाले अनुरोधों की संख्या शामिल है। इसके अलावा, प्लगइन ने एक पैरामीटर जोड़ा है: rejected_code, जो अनुरोध सीमित होने पर निर्दिष्ट स्थिति कोड वापस करता है।

अंतिम चरण में, हम प्लगइन के हैंडलर फ़ंक्शन को rewrite चरण में माउंट करते हैं:

function _M.rewrite(conf, ctx) -- कैश से सीमा संख्या ऑब्जेक्ट प्राप्त करें, यदि नहीं है, तो `create_limit_obj` फ़ंक्शन का उपयोग करके एक नया ऑब्जेक्ट बनाएं और इसे कैश करें local lim, err = core.lrucache.plugin_ctx(plugin_name, ctx, create_limit_obj, conf) -- `ctx.var` से कुंजी का मान प्राप्त करें और कॉन्फ़िगरेशन प्रकार और कॉन्फ़िगरेशन संस्करण संख्या के साथ एक नई कुंजी बनाएं local key = (ctx.var[conf.key] or "") .. ctx.conf_type .. ctx.conf_version -- सीमा में प्रवेश करने का निर्णय करने वाला फ़ंक्शन local delay, remaining = lim:incoming(key, true) if not delay then local err = remaining -- यदि सीमा मान से अधिक है, तो निर्दिष्ट स्थिति कोड वापस करें if err == "rejected" then return conf.rejected_code end core.log.error("failed to limit req: ", err) return 500 end -- यदि सीमा मान से अधिक नहीं है, तो इसे जारी करें और संबंधित प्रतिक्रिया हेडर सेट करें core.response.set_header("X-RateLimit-Limit", conf.count, "X-RateLimit-Remaining", remaining) end

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

सारांश

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

पिछला: भाग 1: OpenResty का उपयोग करके माइक्रोसर्विसेज API गेटवे कैसे बनाएं भाग 2: OpenResty का उपयोग करके माइक्रोसर्विसेज API गेटवे कैसे बनाएं