ओपनरेस्टी में सामान्य APIs का परिचय

API7.ai

November 4, 2022

OpenResty (NGINX + Lua)

पिछले लेखों में, आप OpenResty में कई महत्वपूर्ण Lua APIs से परिचित हो चुके हैं। आज, हम कुछ अन्य सामान्य APIs के बारे में सीखेंगे, जो मुख्य रूप से रेगुलर एक्सप्रेशन, समय, प्रक्रिया आदि से संबंधित हैं।

रेगुलर एक्सप्रेशन से संबंधित APIs

आइए सबसे पहले सबसे अधिक उपयोग किए जाने वाले और सबसे महत्वपूर्ण रेगुलर एक्सप्रेशन को देखें। OpenResty में, हमें रेगुलर एक्सप्रेशन से संबंधित लॉजिक को संभालने के लिए ngx.re.* द्वारा प्रदान किए गए APIs का उपयोग करना चाहिए, न कि Lua पैटर्न मिलान का। यह न केवल प्रदर्शन के कारणों से है, बल्कि इसलिए भी कि Lua की रेगुलर एक्सप्रेशन स्व-निहित है और PCRE स्पेसिफिकेशन नहीं है, जो अधिकांश डेवलपर्स के लिए परेशानी का कारण हो सकता है।

पिछले लेखों में, आप पहले ही कुछ ngx.re.* APIs से परिचित हो चुके हैं, जिनका दस्तावेज़ीकरण बहुत विस्तृत है। इसलिए मैं उन्हें यहां सूचीबद्ध नहीं करूंगा। यहां, मैं निम्नलिखित दो APIs को अलग से पेश करूंगा।

ngx.re.split

पहला है ngx.re.split। स्ट्रिंग कटिंग एक बहुत ही सामान्य फ़ंक्शन है, और OpenResty भी इसके लिए एक संबंधित API प्रदान करता है, लेकिन कई डेवलपर्स ऐसे फ़ंक्शन को नहीं ढूंढ पाते और इसे स्वयं लागू करने का विकल्प चुनते हैं।

क्यों? ngx.re.split API lua-nginx-module में नहीं है बल्कि lua-resty-core में है; यह lua-resty-core होमपेज के दस्तावेज़ीकरण में नहीं है बल्कि lua-resty-core/lib/ngx/re.md तृतीय-स्तरीय निर्देशिका के दस्तावेज़ीकरण में है। परिणामस्वरूप, कई डेवलपर्स इस API के अस्तित्व से पूरी तरह अनजान हैं।

इसी तरह, खोजने में मुश्किल APIs में ngx_resp.add_header, enable_privileged_agent आदि शामिल हैं, जिनका हमने पहले उल्लेख किया है। तो हम इस समस्या को जल्दी से कैसे हल कर सकते हैं? lua-resty-core होमपेज दस्तावेज़ीकरण को पढ़ने के अलावा, आपको lua-resty-core/lib/ngx/ निर्देशिका में *.md दस्तावेज़ीकरण को भी पढ़ना चाहिए।

lua_regex_match_limit

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

हम जानते हैं कि यदि हम बैकट्रैकिंग NFA पर आधारित रेगुलर इंजन का उपयोग करते हैं, तो Catastrophic Backtracking का जोखिम होता है, जहां रेगुलर मिलान करते समय बहुत अधिक बैकट्रैकिंग होती है, जिससे CPU 100% हो जाता है और सेवाएं ब्लॉक हो जाती हैं।

एक बार Catastrophic Backtracking होने पर, हमें इसे ढूंढने के लिए gdb का उपयोग करके डंप का विश्लेषण करना होगा या ऑनलाइन वातावरण का विश्लेषण करने के लिए systemtap का उपयोग करना होगा। दुर्भाग्य से, इसे पहले से पता लगाना आसान नहीं है क्योंकि केवल विशेष अनुरोध ही इसे ट्रिगर करते हैं। यह हमलावरों को इसका फायदा उठाने की अनुमति देता है, और ReDoS (RegEx Denial of Service) इस प्रकार के हमले को संदर्भित करता है।

यहां, मैं आपको यह बताना चाहता हूं कि OpenResty में निम्नलिखित कोड का उपयोग करके उपरोक्त समस्याओं से बचा जा सकता है:

lua_regex_match_limit का उपयोग PCRE रेगुलर इंजन द्वारा बैकट्रैकिंग की संख्या को सीमित करने के लिए किया जाता है। इस तरह, यदि Catastrophic Backtracking होता है, तो इसके परिणाम एक ऐसी सीमा तक सीमित होंगे जो आपके CPU को पूरी तरह से भरने का कारण नहीं बनेगी।

lua_regex_match_limit 100000;

समय से संबंधित APIs

सबसे अधिक उपयोग किया जाने वाला समय API ngx.now है, जो वर्तमान टाइमस्टैम्प को प्रिंट करता है, जैसे निम्नलिखित कोड:

resty -e 'ngx.say(ngx.now())'

प्रिंट किए गए परिणामों से आप देख सकते हैं कि ngx.now में फ्रैक्शनल भाग शामिल है, इसलिए यह अधिक सटीक है। संबंधित ngx.time API केवल मान का पूर्णांक भाग लौटाता है। अन्य, ngx.localtime, ngx.utctime, ngx.cookie_time और ngx.http_time मुख्य रूप से विभिन्न प्रारूपों में समय को लौटाने और प्रोसेस करने के लिए उपयोग किए जाते हैं। यदि आप उनका उपयोग करना चाहते हैं, तो आप दस्तावेज़ीकरण देख सकते हैं, वे समझने में मुश्किल नहीं हैं, इसलिए मुझे उनके बारे में बात करने की आवश्यकता नहीं है।

हालांकि, यह उल्लेख करने योग्य है कि ये APIs जो वर्तमान समय लौटाते हैं, यदि गैर-ब्लॉकिंग नेटवर्क IO ऑपरेशन द्वारा ट्रिगर नहीं किए जाते हैं, तो वे हमेशा कैश किए गए मान को लौटाएंगे न कि वर्तमान वास्तविक समय को जैसा कि हम चाहते हैं। निम्नलिखित नमूना कोड को देखें:

$ resty -e 'ngx.say(ngx.now()) os.execute("sleep 1") ngx.say(ngx.now())'

ngx.now के दो कॉल के बीच, हमने Lua के ब्लॉकिंग फ़ंक्शन का उपयोग करके 1 सेकंड के लिए स्लीप किया, लेकिन दोनों अवसरों पर लौटाया गया टाइमस्टैम्प समान है, जैसा कि प्रिंट किए गए परिणामों से दिखाई देता है।

तो, यदि हम इसे गैर-ब्लॉकिंग स्लीप फ़ंक्शन से बदल दें तो क्या होगा? उदाहरण के लिए, निम्नलिखित नया कोड:

$ resty -e 'ngx.say(ngx.now()) ngx.sleep(1) ngx.say(ngx.now())'

यह एक अलग टाइमस्टैम्प प्रिंट करेगा। यह हमें ngx.sleep की ओर ले जाता है, एक गैर-ब्लॉकिंग स्लीप फ़ंक्शन। निर्दिष्ट समय के लिए स्लीप करने के अलावा, इस फ़ंक्शन का एक और विशेष उद्देश्य है।

उदाहरण के लिए, यदि आपके पास एक कोड है जो गहन गणना कर रहा है, जिसमें बहुत समय लगता है, तो इस कोड के अनुरोध इस समय के दौरान वर्कर और CPU संसाधनों को लगातार लेते रहेंगे, जिससे अन्य अनुरोध कतार में लग जाएंगे और समय पर प्रतिक्रिया नहीं मिलेगी। इस समय, हम ngx.sleep(0) को शामिल कर सकते हैं ताकि यह कोड नियंत्रण छोड़ दे और अन्य अनुरोधों को भी प्रोसेस किया जा सके।

वर्कर और प्रक्रिया API

OpenResty वर्कर्स और प्रक्रियाओं के बारे में जानकारी प्राप्त करने के लिए ngx.worker.* और ngx.process.* APIs प्रदान करता है। पूर्व Nginx वर्कर प्रक्रियाओं से संबंधित है, जबकि बाद वाला सामान्य रूप से सभी Nginx प्रक्रियाओं को संदर्भित करता है, न केवल वर्कर प्रक्रियाएं, बल्कि मास्टर प्रक्रिया, विशेषाधिकार प्राप्त प्रक्रिया आदि भी।

true और null मानों की समस्या

अंत में, आइए true और null मानों की समस्या को देखें। OpenResty में, true मान और null मानों का निर्धारण एक बहुत ही परेशानी और भ्रमित करने वाला बिंदु रहा है।

आइए Lua में true मान की परिभाषा को देखें: nil और false को छोड़कर, वे सभी true मान हैं।

तो, true मानों में 0, खाली string, खाली table आदि भी शामिल होंगे।

आइए Lua में nil को देखें, जो undefined को दर्शाता है। उदाहरण के लिए, यदि आप एक वेरिएबल घोषित करते हैं लेकिन इसे इनिशियलाइज़ नहीं करते हैं, तो इसका मान nil होगा।

$ resty -e 'local a ngx.say(type(a))'

और nil Lua में एक डेटा प्रकार भी है। इन दो बिंदुओं को समझने के बाद, आइए अब इन दो परिभाषाओं से प्राप्त अन्य मुद्दों को देखें।

ngx.null

पहला मुद्दा ngx.null है। क्योंकि Lua का nil table के मान के रूप में उपयोग नहीं किया जा सकता, OpenResty ने ngx.null को table में null मान के रूप में पेश किया है।

$ resty -e 'print(ngx.null)' null
$ resty -e 'print(type(ngx.null))' userdata

जैसा कि आप उपरोक्त दो कोड से देख सकते हैं, ngx.null को null के रूप में प्रिंट किया जाता है, और इसका प्रकार userdata है, तो क्या इसे false मान के रूप में माना जा सकता है? बिल्कुल नहीं। ngx.null का बूलियन मान true है।

$ resty -e 'if ngx.null then ngx.say("true") end'

तो, याद रखें कि केवल nil और false ही false मान हैं। यदि आप इस बिंदु को याद करते हैं, तो गड्ढों में गिरना आसान है, उदाहरण के लिए, जब आप lua-resty-redis का उपयोग करते हैं और निम्नलिखित निर्णय लेते हैं:

local res, err = red:get("dog") if not res then res = res + "test" end

यदि रिटर्न मान res nil है, तो फ़ंक्शन कॉल विफल हो गया है; यदि res ngx.null है, तो redis में dog कुंजी मौजूद नहीं है, तो यदि dog कुंजी मौजूद नहीं है तो कोड क्रैश हो जाएगा।

cdata:NULL

दूसरा मुद्दा cdata:NULL है। जब आप LuaJIT FFI इंटरफ़ेस के माध्यम से एक C फ़ंक्शन को कॉल करते हैं, और फ़ंक्शन एक NULL पॉइंटर लौटाता है, तो आप एक अन्य प्रकार के null मान, cdata:NULL का सामना करेंगे।

$ resty -e 'local ffi = require "ffi" local cdata_null = ffi.new("void*", nil) if cdata_null then ngx.say("true") end'

ngx.null की तरह, cdata:NULL भी true है। लेकिन जो और भी भ्रमित करने वाला है वह यह है कि निम्नलिखित कोड, जो true प्रिंट करता है, का अर्थ है कि cdata:NULL nil के बराबर है।

$ resty -e 'local ffi = require "ffi" local cdata_null = ffi.new("void*", nil) ngx.say(cdata_null == nil)'

तो हमें ngx.null और cdata:NULL को कैसे संभालना चाहिए? एप्लिकेशन लेयर को इन परेशानियों की परवाह करना एक अच्छा समाधान नहीं है। बेहतर है कि एक सेकंड-लेवल रैपर करें और कॉलर को इन विवरणों को न जानने दें।

cjson.null

अंत में, आइए cjson में प्रकट होने वाले null मानों को देखें। cjson लाइब्रेरी json में NULL को लेती है, इसे Lua lightuserdata में डिकोड करती है, और cjson.null का उपयोग करती है।

$ resty -e 'local cjson = require "cjson" local data = cjson.encode(nil) local decode_null = cjson.decode(data) ngx.say(decode_null == cjson.null)'

Lua का nil JSON द्वारा एनकोड और डिकोड होने के बाद cjson.null बन जाता है। जैसा कि आप कल्पना कर सकते हैं, यह ngx.null के समान कारण से पेश किया गया है, क्योंकि nil को table में मान के रूप में उपयोग नहीं किया जा सकता।

अब तक, क्या आप OpenResty में इतने सारे प्रकार के null मानों से भ्रमित हो गए हैं? चिंता न करें। इस भाग को कुछ बार पढ़ें और इसे स्वयं सुलझाएं, तो आप भ्रमित नहीं होंगे। बेशक, भविष्य में हमें यह सोचने की आवश्यकता है कि जब हम if not foo then जैसा कुछ लिखते हैं तो यह काम करता है या नहीं।

सारांश

आज के लेख में, हमने OpenResty में आमतौर पर उपयोग किए जाने वाले Lua APIs का परिचय दिया है।

अंत में, मैं आपके लिए एक प्रश्न छोड़ता हूं: ngx.now उदाहरण में, जब कोई yield ऑपरेशन नहीं होता है, तो ngx.now का मान क्यों संशोधित नहीं होता है? कृपया अपनी राय टिप्पणियों में साझा करें, और इस लेख को साझा करने के लिए भी आपका स्वागत है ताकि हम एक साथ संवाद और सुधार कर सकें।