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

1. نظرة عامة

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

95c034c70c9cac22.png

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

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

ما ستتعلمه

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

المتطلبات

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

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

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

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

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

2. الإعداد والمتطلبات

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

  1. سجِّل الدخول إلى Cloud Console وأنشِئ مشروعًا جديدًا أو أعِد استخدام مشروع حالي. إذا لم يكن لديك حساب على Gmail أو Google Workspace، عليك إنشاء حساب.

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

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

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

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

Google Cloud Shell

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

تفعيل Cloud Shell

  1. من Cloud Console، انقر على تفعيل Cloud Shell 4292cbf4971c9786.png.

bce75f34b2c53987.png

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

70f315d7b402b476.png

يستغرق توفير Cloud Shell والاتصال به بضع لحظات فقط.

fbe3a0674c982259.png

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

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

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

ناتج الأمر

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. نفِّذ الأمر التالي في Cloud Shell للتأكّد من أنّ أمر gcloud يعرف مشروعك:
gcloud config list project

ناتج الأمر

[core]
project = <PROJECT_ID>

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

gcloud config set project <PROJECT_ID>

ناتج الأمر

Updated property [core/project].

3- الانتقال إلى Cloud Profiler

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

37ad0df7ddb2ad17.png

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

d275a5f61ed31fb2.png

حان الوقت الآن للحصول على ملف تعريف.

4. إنشاء ملف تعريف لمقياس الأداء

سنستخدم تطبيق 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 يدويًا. لإجراء ذلك، انقر على الزر "الآن" في أداة اختيار الفاصل الزمني مرّتين:

650051097b651b91.png

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

47a763d4dc78b6e8.png

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

b5d7b4b5051687c9.png

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

5- تحليل بيانات Profiler

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

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

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

fae661c9fe6c58df.png

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

main.go

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

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

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

eb1d97491782b03f.png

الرمز الذي يتطلّب ذاكرة كبيرة

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

f6311c8c841d04c4.png

يشير جدول الملخّص أعلى الرسم البياني الشعلة إلى أنّ إجمالي مقدار الذاكرة المستخدَمة في التطبيق يبلغ في المتوسط ‎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:

ebd57fdff01dede9.png

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

main.go

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

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

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

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

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

83f00dca4a0f768e.png

يتألف الرمز البرمجي الذي ينشئ هذا الملف الشخصي من 4 إجراءات goroutine تتنافس على قفل تبادلي:

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)
        }
}

6. ملخّص

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

7. تهانينا!

تعرّفت على كيفية ضبط Cloud Profiler واستخدامه.

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

الترخيص

يخضع هذا العمل لترخيص المشاع الإبداعي مع نسب العمل إلى مؤلفه 2.0 Generic License.