أداة لتحسين أداء تطبيقك في Go (الجزء 2: المحلّل)

1. مقدمة

e0509e8a07ad5537.png

تاريخ آخر تعديل: 2022-07-14

إمكانية تتبّع بيانات التطبيق

إمكانية تتبّع البيانات وContinuous Profiler

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

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

هذا الدرس التطبيقي حول الترميز هو الجزء الثاني من السلسلة ويتناول عملية إعداد أداة مستمرة لتسجيل الأداء. يتناول الجزء 1 عملية التتبُّع الموزّع باستخدام OpenTelemetry وCloud Trace، وسنتعرّف فيه على كيفية تحديد المشاكل التي تؤدي إلى بطء الأداء في الخدمات المصغّرة بشكل أفضل.

ما ستنشئه

في هذا الدرس التطبيقي حول الترميز، ستستخدم أداة Continuous Profiler في خدمة الخادم الخاصة بـ "تطبيق شكسبير" (المعروف أيضًا باسم Shakesapp) الذي يعمل على مجموعة Google Kubernetes Engine. بنية Shakesapp هي كما هو موضح أدناه:

44e243182ced442f.png

  • يرسل Loadgen سلسلة طلب بحث إلى العميل عبر HTTP
  • ينقل العملاء طلب البحث من أداة إنشاء الحمل إلى الخادم في gRPC
  • يقبل الخادم طلب البحث من العميل، ويستردّ جميع أعمال شكسبير بتنسيق نصي من Google Cloud Storage، ويبحث عن الأسطر التي تحتوي على طلب البحث ويعرض رقم السطر المطابق للعميل

في الجزء 1، تبيّن لك أنّ المؤثِّر السلبي يكمن في مكان ما في خدمة الخادم، ولكن لم تتمكّن من تحديد السبب الدقيق.

ما ستتعلمه

  • كيفية تضمين أداة تحليل الأداء
  • كيفية التحقيق في المشاكل التي تؤدي إلى بطء الأداء في Cloud Profiler

يوضّح هذا الدرس التطبيقي حول الترميز كيفية إعداد أداة تتبُّع مستمرة في تطبيقك.

المتطلبات

  • معرفة أساسية بلغة Go
  • معرفة أساسية بمنصة Kubernetes

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

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

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

إذا كان لديك مشروع حالي، انقر على القائمة المنسدلة لاختيار المشروع في أعلى يمين وحدة التحكّم:

7a32e5469db69e9.png

وانقر على الزر "مشروع جديد" في مربّع الحوار الناتج لإنشاء مشروع جديد:

7136b3ee36ebaf89.png

إذا لم يكن لديك مشروع، من المفترض أن يظهر لك مربّع حوار مشابه لما يلي لإنشاء مشروعك الأول:

870a3cbd6541ee86.png

يتيح لك مربّع حوار إنشاء المشروع اللاحق إدخال تفاصيل مشروعك الجديد:

affdc444517ba805.png

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

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

15d0ef27a8fbab27.png

لن تكلفك تجربة هذا الدرس التطبيقي حول الترميز أكثر من بضعة دولارات، ولكن قد تكون التكلفة أعلى إذا قررت استخدام المزيد من الموارد أو إذا تركتها قيد التشغيل (راجِع قسم "التنظيف" في نهاية هذا المستند). تتوفر أسعار Google Cloud Trace وGoogle Kubernetes Engine وGoogle Artifact Registry في المستندات الرسمية.

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

إعداد Google Cloud Shell

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

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

لتفعيل Cloud Shell من Cloud Console، ما عليك سوى النقر على "تفعيل Cloud Shell" gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A (يستغرق توفير البيئة والاتصال بها بضع لحظات فقط).

JjEuRXGg0AYYIY6QZ8d-66gx_Mtc-_jDE9ijmbXLJSAXFvJt-qUpNtsBsYjNpv2W6BQSrDc1D-ARINNQ-1EkwUhz-iUK-FUCZhJ-NtjvIEx9pIkE-246DomWuCfiGHK78DgoeWkHRw

Screen Shot 2017-06-14 at 10.13.43 PM.png

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

gcloud auth list

ناتج الأمر

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

ناتج الأمر

[core]
project = <PROJECT_ID>

إذا لم يتم ضبط المشروع لسبب ما، ما عليك سوى تنفيذ الأمر التالي:

gcloud config set project <PROJECT_ID>

هل تبحث عن PROJECT_ID؟ يمكنك الاطّلاع على المعرّف الذي استخدمته في خطوات الإعداد أو البحث عنه في لوحة بيانات Cloud Console:

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvO351c5iBv506B3ZwghZoiRg6cz23Qw

يضبط Cloud Shell أيضًا بعض متغيرات البيئة تلقائيًا، ما قد يكون مفيدًا عند تنفيذ الأوامر المستقبلية.

echo $GOOGLE_CLOUD_PROJECT

ناتج الأمر

<PROJECT_ID>

أخيرًا، اضبط المنطقة التلقائية وإعدادات المشروع.

gcloud config set compute/zone us-central1-f

يمكنك اختيار مجموعة متنوعة من المناطق المختلفة. لمزيد من المعلومات، يُرجى الاطّلاع على الأقاليم والمناطق.

الانتقال إلى إعدادات اللغة

في هذا الدرس التطبيقي حول الترميز، نستخدم لغة Go لجميع الرمز المصدر. نفِّذ الأمر التالي على Cloud Shell وتأكَّد مما إذا كان إصدار Go هو 1.17 أو إصدار أحدث.

go version

ناتج الأمر

go version go1.18.3 linux/amd64

إعداد مجموعة Google Kubernetes

في هذا الدرس التطبيقي حول الترميز، ستشغّل مجموعة من الخدمات المصغّرة على Google Kubernetes Engine (GKE). تتضمّن هذه التجربة العملية الخطوات التالية:

  1. تنزيل المشروع الأساسي إلى Cloud Shell
  2. إنشاء خدمات مصغّرة في حاويات
  3. تحميل الحاويات إلى Google Artifact Registry (GAR)
  4. نشر الحاويات على GKE
  5. تعديل رمز المصدر للخدمات من أجل قياس حالة التطبيق
  6. الانتقال إلى الخطوة 2

تفعيل Kubernetes Engine

أولاً، نُعدّ مجموعة Kubernetes حيث يعمل Shakesapp على GKE، لذا علينا تفعيل GKE. انتقِل إلى القائمة "Kubernetes Engine" واضغط على الزر "تفعيل".

548cfd95bc6d344d.png

أنت الآن جاهز لإنشاء مجموعة Kubernetes.

إنشاء مجموعة Kubernetes

في Cloud Shell، نفِّذ الأمر التالي لإنشاء مجموعة Kubernetes. يُرجى التأكّد من أنّ قيمة المنطقة تندرج ضمن المنطقة التي ستستخدمها لإنشاء مستودع Artifact Registry. غيِّر قيمة المنطقة us-central1-f إذا كانت منطقة المستودع لا تغطي المنطقة.

gcloud container clusters create otel-trace-codelab2 \
--zone us-central1-f \
--release-channel rapid \
--preemptible \
--enable-autoscaling \
--max-nodes 8 \
--no-enable-ip-alias \
--scopes cloud-platform

ناتج الأمر

Note: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s).
Creating cluster otel-trace-codelab2 in us-central1-f... Cluster is being health-checked (master is healthy)...done.     
Created [https://container.googleapis.com/v1/projects/development-215403/zones/us-central1-f/clusters/otel-trace-codelab2].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-f/otel-trace-codelab2?project=development-215403
kubeconfig entry generated for otel-trace-codelab2.
NAME: otel-trace-codelab2
LOCATION: us-central1-f
MASTER_VERSION: 1.23.6-gke.1501
MASTER_IP: 104.154.76.89
MACHINE_TYPE: e2-medium
NODE_VERSION: 1.23.6-gke.1501
NUM_NODES: 3
STATUS: RUNNING

إعداد Artifact Registry وskaffold

لدينا الآن مجموعة Kubernetes جاهزة للنشر. بعد ذلك، نستعدّ لإنشاء سجلّ حاويات لإرسال الحاويات ونشرها. بالنسبة إلى هذه الخطوات، علينا إعداد Artifact Registry (GAR) وskaffold لاستخدامه.

إعداد Artifact Registry

انتقِل إلى قائمة "Artifact Registry" واضغط على الزر "تفعيل".

45e384b87f7cf0db.png

بعد بضع لحظات، سيظهر لك متصفّح مستودع GAR. انقر على الزر "إنشاء مستودع" (CREATE REPOSITORY) وأدخِل اسم المستودع.

d6a70f4cb4ebcbe3.png

في هذا الدرس العملي، سأسمّي المستودع الجديد trace-codelab. تنسيق العنصر هو "Docker" ونوع الموقع الجغرافي هو "المنطقة". اختَر المنطقة القريبة من المنطقة التلقائية التي ضبطتها في Google Compute Engine. على سبيل المثال، تم اختيار "us-central1-f" أعلاه، لذا سنختار هنا "us-central1 (آيوا)". بعد ذلك، انقر على الزر "إنشاء".

9c2d1ce65258ef70.png

يظهر الآن "trace-codelab" في متصفّح المستودع.

7a3c1f47346bea15.png

سنعود إلى هنا لاحقًا للتحقّق من مسار التسجيل.

إعداد Skaffold

Skaffold هي أداة مفيدة عند العمل على إنشاء خدمات مصغّرة تعمل على Kubernetes. يتعامل مع سير عمل إنشاء حاويات التطبيقات ونقلها ونشرها باستخدام مجموعة صغيرة من الأوامر. تستخدم Skaffold تلقائيًا Docker Registry كسجلّ حاويات، لذا عليك ضبط Skaffold للتعرّف على GAR عند إرسال الحاويات إليه.

افتح Cloud Shell مرة أخرى وتأكَّد من تثبيت Skaffold. (تثبِّت Cloud Shell أداة Skaffold في البيئة تلقائيًا). نفِّذ الأمر التالي واطّلِع على إصدار Skaffold.

skaffold version

ناتج الأمر

v1.38.0

يمكنك الآن تسجيل المستودع التلقائي الذي سيستخدمه Skaffold. للحصول على مسار السجلّ، انتقِل إلى لوحة بيانات Artifact Registry وانقر على اسم المستودع الذي أعددته في الخطوة السابقة.

7a3c1f47346bea15.png

بعد ذلك، ستظهر مسارات شريط التنقّل في أعلى الصفحة. انقر على رمز e157b1359c3edc06.png لنسخ مسار التسجيل إلى الحافظة.

e0f2ae2144880b8b.png

عند النقر على زر النسخ، يظهر مربّع الحوار في أسفل المتصفّح مع رسالة مثل:

تم نسخ "us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab"

ارجع إلى Cloud Shell. نفِّذ الأمر skaffold config set default-repo مع القيمة التي نسختها للتو من لوحة البيانات.

skaffold config set default-repo us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab

ناتج الأمر

set value default-repo to us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab for context gke_stackdriver-sandbox-3438851889_us-central1-b_stackdriver-sandbox

عليك أيضًا ضبط سجلّ Docker. نفِّذ الأمر التالي:

gcloud auth configure-docker us-central1-docker.pkg.dev --quiet

ناتج الأمر

{
  "credHelpers": {
    "gcr.io": "gcloud",
    "us.gcr.io": "gcloud",
    "eu.gcr.io": "gcloud",
    "asia.gcr.io": "gcloud",
    "staging-k8s.gcr.io": "gcloud",
    "marketplace.gcr.io": "gcloud",
    "us-central1-docker.pkg.dev": "gcloud"
  }
}
Adding credentials for: us-central1-docker.pkg.dev

يمكنك الآن الانتقال إلى الخطوة التالية لإعداد حاوية Kubernetes على GKE.

ملخّص

في هذه الخطوة، عليك إعداد بيئة الدرس العملي:

  • إعداد Cloud Shell
  • تم إنشاء مستودع Artifact Registry لسجلّ الحاويات
  • إعداد Skaffold لاستخدام سجلّ الحاويات
  • أنشأنا مجموعة Kubernetes حيث تعمل الخدمات الدقيقة للدروس التطبيقية

التالي

في الخطوة التالية، ستفعّل أداة Continuous Profiler في خدمة الخادم.

3- إنشاء الخدمات المصغّرة ونشرها

تنزيل مواد الدرس التطبيقي

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

cd ~
git clone https://github.com/ymotongpoo/opentelemetry-trace-codelab-go.git
cd opentelemetry-trace-codelab-go

تكون بنية الدليل الخاص بالمشروع على النحو التالي:

.
├── README.md
├── step0
│   ├── manifests
│   ├── proto
│   ├── skaffold.yaml
│   └── src
├── step1
│   ├── manifests
│   ├── proto
│   ├── skaffold.yaml
│   └── src
├── step2
│   ├── manifests
│   ├── proto
│   ├── skaffold.yaml
│   └── src
├── step3
│   ├── manifests
│   ├── proto
│   ├── skaffold.yaml
│   └── src
├── step4
│   ├── manifests
│   ├── proto
│   ├── skaffold.yaml
│   └── src
├── step5
│   ├── manifests
│   ├── proto
│   ├── skaffold.yaml
│   └── src
└── step6
    ├── manifests
    ├── proto
    ├── skaffold.yaml
    └── src
  • ملفات البيانات: ملفات بيانات Kubernetes
  • proto: تعريف proto للتواصل بين العميل والخادم
  • src: أدلة لرمز المصدر لكل خدمة
  • skaffold.yaml: ملف الإعداد الخاص بـ Skaffold

في هذا الدرس التطبيقي حول الترميز، ستعدّل الرمز المصدر الموجود في المجلد step4. يمكنك أيضًا الرجوع إلى رمز المصدر في مجلدات step[1-6] للاطّلاع على التغييرات منذ البداية. (يتناول الجزء 1 الخطوات من 0 إلى 4، ويتناول الجزء 2 الخطوتين 5 و6)

تنفيذ أمر skaffold

أخيرًا، أنت جاهز لإنشاء المحتوى الكامل ونشره وتوزيعه على مجموعة Kubernetes التي أنشأتها للتو. يبدو أنّ هذه العملية تتضمّن خطوات متعدّدة، ولكن في الواقع، ينفّذ Skaffold كل شيء نيابةً عنك. لنجرِّب ذلك باستخدام الأمر التالي:

cd step4
skaffold dev

بعد تنفيذ الأمر مباشرةً، سيظهر لك ناتج السجلّ الخاص بـ docker build ويمكنك التأكّد من أنّه تم إرساله بنجاح إلى السجلّ.

ناتج الأمر

...
---> Running in c39b3ea8692b
 ---> 90932a583ab6
Successfully built 90932a583ab6
Successfully tagged us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step1
The push refers to repository [us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice]
cc8f5a05df4a: Preparing
5bf719419ee2: Preparing
2901929ad341: Preparing
88d9943798ba: Preparing
b0fdf826a39a: Preparing
3c9c1e0b1647: Preparing
f3427ce9393d: Preparing
14a1ca976738: Preparing
f3427ce9393d: Waiting
14a1ca976738: Waiting
3c9c1e0b1647: Waiting
b0fdf826a39a: Layer already exists
88d9943798ba: Layer already exists
f3427ce9393d: Layer already exists
3c9c1e0b1647: Layer already exists
14a1ca976738: Layer already exists
2901929ad341: Pushed
5bf719419ee2: Pushed
cc8f5a05df4a: Pushed
step1: digest: sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe size: 2001

بعد إرسال جميع حاويات الخدمة، تبدأ عمليات نشر Kubernetes تلقائيًا.

ناتج الأمر

sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8 size: 1997
Tags used in deployment:
 - serverservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step4@sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe
 - clientservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/clientservice:step4@sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8
 - loadgen -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/loadgen:step4@sha256:eea2e5bc8463ecf886f958a86906cab896e9e2e380a0eb143deaeaca40f7888a
Starting deploy...
 - deployment.apps/clientservice created
 - service/clientservice created
 - deployment.apps/loadgen created
 - deployment.apps/serverservice created
 - service/serverservice created

بعد عملية النشر، ستظهر لك سجلات التطبيق الفعلية التي تم إرسالها إلى stdout في كل حاوية على النحو التالي:

ناتج الأمر

[client] 2022/07/14 06:33:15 {"match_count":3040}
[loadgen] 2022/07/14 06:33:15 query 'love': matched 3040
[client] 2022/07/14 06:33:15 {"match_count":3040}
[loadgen] 2022/07/14 06:33:15 query 'love': matched 3040
[client] 2022/07/14 06:33:16 {"match_count":3040}
[loadgen] 2022/07/14 06:33:16 query 'love': matched 3040
[client] 2022/07/14 06:33:19 {"match_count":463}
[loadgen] 2022/07/14 06:33:19 query 'tear': matched 463
[loadgen] 2022/07/14 06:33:20 query 'world': matched 728
[client] 2022/07/14 06:33:20 {"match_count":728}
[client] 2022/07/14 06:33:22 {"match_count":463}
[loadgen] 2022/07/14 06:33:22 query 'tear': matched 463

يُرجى العِلم أنّه في هذه المرحلة، يجب أن تظهر لك أي رسائل من الخادم. حسنًا، أنت الآن جاهز لبدء تزويد تطبيقك بأدوات OpenTelemetry لتتبُّع الخدمات الموزّعة.

قبل بدء إعداد الخدمة، يُرجى إيقاف مجموعتك باستخدام Ctrl-C.

ناتج الأمر

...
[client] 2022/07/14 06:34:57 {"match_count":1}
[loadgen] 2022/07/14 06:34:57 query 'what's past is prologue': matched 1
^CCleaning up...
 - W0714 06:34:58.464305   28078 gcp.go:120] WARNING: the gcp auth plugin is deprecated in v1.22+, unavailable in v1.25+; use gcloud instead.
 - To learn more, consult https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke
 - deployment.apps "clientservice" deleted
 - service "clientservice" deleted
 - deployment.apps "loadgen" deleted
 - deployment.apps "serverservice" deleted
 - service "serverservice" deleted

ملخّص

في هذه الخطوة، أعددت مواد الدرس العملي في بيئتك وتأكّدت من أنّ أداة Skaffold تعمل على النحو المتوقّع.

التالي

في الخطوة التالية، ستعدّل رمز المصدر لخدمة loadgen من أجل تسجيل معلومات التتبُّع.

4. قياس حالة تطبيق وكيل Cloud Profiler

مفهوم إنشاء الملفات الشخصية المستمر

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

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

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

تضمين وكيل Cloud Profiler

افتح Cloud Shell Editor من خلال النقر على الزر 776a11bfb2122549.png في أعلى يسار Cloud Shell. افتح step4/src/server/main.go من "المستكشف" في اللوحة اليمنى وابحث عن الوظيفة الرئيسية.

step4/src/server/main.go

func main() {
        ...
        // step2. setup OpenTelemetry
        tp, err := initTracer()
        if err != nil {
                log.Fatalf("failed to initialize TracerProvider: %v", err)
        }
        defer func() {
                if err := tp.Shutdown(context.Background()); err != nil {
                        log.Fatalf("error shutting down TracerProvider: %v", err)
                }
        }()
        // step2. end setup

        svc := NewServerService()
        // step2: add interceptor
        interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider())
        srv := grpc.NewServer(
                grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor(interceptorOpt)),
                grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor(interceptorOpt)),
        )
        // step2: end adding interceptor
        shakesapp.RegisterShakespeareServiceServer(srv, svc)
        healthpb.RegisterHealthServer(srv, svc)
        if err := srv.Serve(lis); err != nil {
                log.Fatalf("error serving server: %v", err)
        }
}

في الدالة main، سترى بعض رموز الإعداد الخاصة بـ OpenTelemetry وgRPC، وقد تم تنفيذ ذلك في الجزء 1 من الدرس التطبيقي حول الترميز. ستضيف الآن قياس حالة التطبيق لوكيل Cloud Profiler هنا. كما فعلنا مع initTracer()، يمكنك كتابة دالة باسم initProfiler() لتسهيل القراءة.

step4/src/server/main.go

import (
        ...
        "cloud.google.com/go/profiler" // step5. add profiler package
        "cloud.google.com/go/storage"
        ...
)

// step5: add Profiler initializer
func initProfiler() {
        cfg := profiler.Config{
                Service:              "server",
                ServiceVersion:       "1.0.0",
                NoHeapProfiling:      true,
                NoAllocProfiling:     true,
                NoGoroutineProfiling: true,
                NoCPUProfiling:       false,
        }
        if err := profiler.Start(cfg); err != nil {
                log.Fatalf("failed to launch profiler agent: %v", err)
        }
}

لنلقِ نظرة فاحصة على الخيارات المحدّدة في العنصر profiler.Config{}.

  • الخدمة: اسم الخدمة التي يمكنك اختيارها والتبديل إليها في لوحة بيانات أداة تحليل الأداء
  • ServiceVersion: اسم إصدار الخدمة. يمكنك مقارنة مجموعات بيانات الملفات الشخصية استنادًا إلى هذه القيمة.
  • NoHeapProfiling: لإيقاف تحليل استهلاك الذاكرة
  • NoAllocProfiling: لإيقاف تحليل تخصيص الذاكرة
  • NoGoroutineProfiling: لإيقاف تحليل goroutine
  • NoCPUProfiling: لإيقاف تحليل وحدة المعالجة المركزية

في هذا الدرس التطبيقي حول الترميز، سنفعّل فقط إمكانية إنشاء ملفات تعريف لوحدة المعالجة المركزية.

الآن، ما عليك فعله هو استدعاء هذه الدالة في الدالة main. احرص على استيراد حزمة Cloud Profiler في قسم الاستيراد.

step4/src/server/main.go

func main() {
        ...
        defer func() {
                if err := tp.Shutdown(context.Background()); err != nil {
                        log.Fatalf("error shutting down TracerProvider: %v", err)
                }
        }()
        // step2. end setup

        // step5. start profiler
        go initProfiler()
        // step5. end

        svc := NewServerService()
        // step2: add interceptor
        ...
}

لاحظ أنّك تستدعي الدالة initProfiler() باستخدام الكلمة الرئيسية go. لأنّ profiler.Start() يحظر، لذا عليك تشغيله في روتين فرعي آخر. أنت الآن جاهز لإنشاء التطبيق. احرص على تنفيذ go mod tidy قبل عملية النشر.

go mod tidy

يمكنك الآن نشر مجموعتك باستخدام خدمة الخادم الجديدة.

skaffold dev

يستغرق ظهور الرسم البياني الشعلة على Cloud Profiler عادةً بضع دقائق. اكتب "Profiler" في مربّع البحث في أعلى الصفحة وانقر على رمز Profiler.

3d8ca8a64b267a40.png

سيظهر لك بعد ذلك الرسم البياني التالي.

7f80797dddc0128d.png

ملخّص

في هذه الخطوة، عليك تضمين وكيل Cloud Profiler في خدمة الخادم والتأكّد من أنّه ينشئ رسمًا بيانيًا شجريًا.

التالي

في الخطوة التالية، ستتحقّق من سبب عنق الزجاجة في التطبيق باستخدام الرسم البياني الشعلة.

5- تحليل الرسم البياني المفصّل لأداء الرموز في Cloud Profiler

ما هو الرسم البياني المفصَّل لأداء الرموز؟

"الرسم البياني الشعلة" هو إحدى الطرق لتصوُّر بيانات الملف الشخصي. للحصول على شرح مفصّل، يُرجى الرجوع إلى المستند، ولكن إليك ملخّصًا موجزًا:

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

في ضوء ذلك، لنلقِ نظرة على الرسم البياني الشعلة الذي تم الحصول عليه.

7f80797dddc0128d.png

تحليل "الرسم البياني المفصَّل لأداء الرموز"

في القسم السابق، تعرّفت على أنّ كل شريط في الرسم البياني الشعلة يعبّر عن طلب إجراء الدالة/الطريقة، وأنّ طوله يشير إلى استخدام الموارد في الدالة/الطريقة. يرتّب الرسم البياني الشريطي في Cloud Profiler الشريط بترتيب تنازلي أو حسب الطول من اليسار إلى اليمين، ويمكنك البدء بالنظر إلى أعلى يسار الرسم البياني أولاً.

6d90760c6c1183cd.png

في حالتنا، من الواضح أنّ grpc.(*Server).serveStreams.func1.2 يستهلك معظم وقت وحدة المعالجة المركزية، ومن خلال الاطّلاع على حزمة التنفيذ من الأعلى إلى الأسفل، نجد أنّ معظم الوقت يتم استخدامه في main.(*serverService).GetMatchCount، وهو معالج خادم gRPC في خدمة الخادم.

ضمن GetMatchCount، سترى سلسلة من دوال regexp: regexp.MatchString وregexp.Compile. وهي من الحزمة العادية، أي يجب أن يتم اختبارها جيدًا من عدة وجهات نظر، بما في ذلك الأداء. لكنّ النتيجة هنا توضّح أنّ معدّل استخدام مورد وقت وحدة المعالجة المركزية مرتفع في regexp.MatchString وregexp.Compile. في ضوء هذه الحقائق، نفترض أنّ استخدام regexp.MatchString مرتبط بمشاكل الأداء. لذا، لنقرأ الرمز المصدر الذي يتم فيه استخدام الدالة.

step4/src/server/main.go

func (s *serverService) GetMatchCount(ctx context.Context, req *shakesapp.ShakespeareRequest) (*shakesapp.ShakespeareResponse, error) {
        resp := &shakesapp.ShakespeareResponse{}
        texts, err := readFiles(ctx, bucketName, bucketPrefix)
        if err != nil {
                return resp, fmt.Errorf("fails to read files: %s", err)
        }
        for _, text := range texts {
                for _, line := range strings.Split(text, "\n") {
                        line, query := strings.ToLower(line), strings.ToLower(req.Query)
                        isMatch, err := regexp.MatchString(query, line)
                        if err != nil {
                                return resp, err
                        }
                        if isMatch {
                                resp.MatchCount++
                        }
                }
        }
        return resp, nil
}

هذا هو المكان الذي يتم فيه استدعاء regexp.MatchString. من خلال قراءة رمز المصدر، قد تلاحظ أنّه يتم استدعاء الدالة داخل حلقة for المتداخلة. لذلك قد يكون استخدام هذه الدالة غير صحيح. لنبحث عن GoDoc الخاص بـ regexp.

80b8a4ba1931ff7b.png

وفقًا للمستند، تعمل الدالة regexp.MatchString على تجميع نمط التعبير العادي في كل عملية استدعاء. إذًا، يبدو أنّ سبب الاستهلاك الكبير للموارد هو ما يلي.

ملخّص

في هذه الخطوة، افترضت سبب استهلاك الموارد من خلال تحليل الرسم البياني الشعلة.

التالي

في الخطوة التالية، ستعدّل رمز المصدر لخدمة الخادم وتؤكّد التغيير من الإصدار 1.0.0.

6. تعديل الرمز المصدر ومقارنة الرسوم البيانية الشعلية

تعديل رمز المصدر

في الخطوة السابقة، افترضت أنّ استخدام regexp.MatchString مرتبط باستهلاك الموارد الكبير. لنحلّ هذه المشكلة. افتح الرمز البرمجي وغيِّر هذا الجزء قليلاً.

step4/src/server/main.go

func (s *serverService) GetMatchCount(ctx context.Context, req *shakesapp.ShakespeareRequest) (*shakesapp.ShakespeareResponse, error) {
        resp := &shakesapp.ShakespeareResponse{}
        texts, err := readFiles(ctx, bucketName, bucketPrefix)
        if err != nil {
                return resp, fmt.Errorf("fails to read files: %s", err)
        }

        // step6. considered the process carefully and naively tuned up by extracting
        // regexp pattern compile process out of for loop.
        query := strings.ToLower(req.Query)
        re := regexp.MustCompile(query)
        for _, text := range texts {
                for _, line := range strings.Split(text, "\n") {
                        line = strings.ToLower(line)
                        isMatch := re.MatchString(line)
                        // step6. done replacing regexp with strings
                        if isMatch {
                                resp.MatchCount++
                        }
                }
        }
        return resp, nil
}

كما ترى، تم الآن استخراج عملية تجميع نمط التعبير العادي من regexp.MatchString ونقلها خارج حلقة for المتداخلة.

قبل نشر هذا الرمز، احرص على تعديل سلسلة الإصدار في الدالة initProfiler().

step4/src/server/main.go

func initProfiler() {
        cfg := profiler.Config{
                Service:              "server",
                ServiceVersion:       "1.1.0", // step6. update version
                NoHeapProfiling:      true,
                NoAllocProfiling:     true,
                NoGoroutineProfiling: true,
                NoCPUProfiling:       false,
        }
        if err := profiler.Start(cfg); err != nil {
                log.Fatalf("failed to launch profiler agent: %v", err)
        }
}

لنطّلع الآن على طريقة عملها. يمكنك نشر المجموعة باستخدام أمر skaffold.

skaffold dev

وبعد فترة، أعِد تحميل لوحة بيانات Cloud Profiler واطّلِع على شكلها.

283cfcd4c13716ad.png

احرص على تغيير الإصدار إلى "1.1.0" لكي تظهر لك الملفات الشخصية من الإصدار 1.1.0 فقط. كما تلاحظ، تم تقليل طول شريط GetMatchCount ونسبة استخدام وقت وحدة المعالجة المركزية (أي أصبح الشريط أقصر).

e3a1456b4aada9a5.png

لا يمكنك فقط الاطّلاع على الرسم البياني الشريطي لإصدار واحد، بل يمكنك أيضًا مقارنة الاختلافات بين إصدارين.

841dec77d8ba5595.png

غيِّر قيمة القائمة المنسدلة "المقارنة مع" إلى "الإصدار"، وغيِّر قيمة "الإصدار الذي تتم مقارنته" إلى "1.0.0"، وهو الإصدار الأصلي.

5553844292d6a537.png

سيظهر لك هذا النوع من الرسومات البيانية. شكل الرسم البياني هو نفسه في الإصدار 1.1.0، ولكن الألوان مختلفة. في وضع المقارنة، يشير اللون إلى ما يلي:

  • أزرق: القيمة (استهلاك الموارد) التي تم تخفيضها
  • اللون البرتقالي: القيمة (استهلاك الموارد) التي تم تحقيقها
  • الرمادي: محايد

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

ca08d942dc1e2502.png

ويشير إلى أنّ إجمالي وقت وحدة المعالجة المركزية (CPU) انخفض من 5.26 ثانية إلى 2.88 ثانية (الإجمالي هو 10 ثوانٍ = فترة أخذ العيّنات). هذا تحسُّن كبير!

يمكنك الآن تحسين أداء تطبيقك من خلال تحليل بيانات الملف الشخصي.

ملخّص

في هذه الخطوة، أجريت تعديلاً في خدمة الخادم وأكّدت التحسين في وضع المقارنة في Cloud Profiler.

التالي

في الخطوة التالية، ستعدّل رمز المصدر لخدمة الخادم وتؤكّد التغيير من الإصدار 1.0.0.

7. خطوة إضافية: تأكيد التحسّن في "الرسم البياني التسلسلي لتتبُّع الأثر"

الفرق بين التتبُّع الموزّع والتحليل المستمر

في الجزء 1 من الدرس العملي، تأكّدت من أنّه يمكنك تحديد الخدمة التي تشكّل عنق الزجاجة في جميع الخدمات المصغّرة لمسار الطلب، وأنّه لا يمكنك تحديد السبب الدقيق لعنق الزجاجة في الخدمة المحدّدة. في الجزء الثاني من هذا الدرس التطبيقي حول الترميز، تعلّمت أنّ عملية إنشاء الملفات الشخصية المستمرة تتيح لك تحديد المؤثِّر السلبي داخل الخدمة الفردية من حِزم التنفيذ.

في هذه الخطوة، لنراجع الرسم البياني الشلالي من التتبُّع الموزّع (Cloud Trace) ونرى الفرق بينه وبين إنشاء الملفات الشخصية المستمر.

هذا الرسم البياني للشلال هو أحد التتبّعات التي تتضمّن طلب البحث "حب". يستغرق ذلك حوالي 6.7 ثانية (6700 ملي ثانية) إجمالاً.

e2b7dec25926ee51.png

وهذا هو الأداء بعد التحسين للاستعلام نفسه. كما تلاحظ، أصبح إجمالي وقت الاستجابة الآن 1.5 ثانية (1500 ملي ثانية)، وهو تحسّن كبير مقارنةً بالتنفيذ السابق.

feeb7207f36c7e5e.png

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

من ناحية أخرى، يكون التتبُّع الموزّع مستندًا إلى الأحداث، بينما يكون الملف الشخصي المستمر إحصائيًا. يحتوي كل تتبُّع على رسم بياني مختلف لوقت الاستجابة، وتحتاج إلى تنسيق مختلف، مثل التوزيع، للحصول على مؤشر التغييرات في وقت الاستجابة.

ملخّص

في هذه الخطوة، تحقّقت من الفرق بين التتبُّع الموزّع والقياس المتواصل للأداء.

8. تهانينا

لقد أنشأت عمليات تتبُّع موزّعة بنجاح باستخدام OpenTelemery وأكّدت أوقات استجابة الطلبات في جميع الخدمات المصغّرة على Google Cloud Trace.

بالنسبة إلى التمارين الموسّعة، يمكنك تجربة المواضيع التالية بنفسك.

  • ترسل عملية التنفيذ الحالية جميع الفترات الزمنية التي تم إنشاؤها من خلال عملية التحقّق من الصحة. (grpc.health.v1.Health/Check) كيف يمكن فلترة هذه الفترات من Cloud Trace؟ يمكنك الاطّلاع على التلميح هنا.
  • ربط سجلّات الأحداث بالنطاقات والاطّلاع على طريقة عملها في Google Cloud Trace وGoogle Cloud Logging يمكنك الاطّلاع على التلميح هنا.
  • استبدِل بعض الخدمات بالخدمات المتوفّرة بلغة أخرى وحاوِل تفعيلها باستخدام OpenTelemetry لهذه اللغة.

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

تنظيف

بعد الانتهاء من هذا الدرس التطبيقي حول الترميز، يُرجى إيقاف مجموعة Kubernetes وحذف المشروع لكي لا يتم تحصيل رسوم غير متوقّعة منك في Google Kubernetes Engine وGoogle Cloud Trace وGoogle Artifact Registry.

أولاً، احذف المجموعة. إذا كنت تشغّل المجموعة باستخدام skaffold dev، ما عليك سوى الضغط على Ctrl-C. إذا كنت تشغّل المجموعة باستخدام skaffold run، نفِّذ الأمر التالي:

skaffold delete

ناتج الأمر

Cleaning up...
 - deployment.apps "clientservice" deleted
 - service "clientservice" deleted
 - deployment.apps "loadgen" deleted
 - deployment.apps "serverservice" deleted
 - service "serverservice" deleted

بعد حذف المجموعة، اختَر "إدارة الهوية وإمكانية الوصول (IAM) والمشرف" > "الإعدادات" من لوحة القائمة، ثمّ انقر على الزر "إيقاف".

45aa37b7d5e1ddd1.png

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