`systemtap-toolkit` و `stapxx`: كيفية استخدام البيانات لحل المشكلات الصعبة؟
API7.ai
December 22, 2022
كما تم تقديمه في المقالة السابقة، كمهندسي تطوير على جانب الخادم، لا نقوم بالتعمق في تعلم أدوات التصحيح الديناميكي، ولكننا نبقى في الغالب على مستوى الاستخدام ونكتب بعض البرامج النصية البسيطة على الأكثر. المستوى الأدنى، مثل ذاكرة التخزين المؤقت للوحدة المركزية (CPU cache)، والهندسة المعمارية، والمترجم، وما إلى ذلك، هو مجال مهندسي الأداء.
هناك مشروعان مفتوحا المصدر في OpenResty: openresty-systemtap-toolkit
و stapxx
. هما مجموعتا أدوات مبنية على systemtap
لتحليل وتشخيص NGINX و OpenResty في الوقت الفعلي. يمكنهما تغطية الميزات وسيناريوهات التصحيح الشائعة مثل on-CPU، off-CPU، القاموس المشترك، جمع القمامة، تأخر الطلبات، تجميع الذاكرة، تجميع الاتصالات، الوصول إلى الملفات، وغيرها.
في هذه المقالة، سنستعرض هذه الأدوات واستخداماتها المقابلة، لمساعدتنا في العثور بسرعة على الأدوات لتحديد المشكلات عند مواجهة مشاكل استكشاف الأخطاء وإصلاحها مع NGINX و OpenResty. في عالم OpenResty، تعلم استخدام هذه الأدوات هو طريقة مؤكدة للتقدم، وطريقة فعالة جدًا للتواصل مع المطورين الآخرين - بعد كل شيء، البيانات التي تولدها الأدوات ستكون أكثر دقة وتفصيلاً مما يمكننا وصفه بالكلمات.
ومع ذلك، من الضروري ملاحظة أن وضع LuaJIT GC64 مفعل بشكل افتراضي في إصدار OpenResty 1.15.8
، ولكن openresty-systemtap-toolkit
و stapxx
لا يتبعان التغييرات المقابلة، مما سيؤدي إلى عدم عمل الأدوات داخلهما بشكل صحيح. لذا، من الأفضل استخدام هذه الأدوات في الإصدار القديم 1.13
من OpenResty.
معظم مساهمي المشاريع مفتوحة المصدر يعملون بدوام جزئي وليسوا ملزمين بإبقاء الأدوات تعمل، وهو ما نحتاج إلى أن نكون على دراية به عند استخدام المشاريع مفتوحة المصدر.
مثال: shared dict
لنبدأ بأداة ربما نكون أكثر دراية بها وأسهل في البدء بها، ngx-lua-shdict
، كمثال لبدء منشور اليوم.
ngx-lua-shdict
هي أداة تحلل shared dict
s في NGINX وتتبع عملياتها. يمكنك استخدام الخيار -f
لتحديد dict
و key
للحصول على البيانات في shared dict
. الخيار --raw
يسمح لك بتصدير القيمة الخام للمفتاح المحدد.
التالي هو مثال على سطر الأوامر للحصول على البيانات من shared dict
.
# بافتراض أن معرف عملية NGINX Worker هو 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
لتتبع كتابات القاموس لمفتاح معين:
$./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/src/ngx_http_lua_shdict.c
في وحدة lua-nginx-module
.
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) مرتفع جدًا واستخدام وحدة المعالجة المركزية منخفض جدًا. قد يكون سبب الاختناق الأول هو عدم استخدام طرق تحسين الأداء التي قدمناها سابقًا، بينما قد يكون السبب الثاني هو وظائف الحظر. يمكن أن تساعدنا الرسوم البيانية للهب on-CPU و off-CPU في تحديد السبب الجذري النهائي.
لإنشاء رسوم بيانية للهب on-CPU على مستوى C، تحتاج إلى استخدام sample-bt
في systemtap-toolkit
؛ بينما يتم إنشاء رسوم بيانية للهب on-CPU على مستوى Lua بواسطة lj-lua-stacks
في stapxx
.
لنأخذ sample-bt
كمثال على كيفية استخدامه. sample-bt
هو نص برمجي يأخذ عينات من مكدس الاستدعاءات لأي عملية مستخدم نحددها (ليس فقط عمليات NGINX و OpenResty).
على سبيل المثال، يمكننا أخذ عينة من عملية NGINX Worker قيد التشغيل (مع معرف عملية 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، باستخدام النص البرمجي sample-bt-off-cpu
في systemtap-toolkit
، والذي يشبه 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 مللي ثانية، ولكن هناك عدد قليل فوق 200 مللي ثانية، وهذه هي التي يجب مراقبتها.
تتبع Upstream والمراحل
بالإضافة إلى مشاكل الأداء التي قد تحدث في كود OpenResty نفسه، عندما يتواصل OpenResty مع خدمات upstream من خلال وحدات مثل cosocket
أو proxy_pass
، إذا كانت الخدمة upstream نفسها لديها تأخير كبير، يمكن أن يكون لها تأثير كبير على الأداء العام.
في هذه الحالة، يمكننا استخدام الأدوات 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
، وكذلك upstream.
يمكننا النظر إلى مثال محدد للكود:
# جعل أداة ./stap++ مرئية في PATH:
$ export PATH=$PWD:$PATH
# بافتراض أن معرف عملية NGINX Worker هو 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
، كإصدارات غير تدخلية من APM (إدارة أداء التطبيقات) في OpenResty.
الخلاصة
بالإضافة إلى الأدوات الشائعة التي تحدثت عنها اليوم، يوفر OpenResty بشكل طبيعي العديد من الأدوات الأخرى التي يمكنك استكشافها وتعلمها بنفسك.
هناك فرق كبير آخر بين OpenResty ولغات ومنصات التطوير الأخرى في تقنية التتبع، والذي يمكننا تقديره ببطء.
حافظ على الكود نظيفًا ومستقرًا، لا تضيف إليه مجسات، ولكن خذ عينات منه من خلال تقنيات التتبع الديناميكي الخارجية.
ما هي الأدوات التي استخدمتها لتتبع وتحليل المشكلات أثناء استخدام OpenResty؟ أنت مدعو لمشاركة هذه المقالة، وسنتشارك ونتقدم معًا.