`systemtap-toolkit` और `stapxx`: डेटा का उपयोग करके कठिन समस्याओं को कैसे हल करें?
API7.ai
December 22, 2022
पिछले लेख में परिचय के अनुसार, सर्वर-साइड डेवलपमेंट इंजीनियर के रूप में, हम डायनामिक डिबगिंग टूलसेट का गहराई से अध्ययन नहीं करते हैं, बल्कि ज्यादातर उपयोग के स्तर पर रहते हैं और अधिक से अधिक कुछ सरल स्टेपल स्क्रिप्ट लिखते हैं। निचले स्तर, जैसे CPU कैश, आर्किटेक्चर, कंपाइलर आदि, परफॉर्मेंस इंजीनियरों का क्षेत्र है।
OpenResty में दो ओपन-सोर्स प्रोजेक्ट हैं: openresty-systemtap-toolkit और stapxx। ये सिस्टमटैप-आधारित लपेटे गए टूलसेट हैं जो NGINX और OpenResty के लिए रियल-टाइम विश्लेषण और निदान के लिए हैं। ये on-CPU, off-CPU, शेयर्ड डिक्शनरी, गार्बेज कलेक्शन, रिक्वेस्ट लेटेंसी, मेमोरी पूलिंग, कनेक्शन पूलिंग, फाइल एक्सेस, और अन्य सामान्य फीचर्स और डिबगिंग स्थितियों को कवर कर सकते हैं।
इस लेख में, हम इन टूल्स और उनके संबंधित उपयोग को ब्राउज़ करेंगे, ताकि जब हम NGINX और OpenResty के साथ समस्या निवारण के मुद्दों का सामना करें, तो हम समस्याओं का पता लगाने के लिए टूल्स को जल्दी से ढूंढ सकें। OpenResty की दुनिया में, इन टूल्स का उपयोग करना सीखना आगे बढ़ने का एक निश्चित तरीका है, और अन्य डेवलपर्स के साथ संवाद करने का एक बहुत प्रभावी तरीका है - आखिरकार, टूल्स द्वारा उत्पन्न डेटा हमारे द्वारा शब्दों में वर्णित करने से अधिक सटीक और विस्तृत होगा।
हालांकि, यह ध्यान रखना आवश्यक है कि OpenResty संस्करण 1.15.8 में LuaJIT GC64 मोड डिफ़ॉल्ट रूप से सक्षम है, लेकिन openresty-systemtap-toolkit और stapxx संबंधित परिवर्तनों का पालन नहीं करते हैं, जिससे टूल्स के अंदर के टूल्स सही ढंग से काम नहीं करेंगे। इसलिए, हम इन टूल्स का उपयोग OpenResty के पुराने 1.13 संस्करण में करना बेहतर है।
अधिकांश ओपन-सोर्स प्रोजेक्ट योगदानकर्ता अंशकालिक हैं और टूल्स को काम करने के लिए बाध्य नहीं हैं, जिसे हमें ओपन-सोर्स प्रोजेक्ट का उपयोग करते समय ध्यान में रखना चाहिए।
उदाहरण: shared dict
आइए एक ऐसे टूल से शुरू करें जिससे हम शायद सबसे अधिक परिचित हैं और शुरुआत करने में सबसे आसान है, ngx-lua-shdict, आज के पोस्ट की शुरुआत के रूप में।
ngx-lua-shdict एक टूल है जो NGINX के shared dict का विश्लेषण करता है और उनके ऑपरेशन को ट्रैक करता है। आप -f विकल्प का उपयोग करके dict और key निर्दिष्ट कर सकते हैं ताकि shared dict में डेटा प्राप्त कर सकें। --raw विकल्प आपको निर्दिष्ट key का कच्चा मूल्य निर्यात करने की अनुमति देता है।
निम्नलिखित एक shared dict से डेटा प्राप्त करने का कमांड-लाइन उदाहरण है।
# मान लें कि NGINX Worker PID 5050 है $ ./ngx-lua-shdict -p 5050 -f --dict dogs --key Jim --luajit20 Tracing 5050 (/opt/nginx/sbin/nginx)... type: LUA_TBOOLEAN value: true expires: 1372719243270 flags: 0xa
इसी तरह, हम -w विकल्प का उपयोग करके दिए गए key के लिए dict लेखन को ट्रैक कर सकते हैं:
$./ngx-lua-shdict -p 5050 -w --key Jim --luajit20 Tracing 5050 (/opt/nginx/sbin/nginx)... Hit Ctrl-C to end set Jim exptime=4626322717216342016 replace Jim exptime=4626322717216342016 ^C
आइए देखें कि यह टूल कैसे लागू किया गया है। ngx-lua-shdict एक Perl स्क्रिप्ट है, लेकिन कार्यान्वयन का Perl से कोई लेना-देना नहीं है, जिसका उपयोग केवल stap स्क्रिप्ट उत्पन्न करने और इसे चलाने के लिए किया जाता है।
open my $in, "|stap $stap_args -x $pid -" or die "Cannot run stap: $!\n";
हम इसे Python, PHP, Go, या किसी भी भाषा में लिख सकते हैं जो हम पसंद करते हैं। stap स्क्रिप्ट में महत्वपूर्ण बिंदु निम्नलिखित कोड की पंक्ति है:
probe process("$nginx_path").function("ngx_http_lua_shdict_set_helper")
यह वह probe है जिसका हमने पहले उल्लेख किया था, जो ngx_http_lua_shdict_set_helper फ़ंक्शन की जांच करता है। इस फ़ंक्शन के कॉल सभी lua-nginx-module मॉड्यूल के lua-nginx-module/src/ngx_http_lua_shdict.c फ़ाइल में हैं।
static int ngx_http_lua_shdict_add(lua_State *L) { return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_ADD); } static int ngx_http_lua_shdict_safe_add(lua_State *L) { return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_ADD |NGX_HTTP_LUA_SHDICT_SAFE_STORE); } static int ngx_http_lua_shdict_replace(lua_State *L) { return ngx_http_lua_shdict_set_helper(L, NGX_HTTP_LUA_SHDICT_REPLACE); }
इस तरह, हम इस फ़ंक्शन की जांच करके शेयर्ड डिक्शनरी के सभी ऑपरेशन को ट्रैक कर सकते हैं।
on-CPU, off-CPU
OpenResty का उपयोग करते समय, हमारे सामने सबसे आम समस्या प्रदर्शन होनी चाहिए। प्रदर्शन की दो मुख्य प्रकार की समस्याएं हैं, यानी कम QPS: CPU उपयोग बहुत अधिक होना और CPU उपयोग बहुत कम होना। पहले की बाधा हमारे द्वारा पहले परिचय किए गए प्रदर्शन अनुकूलन विधियों का उपयोग न करने के कारण हो सकती है, जबकि बाद वाली ब्लॉकिंग फ़ंक्शन के कारण हो सकती है। संबंधित on-CPU और off-CPU फ्लेम ग्राफ हमें अंतिम मूल कारण की पहचान करने में मदद कर सकते हैं।
C-स्तरीय on-CPU फ्लेम ग्राफ उत्पन्न करने के लिए, आपको systemtap-toolkit में sample-bt का उपयोग करने की आवश्यकता है; जबकि Lua-स्तरीय on-CPU फ्लेम ग्राफ stapxx में lj-lua-stacks द्वारा उत्पन्न किए जाते हैं।
आइए sample-bt को उदाहरण के रूप में लेते हैं कि इसका उपयोग कैसे करें। sample-bt एक स्क्रिप्ट है जो हमारे द्वारा निर्दिष्ट किसी भी यूजर प्रोसेस (न केवल NGINX और OpenResty प्रोसेस) के कॉल स्टैक को सैंपल करती है।
उदाहरण के लिए, हम एक चल रहे NGINX Worker प्रोसेस (PID 8736) को 5 सेकंड के लिए निम्नलिखित कोड के साथ सैंपल कर सकते हैं:
$ ./sample-bt -p 8736 -t 5 -u > a.bt WARNING: Tracing 8736 (/opt/nginx/sbin/nginx) in user-space only... WARNING: Missing unwind data for module, rerun with 'stap -d stap_df60590ce8827444bfebaf5ea938b5a_11577' WARNING: Time's up. Quitting now...(it may take a while) WARNING: Number of errors: 0, skipped probes: 24
यह आउटपुट फ़ाइल a.bt उत्पन्न करता है, जिसका उपयोग FlameGraph टूलसेट का उपयोग करके फ्लेम ग्राफ उत्पन्न करने के लिए किया जा सकता है:
stackcollapse-stap.pl a.bt > a.cbt flamegraph.pl a.cbt > a.svg
a.svg उत्पन्न फ्लेम ग्राफ है, जिसे हम ब्राउज़र में खोलकर देख सकते हैं। हालांकि, सैंपलिंग अवधि के दौरान, हमें एक निश्चित संख्या में अनुरोध बनाए रखना चाहिए। अन्यथा, यदि सैंपल काउंट 0 है, तो फ्लेम ग्राफ उत्पन्न करने का कोई तरीका नहीं होगा।
अगला, हम देखेंगे कि off-CPU को कैसे सैंपल किया जाए, systemtap-toolkit में sample-bt-off-cpu स्क्रिप्ट का उपयोग करके, जो sample-bt के समान है, निम्नानुसार:
$ ./sample-bt-off-cpu -p 10901 -t 5 > a.bt WARNING: Tracing 10901 (/opt/nginx/sbin/nginx)... WARNING: _stp_read_address failed to access memory location WARNING: Time's up. Quitting now...(it may take a while) WARNING: Number of errors: 0, skipped probes: 23
stapxx में, लेटेंसी का विश्लेषण करने के लिए टूल epoll-loop-blocking-distr है, जो निर्दिष्ट यूजर प्रोसेस को सैंपल करता है और लगातार epoll_wait सिस्टम कॉल के बीच लेटेंसी वितरण को आउटपुट करता है।
$ ./samples/epoll-loop-blocking-distr.sxx -x 19647 --arg time=60 Start tracing 19647... Please wait for 60 seconds. Distribution of epoll loop blocking latencies (in milliseconds) max/avg/min: 1097/0/0 value |-------------------------------------------------- count 0 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 18471 1 |@@@@@@@@ 3273 2 |@ 473 4 | 119 8 | 67 16 | 51 32 | 35 64 | 20 128 | 23 256 | 9 512 | 2 1024 | 2 2048 | 0 4096 | 0
जैसा कि हम देख सकते हैं, यह आउटपुट दिखाता है कि अधिकांश देरी 1 ms से कम है, लेकिन कुछ 200 ms से ऊपर हैं, और ये वे हैं जिन पर ध्यान देना चाहिए।
अपस्ट्रीम और फेज ट्रैकिंग
OpenResty के कोड में होने वाली प्रदर्शन समस्याओं के अलावा, जब OpenResty cosocket या proxy_pass जैसे अपस्ट्रीम मॉड्यूल के माध्यम से अपस्ट्रीम सेवाओं के साथ संचार करता है, यदि अपस्ट्रीम सेवा में स्वयं बड़ी लेटेंसी है, तो यह समग्र प्रदर्शन पर महत्वपूर्ण प्रभाव डाल सकती है।
इस समय, हम ngx-lua-tcp-recv-time, ngx-lua-udp-recv-time और ngx-single-req-latency टूल्स का उपयोग कर सकते हैं। यहां ngx-single-req-latency का एक उदाहरण है।
यह टूल टूलसेट के अंदर के अधिकांश टूल्स से थोड़ा अलग है। अधिकांश अन्य टूल्स बड़ी संख्या में सैंपल और सांख्यिकीय विश्लेषण पर आधारित होते हैं ताकि वितरण के बारे में एक गणितीय निष्कर्ष निकाल सकें। इसके बजाय, ngx-single-req-latency व्यक्तिगत अनुरोधों का विश्लेषण करता है और OpenResty में विभिन्न फेज, जैसे rewrite, access, और content फेज, साथ ही अपस्ट्रीम पर व्यक्तिगत अनुरोधों पर खर्च किए गए समय को ट्रैक करता है।
हम एक विशिष्ट उदाहरण कोड देख सकते हैं:
# ./stap++ टूल को PATH में दिखाई देने योग्य बनाना: $ export PATH=$PWD:$PATH # मान लें कि एक nginx worker प्रोसेस का pid 27327 है $ ./samples/ngx-single-req-latency.sxx -x 27327 Start tracing process 27327 (/opt/nginx/sbin/nginx)... POST /api_json total: 143596us, accept() ~ header-read: 43048us, rewrite: 8us, pre-access: 7us, access: 6us, content: 100507us upstream: connect=29us, time-to-first-byte=99157us, read=103us $ ./samples/ngx-single-req-latency.sxx -x 27327 Start tracing process 27327 (/opt/nginx/sbin/nginx)... GET /robots.txt total: 61198us, accept() ~ header-read: 33410us, rewrite: 7us, pre-access: 7us, access: 5us, content: 27750us upstream: connect=30us, time-to-first-byte=18955us, read=96us
यह टूल शुरू होने के बाद पहले अनुरोध को ट्रैक करेगा। आउटपुट opentracing के समान है, और हम systemtap-toolkit और stapxx को OpenResty में APM (एप्लिकेशन परफॉर्मेंस मैनेजमेंट) के गैर-आक्रामक संस्करण के रूप में सोच सकते हैं।
सारांश
आज मैंने जिन सामान्य टूल्स के बारे में बात की है, उनके अलावा, OpenResty स्वाभाविक रूप से और भी कई टूल्स प्रदान करता है जिन्हें आप स्वयं खोज और सीख सकते हैं।
ट्रेसिंग तकनीक के संदर्भ में OpenResty और अन्य डेवलपमेंट भाषाओं और प्लेटफॉर्म के बीच एक और महत्वपूर्ण अंतर है, जिसे हम धीरे-धीरे महसूस कर सकते हैं।
कोड को साफ और स्थिर रखें, इसमें प्रोब्स न जोड़ें, बल्कि बाहरी डायनामिक ट्रेसिंग तकनीक के माध्यम से इसे सैंपल करें।
OpenResty का उपयोग करते समय आपने किन टूल्स का उपयोग करके समस्याओं को ट्रैक और विश्लेषण किया है? आप इस लेख को साझा करने के लिए स्वागत करते हैं, और हम साझा करेंगे और एक साथ प्रगति करेंगे।