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

1. نظرة عامة

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

95c034c70c9cac22.png

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

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

المعلومات التي ستطّلع عليها

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

المتطلبات

  • مشروع Google Cloud Platform
  • متصفح، مثل Chrome أو Firefox
  • الإلمام بأدوات تحرير النصوص القياسية في Linux مثل Vim أو EMAC أو 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 UI من خلال النقر على 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) وأجزاء وحدات الذاكرة المتعددة وسلاسل المحادثات. يعمل الرمز هنا على تفعيل جمع ملفات كائن المزامنة (المعروفة أيضًا باسم "contention") الشخصية.

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

$ 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 وكتابة "خروج".

5- تحليل بيانات المحلّل

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

رمز تستهلك وحدة المعالجة المركزية (CPU)

اختَر نوع الملف الشخصي لوحدة المعالجة المركزية (CPU). بعد تحميل واجهة المستخدم للدالة، سترى في الرسم البياني لل صفحات الكتلية الأربعة الخاصة بدالة load والتي تحتسب معًا استهلاك وحدة المعالجة المركزية (CPU):

fae661c9fe6c58df.png

وقد صُممت هذه الدالة خصيصًا لاستهلاك الكثير من دورات وحدة المعالجة المركزية (CPU) عن طريق تنفيذ حلقة دقيقة:

main.go

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

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

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

eb1d97491782b03f.png

رمز برمجي مكثّف للذاكرة

التبديل الآن إلى "لقطات لأجزاء من الذاكرة" نوع الملف الشخصي. تأكّد من إزالة أيّ فلاتر أنشأتها في التجارب السابقة. من المفترض أن يظهر لك الآن رسم بياني للهبوط حيث يتم عرض 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()
        }

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

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

رمز برمجي مكثّف للمنافسة

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

83f00dca4a0f768e.png

يتكون الرمز الذي يُنشئ هذا الملف الشخصي من 4 وحدات غوروتين تتصارع على كائن تسلسلي:

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 مع نسب العمل إلى مؤلف عام.