تحليل أداء الإنتاج باستخدام Stackdriver Profiler

في حين أنّ مطوّري تطبيقات العميل وواجهة الويب الأمامية يستخدمون عادةً أدوات مثل أداة تحليل استخدام وحدة المعالجة المركزية في Android Studio أو أدوات تحليل الأداء المضمّنة في Chrome لتحسين أداء الرموز البرمجية، لم تكن التقنيات المشابهة متاحة أو مستخدَمة على نطاق واسع من قِبل المطوّرين الذين يعملون على الخدمات الخلفية. توفّر Stackdriver Profiler هذه الإمكانات نفسها لمطوّري الخدمات، بغض النظر عمّا إذا كان الرمز البرمجي يعمل على Google Cloud Platform أو في مكان آخر.

تجمع الأداة معلومات عن استخدام وحدة المعالجة المركزية وتخصيص الذاكرة من تطبيقات الإنتاج. وتنسب هذه المعلومات إلى الرمز المصدر للتطبيق، ما يساعدك في تحديد أجزاء التطبيق التي تستهلك أكبر قدر من الموارد، كما توضّح خصائص أداء الرمز. إنّ التكاليف المنخفضة لأساليب الجمع التي تستخدمها الأداة تجعلها مناسبة للاستخدام المستمر في بيئات الإنتاج.

في هذا الدرس التطبيقي، ستتعرّف على كيفية إعداد Stackdriver Profiler لبرنامج Go، كما ستتعرّف على نوع الإحصاءات التي يمكن أن تقدّمها الأداة حول أداء التطبيق.

ما ستتعرّف عليه

  • كيفية ضبط برنامج Go لتسجيل البيانات باستخدام Stackdriver Profiler
  • كيفية جمع بيانات الأداء وعرضها وتحليلها باستخدام Stackdriver Profiler

المتطلبات

  • مشروع Google Cloud Platform
  • متصفّح، مثل Chrome أو Firefox
  • الإلمام بمحرّرات النصوص القياسية في Linux، مثل Vim أو EMACs أو Nano

كيف ستستخدم هذا البرنامج التعليمي؟

قراءة المحتوى فقط قراءة المحتوى وإكمال التمارين

ما هو تقييمك لتجربة استخدام Google Cloud Platform؟

مبتدئ متوسط متقدّم

إعداد البيئة بالسرعة التي تناسبك

إذا لم يكن لديك حساب على Google (Gmail أو Google Apps)، عليك إنشاء حساب. سجِّل الدخول إلى "وحدة تحكّم Google Cloud Platform" (console.cloud.google.com) وأنشِئ مشروعًا جديدًا:

لقطة شاشة من 2016-02-10 12:45:26.png

تذكَّر معرّف المشروع، وهو اسم فريد في جميع مشاريع Google Cloud (الاسم أعلاه مستخدَم حاليًا ولن يكون متاحًا لك، نأسف لذلك). سيتم الإشارة إليه لاحقًا في هذا الدرس العملي باسم PROJECT_ID.

بعد ذلك، عليك تفعيل الفوترة في Cloud Console من أجل استخدام موارد Google Cloud.

لن تكلفك تجربة هذا الدرس البرمجي أكثر من بضعة دولارات، ولكن قد تكون التكلفة أعلى إذا قررت استخدام المزيد من الموارد أو إذا تركتها قيد التشغيل (راجِع قسم "التنظيف" في نهاية هذا المستند).

يمكن للمستخدمين الجدد في Google Cloud Platform الاستفادة من فترة تجريبية مجانية بقيمة 300 دولار أمريكي.

Google Cloud Shell

على الرغم من إمكانية تشغيل Google Cloud عن بُعد من الكمبيوتر المحمول، سنستخدم Google Cloud Shell في هذا الدرس العملي، وهي بيئة سطر أوامر تعمل في السحابة الإلكترونية، وذلك لتسهيل عملية الإعداد.

تفعيل Google Cloud Shell

من وحدة تحكّم Google Cloud Platform، انقر على رمز Cloud Shell في شريط الأدوات العلوي الأيسر:

ثم انقر على "بدء Cloud Shell":

ينبغي ألا تستغرق إدارة الحسابات والاتصال بالبيئة أكثر من بضع لحظات.

يتم تحميل هذه الآلة الافتراضية مزوّدة بكل أدوات التطوير التي ستحتاج إليها. وتوفِّر هذه الآلة دليلاً رئيسيًا دائمًا بسعة 5 غيغابايت ويتمّ تشغيله على Google Cloud، ما يحسّن كثيرًا أداء الشبكة والمصادقة. ويمكن إتمام معظم عملك إن لم يكن كلّه في هذا الدرس ببساطة من خلال متصفّح أو جهاز Google Chromebook فقط.

بعد الاتصال بـ Cloud Shell، من المفترض أن ترى أنّه تم إثبات هويتك وأنّ المشروع تم ضبطه على PROJECT_ID.

نفِّذ الأمر التالي في Cloud Shell للتأكّد من إكمال عملية المصادقة:

gcloud auth list

ناتج الأمر

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

ناتج الأمر

[core]
project = <PROJECT_ID>

إذا لم يكن كذلك، يمكنك تعيينه من خلال هذا الأمر:

gcloud config set project <PROJECT_ID>

ناتج الأمر

Updated property [core/project].

في Cloud Console، انتقِل إلى واجهة مستخدم Profiler من خلال النقر على "Profiler" في شريط التنقّل الأيمن:

بدلاً من ذلك، يمكنك استخدام شريط البحث في Cloud Console للانتقال إلى واجهة مستخدم Profiler: ما عليك سوى كتابة "Stackdriver Profiler" واختيار العنصر الذي تم العثور عليه. في كلتا الحالتين، من المفترض أن تظهر لك واجهة مستخدم Profiler مع الرسالة "ما مِن بيانات متوفّرة لعرضها" كما هو موضّح أدناه. المشروع جديد، لذا لا يتضمّن أي بيانات تحديد مواصفات تم جمعها حتى الآن.

حان الوقت الآن للحصول على بعض البيانات الشخصية.

سنستخدم تطبيق Go اصطناعيًا بسيطًا متاحًا على Github. في نافذة Cloud Shell الطرفية التي لا تزال مفتوحة (وبينما لا تزال الرسالة "لا تتوفّر بيانات لعرضها" معروضة في واجهة مستخدم Profiler)، شغِّل الأمر التالي:

$ go get -u github.com/GoogleCloudPlatform/golang-samples/profiler/...

بعد ذلك، انتقِل إلى دليل التطبيق:

$ cd ~/gopath/src/github.com/GoogleCloudPlatform/golang-samples/profiler/hotapp

يحتوي الدليل على الملف "main.go"، وهو تطبيق اصطناعي تم تفعيل أداة جمع بيانات الأداء فيه:

main.go

...
import (
        ...
        "cloud.google.com/go/profiler"
)
...
func main() {
        err := profiler.Start(profiler.Config{
                Service:        "hotapp-service",
                DebugLogging:   true,
                MutexProfiling: true,
        })
        if err != nil {
                log.Fatalf("failed to start the profiler: %v", err)
        }
        ...
}

يجمع عامل إنشاء الملفات الشخصية ملفات وحدة المعالجة المركزية (CPU) والذاكرة المؤقتة والملفات المتعددة تلقائيًا. يتيح الرمز البرمجي هنا جمع ملفات تعريف mutex (المعروفة أيضًا باسم "التنازع").

الآن، شغِّل البرنامج:

$ go run main.go

أثناء تشغيل البرنامج، سيجمع عامل إنشاء الملفات الشخصية بشكل دوري ملفات شخصية من الأنواع الخمسة التي تم ضبطها. يتم اختيار البيانات بشكل عشوائي بمرور الوقت (بمعدل متوسط يبلغ ملف شخصي واحد في الدقيقة لكل نوع من الأنواع)، لذا قد يستغرق جمع كل نوع من الأنواع مدة تصل إلى ثلاث دقائق. يُعلمك البرنامج عند إنشاء ملف شخصي. يتم تفعيل الرسائل من خلال العلامة DebugLogging في الإعدادات أعلاه، وإلا سيعمل البرنامج بدون إظهار أي رسائل:

$ go run main.go
2018/03/28 15:10:24 profiler has started
2018/03/28 15:10:57 successfully created profile THREADS
2018/03/28 15:10:57 start uploading profile
2018/03/28 15:11:19 successfully created profile CONTENTION
2018/03/28 15:11:30 start uploading profile
2018/03/28 15:11:40 successfully created profile CPU
2018/03/28 15:11:51 start uploading profile
2018/03/28 15:11:53 successfully created profile CONTENTION
2018/03/28 15:12:03 start uploading profile
2018/03/28 15:12:04 successfully created profile HEAP
2018/03/28 15:12:04 start uploading profile
2018/03/28 15:12:04 successfully created profile THREADS
2018/03/28 15:12:04 start uploading profile
2018/03/28 15:12:25 successfully created profile HEAP
2018/03/28 15:12:25 start uploading profile
2018/03/28 15:12:37 successfully created profile CPU
...

سيتم تعديل واجهة المستخدم تلقائيًا بعد جمع أول الملفات الشخصية. ولن يتم تعديلها تلقائيًا بعد ذلك، لذا لعرض البيانات الجديدة، عليك إعادة تحميل واجهة مستخدم Profiler يدويًا. لإجراء ذلك، انقر على الزر "الآن" في أداة اختيار الفاصل الزمني مرّتين:

بعد إعادة تحميل واجهة المستخدم، سيظهر لك ما يلي:

تعرض أداة اختيار نوع الملف الشخصي أنواع الملفات الشخصية الخمسة المتاحة:

لنراجع الآن كل نوع من أنواع الملفات الشخصية وبعض إمكانات واجهة المستخدم المهمة، ثم نُجري بعض التجارب. في هذه المرحلة، لن تحتاج إلى وحدة Cloud Shell الطرفية، لذا يمكنك الخروج منها بالضغط على CTRL-C وكتابة "exit".

بعد أن جمعنا بعض البيانات، لنلقِ نظرة عليها عن كثب. نستخدم تطبيقًا اصطناعيًا (يتوفّر المصدر على Github) يحاكي السلوكيات النموذجية لأنواع مختلفة من مشاكل الأداء في مرحلة الإنتاج.

الرموز البرمجية التي تستهلك الكثير من وحدة المعالجة المركزية

اختَر نوع ملف وحدة المعالجة المركزية. بعد تحميل واجهة المستخدم، ستظهر لك في الرسم البياني الشعلة أربع كتل أوراق لوظيفة load، والتي تمثّل بشكل جماعي كل استهلاك وحدة المعالجة المركزية:

تمت كتابة هذه الدالة خصيصًا لاستهلاك الكثير من دورات وحدة المعالجة المركزية من خلال تشغيل حلقة ضيقة:

main.go

func load() {
        for i := 0; i < (1 << 20); i++ {
        }
}

يتم استدعاء الدالة بشكل غير مباشر من busyloop() عبر أربعة مسارات استدعاء: busyloop → {foo1, foo2} → {bar, baz} → load. يمثّل عرض مربّع الدالة التكلفة النسبية لمسار الاستدعاء المحدّد. في هذه الحالة، تتشابه تكلفة المسارات الأربعة. في برنامج حقيقي، عليك التركيز على تحسين مسارات الاتصال الأكثر أهمية من حيث الأداء. يسهّل الرسم البياني الشعلة، الذي يؤكّد بصريًا على المسارات الأكثر تكلفة باستخدام مربّعات أكبر، تحديد هذه المسارات.

يمكنك استخدام فلتر بيانات الملف الشخصي لتحسين العرض بشكل أكبر. على سبيل المثال، جرِّب إضافة فلتر "عرض حِزم" مع تحديد "baz" كسلسلة الفلتر. من المفترض أن يظهر لك شيء مشابه للقطة الشاشة أدناه، حيث يتم عرض مسارين فقط من مسارات الاتصال الأربعة إلى load(). هذان المساران هما المساران الوحيدان اللذان يمران بدالة تتضمّن السلسلة "baz" في اسمها. تكون عملية الفلترة هذه مفيدة عندما تريد التركيز على جزء فرعي من برنامج أكبر (على سبيل المثال، لأنّك تملك جزءًا منه فقط).

الرمز البرمجي الذي يستهلك الكثير من الذاكرة

الآن، انتقِل إلى نوع الملف الشخصي "Heap". احرص على إزالة أي فلاتر أنشأتها في التجارب السابقة. من المفترض أن يظهر لك الآن رسم بياني على شكل شعلة يظهر فيه allocImpl، الذي تم استدعاؤه من خلال alloc، باعتباره المستهلك الرئيسي للذاكرة في التطبيق:

يشير جدول الملخّص أعلى الرسم البياني الشعلة إلى أنّ إجمالي مقدار الذاكرة المستخدَمة في التطبيق يبلغ في المتوسط ‎57.4 ميغابايت تقريبًا، ويتم تخصيص معظمها من خلال الدالة allocImpl. وهذا ليس مفاجئًا، نظرًا إلى طريقة تنفيذ هذه الدالة:

main.go

func allocImpl() {
        // Allocate 64 MiB in 64 KiB chunks
        for i := 0; i < 64*16; i++ {
                mem = append(mem, make([]byte, 64*1024))
        }
}

يتم تنفيذ الدالة مرة واحدة، مع تخصيص 64 ميغابايت في أجزاء أصغر، ثم يتم تخزين مؤشرات لتلك الأجزاء في متغير عام لحمايتها من جمع البيانات غير الضرورية. يُرجى العِلم أنّ مقدار الذاكرة المعروضة على أنّه مستخدَم من خلال أداة تحليل الأداء يختلف قليلاً عن 64 ميغابايت: أداة تحليل الأداء في Go هي أداة إحصائية، لذا تكون القياسات منخفضة الحمل ولكنّها ليست دقيقة على مستوى البايت. لا تستغرب عند رؤية فرق بنسبة% 10 تقريبًا على هذا النحو.

الرمز البرمجي الذي يتطلّب عمليات إدخال وإخراج مكثّفة

إذا اخترت "سلاسل المحادثات" في أداة اختيار نوع الملف الشخصي، سيتم التبديل إلى رسم بياني على شكل لهب حيث يشغل معظم العرض الدالتان wait وwaitImpl:

في الملخّص أعلاه، يمكنك ملاحظة أنّ هناك 100 روتين فرعي يزيد من حجم حزمة استدعاءاته من الدالة wait. هذا صحيح تمامًا، علمًا بأنّ الرمز الذي يبدأ عمليات الانتظار هذه يبدو على النحو التالي:

main.go

func main() {
        ...
        // Simulate some waiting goroutines.
        for i := 0; i < 100; i++ {
                go wait()
        }

يكون نوع الملف هذا مفيدًا لمعرفة ما إذا كان البرنامج يستغرق وقتًا غير متوقّع في عمليات الانتظار (مثل عمليات الإدخال والإخراج). ولا يتم عادةً أخذ عيّنات من حِزم استدعاء الدوال البرمجية هذه بواسطة أداة تحليل استخدام وحدة المعالجة المركزية، لأنّها لا تستهلك أي جزء كبير من وقت وحدة المعالجة المركزية. في كثير من الأحيان، ستحتاج إلى استخدام فلاتر "إخفاء حِزم البيانات" مع ملفات شخصية في Threads، مثلاً لإخفاء جميع حِزم البيانات التي تنتهي بطلب إلى gopark, لأنّها غالبًا ما تكون إجراءات goroutine غير نشطة وأقل أهمية من تلك التي تنتظر عمليات الإدخال والإخراج.

يمكن أن يساعد نوع ملف تعريف سلاسل التنفيذ أيضًا في تحديد النقاط في البرنامج التي تنتظر فيها سلاسل التنفيذ mutex مملوكًا لجزء آخر من البرنامج لفترة طويلة، ولكن نوع ملف التعريف التالي أكثر فائدة لذلك.

الرمز البرمجي الذي يتطلب الكثير من التنافس

يحدّد نوع ملف تعريف التنازع الأقفال "الأكثر طلبًا" في البرنامج. يتوفّر نوع الملف الشخصي هذا لبرامج Go، ولكن يجب تفعيله بشكل صريح من خلال تحديد "MutexProfiling: true" في رمز إعداد الوكيل. تعمل عملية الجمع من خلال تسجيل (ضمن مقياس "المنازعات") عدد المرات التي كان فيها قفل معيّن، عند إلغاء قفله بواسطة روتين فرعي A، ينتظر روتين فرعي آخر B إلغاء قفل القفل. تسجّل أيضًا (ضمن مقياس "التأخير") الوقت الذي انتظرت فيه goroutine المحظورة القفل. في هذا المثال، هناك مجموعة واحدة من عمليات الانتظار وكان إجمالي وقت الانتظار للقفل 11.03 ثانية:

يتألف الرمز الذي ينشئ هذا الملف من 4 إجراءات goroutine تتنافس على mutex:

main.go

func contention(d time.Duration) {
        contentionImpl(d)
}

func contentionImpl(d time.Duration) {
        for {
                mu.Lock()
                time.Sleep(d)
                mu.Unlock()
        }
}
...
func main() {
        ...
        for i := 0; i < 4; i++ {
                go contention(time.Duration(i) * 50 * time.Millisecond)
        }
}

في هذا الدرس التطبيقي، تعرّفت على كيفية إعداد برنامج Go لاستخدامه مع Stackdriver Profiler. تعرّفت أيضًا على كيفية جمع بيانات الأداء وعرضها وتحليلها باستخدام هذه الأداة. يمكنك الآن تطبيق مهارتك الجديدة على الخدمات الفعلية التي تديرها على Google Cloud Platform.

لقد تعلّمت كيفية إعداد Stackdriver Profiler واستخدامه.

مزيد من المعلومات

الترخيص

يخضع هذا العمل لترخيص Creative Commons Attribution 2.0 Generic License.