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

1. مقدمة

505827108874614d.png

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

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

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

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

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

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

هذا الدرس التطبيقي حول الترميز هو الجزء الأول من السلسلة ويتناول تتبُّع العمليات الموزّعة في الخدمات المصغّرة باستخدام OpenTelemetry وCloud Trace. سيتناول الجزء 2 عملية التحليل المستمر باستخدام Cloud Profiler.

التتبُّع الموزّع

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

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

يمثّل النطاق وحدة فردية من العمل يتم إجراؤها في نظام موزّع، ويتم تسجيل وقتَي البدء والانتهاء. غالبًا ما تكون هناك علاقات هرمية بين الفترات الزمنية. في الصورة أدناه، جميع الفترات الزمنية الأصغر هي فترات زمنية فرعية لفترة زمنية كبيرة /messages، ويتم تجميعها في عملية تتبُّع واحدة تعرض مسار العمل خلال النظام.

تتبُّع

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

ما ستنشئه

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

44e243182ced442f.png

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

ستسجّل معلومات التتبُّع في الطلب. بعد ذلك، عليك تضمين وكيل محلّل الأداء في الخادم والتحقيق في المشكلة.

ما ستتعلمه

  • كيفية بدء استخدام مكتبات OpenTelemetry Trace في مشروع Go
  • كيفية إنشاء فترة باستخدام المكتبة
  • كيفية نشر سياقات الامتداد عبر الشبكة بين مكوّنات التطبيق
  • كيفية إرسال بيانات التتبُّع إلى Cloud Trace
  • كيفية تحليل التتبُّع على Cloud Trace

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

المتطلبات

  • معرفة أساسية بلغة 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 حيث يتم تشغيل الخدمات المصغّرة للدرس التطبيقي حول الترميز

التالي

في الخطوة التالية، ستنشئ خدماتك المصغّرة وتدفعها وتنشرها على المجموعة.

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

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

تنفيذ أمر skaffold

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

cd step0
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. قياس حالة HTTP

مفهوم تتبُّع الأجهزة ونقل البيانات

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

6be42e353b9bfd1d.png

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

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

تساعدك OpenTelemetry في ما يلي:

  • لإنشاء معرّف تتبُّع ومعرّف مدى فريدَين
  • لتصدير معرّف التتبُّع ومعرّف الامتداد إلى الخلفية
  • لنشر سياقات التتبُّع إلى خدمات أخرى
  • لتضمين بيانات تعريف إضافية تساعد في تحليل عمليات التتبُّع

المكوّنات في OpenTelemetry Trace

b01f7bb90188db0d.png

تكون عملية تتبُّع التطبيق باستخدام OpenTelemetry على النحو التالي:

  1. إنشاء مصدِّر
  2. أنشئ TracerProvider لربط أداة التصدير في 1 واضبطها على مستوى العالم.
  3. ضبط TextMapPropagaror لضبط طريقة الانتشار
  4. الحصول على Tracer من TracerProvider
  5. إنشاء Span من Tracer

في الوقت الحالي، لا تحتاج إلى فهم الخصائص التفصيلية في كل مكوّن، ولكن أهم ما يجب تذكّره هو:

  • يمكن ربط أداة التصدير هنا بـ TracerProvider
  • يحتوي TracerProvider على جميع الإعدادات المتعلّقة بأخذ عيّنات من عمليات التتبُّع وتصديرها.
  • يتم تجميع جميع عمليات التتبُّع في عنصر Tracer

بعد فهم ذلك، لننتقل إلى عمل الترميز الفعلي.

Instrument first span

خدمة إنشاء عمليات تحميل الأدوات

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

step0/src/loadgen/main.go

func main() {
        ...
        for range t.C {
                log.Printf("simulating client requests, round %d", i)
                if err := run(numWorkers, numConcurrency); err != nil {
                        log.Printf("aborted round with error: %v", err)
                }
                log.Printf("simulated %d requests", numWorkers)
                if numRounds != 0 && i > numRounds {
                        break
                }
                i++
        }
}

في الدالة الرئيسية، يمكنك الاطّلاع على الحلقة التي تستدعي الدالة run. في التنفيذ الحالي، يحتوي القسم على سطرَي سجلّ يسجّلان بداية استدعاء الدالة ونهايته. لنبدأ الآن بتسجيل معلومات Span لتتبُّع وقت استجابة استدعاء الدالة.

أولاً، كما هو موضّح في القسم السابق، لنبدأ بإعداد جميع إعدادات OpenTelemetry. أضِف حِزم OpenTelemetry على النحو التالي:

step0/src/loadgen/main.go

import (
        "context" // step1. add packages
        "encoding/json"
        "fmt"
        "io"
        "log"
        "math/rand"
        "net/http"
        "net/url"
        "time"
        // step1. add packages
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/attribute"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
        "go.opentelemetry.io/otel/trace"
        // step1. end add packages
)

لتحسين إمكانية القراءة، ننشئ دالة إعداد باسم initTracer ونستدعيها في الدالة main.

step0/src/loadgen/main.go

// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
        // create a stdout exporter to show collected spans out to stdout.
        exporter, err := stdout.New(stdout.WithPrettyPrint())
        if err != nil {
                return nil, err
        }

        // for the demonstration, we use AlwaysSmaple sampler to take all spans.
        // do not use this option in production.
        tp := sdktrace.NewTracerProvider(
                sdktrace.WithSampler(sdktrace.AlwaysSample()),
                sdktrace.WithBatcher(exporter),
        )
        otel.SetTracerProvider(tp)
        otel.SetTextMapPropagator(propagation.TraceContext{})
        return tp, nil
}

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

بعد ذلك، يمكنك استدعاؤها من الدالة الرئيسية. استدعِ الدالة initTracer() وتأكَّد من استدعاء الدالة TracerProvider.Shutdown() عند إغلاق التطبيق.

step0/src/loadgen/main.go

func main() {
        // step1. 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)
                }
        }()
        // step1. end setup

        log.Printf("starting worder with %d workers in %d concurrency", numWorkers, numConcurrency)
        log.Printf("number of rounds: %d (0 is inifinite)", numRounds)
        ...

بعد الانتهاء من عملية الإعداد، عليك إنشاء Span باستخدام Trace ID وSpan ID فريدَين. توفّر OpenTelemetry مكتبة مفيدة لذلك. أضِف حِزمًا جديدة إضافية إلى برنامج HTTP للعميل.

step0/src/loadgen/main.go

import (
        "context"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "math/rand"
        "net/http"
        "net/http/httptrace" // step1. add packages
        "net/url"
        "time"
        // step1. add packages
        "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
        "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
        // step1. end add packages
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/attribute"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
        "go.opentelemetry.io/otel/trace"
)

بما أنّ أداة إنشاء التحميل تستدعي خدمة العميل في HTTP باستخدام net/http في الدالة runQuery، فإنّنا نستخدم حزمة contrib لـ net/http ونفعّل قياس حالة التطبيق باستخدام إضافة حزمة httptrace وotelhttp.

أولاً، أضِف متغيّرًا عامًا للحزمة httpClient لاستدعاء طلبات HTTP من خلال العميل الذي تمّ تزويده بأدوات القياس.

step0/src/loadgen/main.go

var httpClient = http.Client{
        Transport: otelhttp.NewTransport(http.DefaultTransport)
}

بعد ذلك، أضِف قياس حالة التطبيق في الدالة runQuery لإنشاء النطاق المخصّص باستخدام OpenTelemetry والنطاق الذي تم إنشاؤه تلقائيًا من عميل HTTP المخصّص. عليك اتّخاذ الإجراءات التالية:

  1. الحصول على جهاز تتبُّع من TracerProvider العالمي مقابل otel.Tracer()
  2. إنشاء نطاق رئيسي باستخدام الطريقة Tracer.Start()
  3. إنهاء النطاق الجذر في توقيت عشوائي (في هذه الحالة، نهاية الدالة runQuery)

step0/src/loadgen/main.go

        reqURL.RawQuery = v.Encode()
        // step1. replace http.Get() with custom client call
        // resp, err := http.Get(reqURL.String())

        // step1. instrument trace
        ctx := context.Background()
        tr := otel.Tracer("loadgen")
        ctx, span := tr.Start(ctx, "query.request", trace.WithAttributes(
                semconv.TelemetrySDKLanguageGo,
                semconv.ServiceNameKey.String("loadgen.runQuery"),
                attribute.Key("query").String(s),
        ))
        defer span.End()
        ctx = httptrace.WithClientTrace(ctx, otelhttptrace.NewClientTrace(ctx))
        req, err := http.NewRequestWithContext(ctx, "GET", reqURL.String(), nil)
        if err != nil {
                return -1, fmt.Errorf("error creating HTTP request object: %v", err)
        }
        resp, err := httpClient.Do(req)
        // step1. end instrumentation
        if err != nil {
                return -1, fmt.Errorf("error sending request to %v: %v", reqURL.String(), err)
        }

لقد انتهيت الآن من عملية قياس حالة التطبيق في loadgen (تطبيق عميل HTTP). يُرجى الحرص على تعديل go.mod وgo.sum باستخدام الأمر go mod.

go mod tidy

خدمة عملاء أداة القياس

في القسم السابق، أضفنا أدوات إلى الجزء المضمّن في المستطيل الأحمر في الرسم أدناه. لقد أضفنا معلومات حول النطاق في خدمة إنشاء الحمل. على غرار خدمة إنشاء الحمل، علينا الآن إعداد خدمة العميل. يختلف هذا عن خدمة إنشاء التحميل في أنّ خدمة العميل تحتاج إلى استخراج معلومات رقم تعريف التتبُّع التي تم نشرها من خدمة إنشاء التحميل في عنوان HTTP واستخدام رقم التعريف لإنشاء "فترات".

bcaccd06691269f8.png

افتح Cloud Shell Editor وأضِف الحِزم المطلوبة كما فعلنا مع خدمة إنشاء الحمل.

step0/src/client/main.go

import (
        "context"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "net/http"
        "net/url"
        "os"
        "time"

        "opentelemetry-trace-codelab-go/client/shakesapp"
        // step1. add new import
        "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/attribute"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        "go.opentelemetry.io/otel/trace"
        "google.golang.org/grpc"
        "google.golang.org/grpc/credentials/insecure"
        // step1. end new import
)

مرة أخرى، علينا إعداد OpenTelemtry. ما عليك سوى نسخ دالة initTracer من loadgen ولصقها واستدعائها في دالة main الخاصة بخدمة العميل أيضًا.

step0/src/client/main.go

// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
        // create a stdout exporter to show collected spans out to stdout.
        exporter, err := stdout.New(stdout.WithPrettyPrint())
        if err != nil {
                return nil, err
        }

        // for the demonstration, we use AlwaysSmaple sampler to take all spans.
        // do not use this option in production.
        tp := sdktrace.NewTracerProvider(
                sdktrace.WithSampler(sdktrace.AlwaysSample()),
                sdktrace.WithBatcher(exporter),
        )
        otel.SetTracerProvider(tp)
        otel.SetTextMapPropagator(propagation.TraceContext{})
        return tp, nil
}

حان الوقت الآن لتسجيل بيانات النطاقات. بما أنّ خدمة العميل تحتاج إلى قبول طلبات HTTP من خدمة loadgen، يجب أن يتم تجهيز المعالج. يتم تنفيذ خادم HTTP في خدمة العميل باستخدام net/http، ويمكنك استخدام حزمة otelhttp مثلما فعلنا في loadgen.

أولاً، نستبدل تسجيل المعالج بـ otelhttp Handler. في الدالة main، ابحث عن الأسطر التي تم فيها تسجيل معالج HTTP باستخدام http.HandleFunc().

step0/src/client/main.go

        // step1. change handler to intercept OpenTelemetry related headers
        // http.HandleFunc("/", svc.handler)
        otelHandler := otelhttp.NewHandler(http.HandlerFunc(svc.handler), "client.handler")
        http.Handle("/", otelHandler)
        // step1. end intercepter setting
        http.HandleFunc("/_genki", svc.health)

بعد ذلك، نضبط المدى الفعلي داخل المعالج. ابحث عن func (*clientService) handler()، وأضِف أداة قياس حالة التطبيق باستخدام trace.SpanFromContext().

step0/src/client/main.go

func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) {
        ...
        ctx := r.Context()
        ctx, cancel := context.WithCancel(ctx)
        defer cancel()
        // step1. instrument trace
        span := trace.SpanFromContext(ctx)
        defer span.End()
        // step1. end instrument
        ...

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

step0/src/client/main.go

func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) {
        ...
        // step1. add span specific attribute
        span.SetAttributes(attribute.Key("matched").Int64(resp.MatchCount))
        // step1. end adding attribute
        log.Println(string(ret))
        ...

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

skaffold dev

بعد مرور بعض الوقت على تشغيل الخدمات على مجموعة GKE، ستظهر لك كمية كبيرة من رسائل السجلّ على النحو التالي:

ناتج الأمر

[loadgen] {
[loadgen]       "Name": "query.request",
[loadgen]       "SpanContext": {
[loadgen]               "TraceID": "cfa22247a542beeb55a3434392d46b89",
[loadgen]               "SpanID": "18b06404b10c418b",
[loadgen]               "TraceFlags": "01",
[loadgen]               "TraceState": "",
[loadgen]               "Remote": false
[loadgen]       },
[loadgen]       "Parent": {
[loadgen]               "TraceID": "00000000000000000000000000000000",
[loadgen]               "SpanID": "0000000000000000",
[loadgen]               "TraceFlags": "00",
[loadgen]               "TraceState": "",
[loadgen]               "Remote": false
[loadgen]       },
[loadgen]       "SpanKind": 1,
[loadgen]       "StartTime": "2022-07-14T13:13:36.686751087Z",
[loadgen]       "EndTime": "2022-07-14T13:14:31.849601964Z",
[loadgen]       "Attributes": [
[loadgen]               {
[loadgen]                       "Key": "telemetry.sdk.language",
[loadgen]                       "Value": {
[loadgen]                               "Type": "STRING",
[loadgen]                               "Value": "go"
[loadgen]                       }
[loadgen]               },
[loadgen]               {
[loadgen]                       "Key": "service.name",
[loadgen]                       "Value": {
[loadgen]                               "Type": "STRING",
[loadgen]                               "Value": "loadgen.runQuery"
[loadgen]                       }
[loadgen]               },
[loadgen]               {
[loadgen]                       "Key": "query",
[loadgen]                       "Value": {
[loadgen]                               "Type": "STRING",
[loadgen]                               "Value": "faith"
[loadgen]                       }
[loadgen]               }
[loadgen]       ],
[loadgen]       "Events": null,
[loadgen]       "Links": null,
[loadgen]       "Status": {
[loadgen]               "Code": "Unset",
[loadgen]               "Description": ""
[loadgen]       },
[loadgen]       "DroppedAttributes": 0,
[loadgen]       "DroppedEvents": 0,
[loadgen]       "DroppedLinks": 0,
[loadgen]       "ChildSpanCount": 5,
[loadgen]       "Resource": [
[loadgen]               {
[loadgen]                       "Key": "service.name",
[loadgen]                       "Value": {
[loadgen]                               "Type": "STRING",
[loadgen]                               "Value": "unknown_service:loadgen"
...

يُصدر برنامج التصدير stdout هذه الرسائل. ستلاحظ أنّ العناصر الرئيسية لجميع الفترات الزمنية التي تم إنشاؤها بواسطة loadgen تحتوي على TraceID: 00000000000000000000000000000000، لأنّ هذه الفترة الزمنية هي الفترة الزمنية الجذرية، أي الفترة الزمنية الأولى في التتبُّع. ستجد أيضًا أنّ السمة "تضمين" "query" تتضمّن سلسلة طلب البحث التي يتم تمريرها إلى خدمة العميل.

ملخّص

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

التالي

في الخطوة التالية، ستعمل على إعداد خدمة العميل وخدمة الخادم للتأكّد من كيفية نشر "سياق التتبُّع" من خلال gRPC.

5- قياس حالة التطبيق لـ gRPC

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

75310d8e0e3b1a30.png

أدوات القياس المسبقة الإنشاء لبرنامج gRPC

توفّر المنظومة المتكاملة لـ OpenTelemetry الكثير من المكتبات المفيدة التي تساعد المطوّرين في قياس أداء التطبيقات. في الخطوة السابقة، استخدمنا أداة قياس حالة التطبيق مسبقة الإنشاء لحزمة net/http. في هذه الخطوة، وبما أنّنا نحاول نشر Trace Context من خلال gRPC، سنستخدم المكتبة لذلك.

أولاً، عليك استيراد حزمة gRPC المُنشأة مسبقًا والتي تُسمى otelgrpc.

step0/src/client/main.go

import (
        "context"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "net/http"
        "net/url"
        "os"
        "time"

        "opentelemetry-trace-codelab-go/client/shakesapp"
        // step2. add prebuilt gRPC package (otelgrpc) 
        "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
        "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/attribute"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        "go.opentelemetry.io/otel/trace"
        "google.golang.org/grpc"
        "google.golang.org/grpc/credentials/insecure"
)

في هذه المرة، خدمة العميل هي عميل gRPC مقابل خدمة الخادم، لذا عليك إعداد عميل gRPC. ابحث عن الدالة mustConnGRPC وأضِف أدوات اعتراض gRPC التي تسجّل نطاقات جديدة في كل مرة يرسل فيها العميل طلبات إلى الخادم.

step0/src/client/main.go

// Helper function for gRPC connections: Dial and create client once, reuse.
func mustConnGRPC(ctx context.Context, conn **grpc.ClientConn, addr string) {
        var err error
        // step2. add gRPC interceptor
        interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider())
        *conn, err = grpc.DialContext(ctx, addr,
                grpc.WithTransportCredentials(insecure.NewCredentials()),
                grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor(interceptorOpt)),
                grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor(interceptorOpt)),
                grpc.WithTimeout(time.Second*3),
        )
        // step2: end adding interceptor
        if err != nil {
                panic(fmt.Sprintf("Error %s grpc: failed to connect %s", err, addr))
        }
}

بما أنّك سبق وأعددت OpenTelemetry في القسم السابق، ليس عليك إجراء ذلك.

أدوات مُعدّة مسبقًا لقياس حالة تطبيق خادم gRPC

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

step0/src/server/main.go

import (
        "context"
        "fmt"
        "io/ioutil"
        "log"
        "net"
        "os"
        "regexp"
        "strings"

        "opentelemetry-trace-codelab-go/server/shakesapp"

        "cloud.google.com/go/storage"
        // step2. add OpenTelemetry packages including otelgrpc
        "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
        "go.opentelemetry.io/otel"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        "google.golang.org/api/iterator"
        "google.golang.org/api/option"
        "google.golang.org/grpc"
        healthpb "google.golang.org/grpc/health/grpc_health_v1"
)

بما أنّها المرة الأولى التي يتم فيها إعداد الخادم، عليك إعداد OpenTelemetry أولاً، على غرار ما فعلناه مع خدمات loadgen والعميل.

step0/src/server/main.go

// step2. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
        // create a stdout exporter to show collected spans out to stdout.
        exporter, err := stdout.New(stdout.WithPrettyPrint())
        if err != nil {
                return nil, err
        }
        // for the demonstration, we use AlwaysSmaple sampler to take all spans.
        // do not use this option in production.
        tp := sdktrace.NewTracerProvider(
                sdktrace.WithSampler(sdktrace.AlwaysSample()),
                sdktrace.WithBatcher(exporter),
        )
        otel.SetTracerProvider(tp)
        otel.SetTextMapPropagator(propagation.TraceContext{})
        return tp, nil
}

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
        ...

بعد ذلك، عليك إضافة أدوات اعتراض الخادم. في الدالة main، ابحث عن المكان الذي يتم فيه استدعاء grpc.NewServer() وأضِف أدوات اعتراض إلى الدالة.

step0/src/server/main.go

func main() {
        ...
        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)
        ...

تشغيل الخدمة المصغّرة وتأكيد التتبُّع

بعد ذلك، شغِّل الرمز المعدَّل باستخدام أمر skaffold.

skaffold dev

مرة أخرى، ستظهر لك مجموعة من معلومات النطاق على stdout.

ناتج الأمر

...
[server] {
[server]        "Name": "shakesapp.ShakespeareService/GetMatchCount",
[server]        "SpanContext": {
[server]                "TraceID": "89b472f213a400cf975e0a0041649667",
[server]                "SpanID": "96030dbad0061b3f",
[server]                "TraceFlags": "01",
[server]                "TraceState": "",
[server]                "Remote": false
[server]        },
[server]        "Parent": {
[server]                "TraceID": "89b472f213a400cf975e0a0041649667",
[server]                "SpanID": "cd90cc3859b73890",
[server]                "TraceFlags": "01",
[server]                "TraceState": "",
[server]                "Remote": true
[server]        },
[server]        "SpanKind": 2,
[server]        "StartTime": "2022-07-14T14:05:55.74822525Z",
[server]        "EndTime": "2022-07-14T14:06:03.449258891Z",
[server]        "Attributes": [
...
[server]        ],
[server]        "Events": [
[server]                {
[server]                        "Name": "message",
[server]                        "Attributes": [
...
[server]                        ],
[server]                        "DroppedAttributeCount": 0,
[server]                        "Time": "2022-07-14T14:05:55.748235489Z"
[server]                },
[server]                {
[server]                        "Name": "message",
[server]                        "Attributes": [
...
[server]                        ],
[server]                        "DroppedAttributeCount": 0,
[server]                        "Time": "2022-07-14T14:06:03.449255889Z"
[server]                }
[server]        ],
[server]        "Links": null,
[server]        "Status": {
[server]                "Code": "Unset",
[server]                "Description": ""
[server]        },
[server]        "DroppedAttributes": 0,
[server]        "DroppedEvents": 0,
[server]        "DroppedLinks": 0,
[server]        "ChildSpanCount": 0,
[server]        "Resource": [
[server]                {
...
[server]        ],
[server]        "InstrumentationLibrary": {
[server]                "Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
[server]                "Version": "semver:0.33.0",
[server]                "SchemaURL": ""
[server]        }
[server] }
...

تلاحظ أنّه لم يتم تضمين أي أسماء فترات، وأنّه تم إنشاء فترات يدويًا باستخدام trace.Start() أو span.SpanFromContext(). ومع ذلك، ستحصل على عدد كبير من الفترات الزمنية لأنّ أدوات اعتراض gRPC أنشأتها.

ملخّص

في هذه الخطوة، أضفت أدوات إلى عملية التواصل المستندة إلى gRPC باستخدام مكتبات منظومة OpenTelemetry المتكاملة.

التالي

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

6. عرض عمليات التتبُّع باستخدام Cloud Trace

لقد تتبّعت العمليات في النظام بأكمله باستخدام OpenTelemetry. لقد تعلّمت حتى الآن كيفية إعداد خدمات HTTP وgRPC. على الرغم من أنّك تعلّمت كيفية إعدادها، لم تتعلّم بعد كيفية تحليلها. في هذا القسم، ستستبدل أدوات تصدير stdout بأدوات تصدير Cloud Trace، وستتعرّف على كيفية تحليل عمليات التتبُّع.

استخدام أداة التصدير في Cloud Trace

إحدى الخصائص الفعّالة في OpenTelemetry هي إمكانية توصيلها. لتصوّر جميع الفترات التي تم جمعها من خلال أدوات القياس، ما عليك سوى استبدال أداة التصدير stdout بأداة التصدير Cloud Trace.

افتح ملفات main.go لكل خدمة، وابحث عن الدالة initTracer(). احذف السطر لإنشاء أداة تصدير stdout وأنشئ أداة تصدير Cloud Trace بدلاً من ذلك.

step0/src/loadgen/main.go

import (
        ...
        // step3. add OpenTelemetry for Cloud Trace package
        cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
)

// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
        // step3. replace stdout exporter with Cloud Trace exporter
        // cloudtrace.New() finds the credentials to Cloud Trace automatically following the
        // rules defined by golang.org/x/oauth2/google.findDefaultCredentailsWithParams.
        // https://pkg.go.dev/golang.org/x/oauth2/google#FindDefaultCredentialsWithParams
        exporter, err := cloudtrace.New()
        // step3. end replacing exporter
        if err != nil {
                return nil, err
        }

        // for the demonstration, we use AlwaysSmaple sampler to take all spans.
        // do not use this option in production.
        tp := sdktrace.NewTracerProvider(
                sdktrace.WithSampler(sdktrace.AlwaysSample()),
                sdktrace.WithBatcher(exporter),
        )
        otel.SetTracerProvider(tp)
        otel.SetTextMapPropagator(propagation.TraceContext{})
        return tp, nil
}

عليك تعديل الدالة نفسها في خدمة العميل والخادم أيضًا.

تشغيل الخدمة المصغّرة وتأكيد التتبُّع

بعد التعديل، ما عليك سوى تشغيل المجموعة كالمعتاد باستخدام أمر skaffold.

skaffold dev

بعد ذلك، لن ترى الكثير من معلومات النطاق بتنسيق السجلات المنظَّمة على stdout، لأنّك استبدلت أداة التصدير بأداة Cloud Trace.

ناتج الأمر

[loadgen] 2022/07/14 15:01:07 simulated 20 requests
[loadgen] 2022/07/14 15:01:07 simulating client requests, round 37
[loadgen] 2022/07/14 15:01:14 query 'sweet': matched 958
[client] 2022/07/14 15:01:14 {"match_count":958}
[client] 2022/07/14 15:01:14 {"match_count":3040}
[loadgen] 2022/07/14 15:01:14 query 'love': matched 3040
[client] 2022/07/14 15:01:15 {"match_count":349}
[loadgen] 2022/07/14 15:01:15 query 'hello': matched 349
[client] 2022/07/14 15:01:15 {"match_count":484}
[loadgen] 2022/07/14 15:01:15 query 'faith': matched 484
[loadgen] 2022/07/14 15:01:15 query 'insolence': matched 14
[client] 2022/07/14 15:01:15 {"match_count":14}
[client] 2022/07/14 15:01:21 {"match_count":484}
[loadgen] 2022/07/14 15:01:21 query 'faith': matched 484
[client] 2022/07/14 15:01:21 {"match_count":728}
[loadgen] 2022/07/14 15:01:21 query 'world': matched 728
[client] 2022/07/14 15:01:22 {"match_count":484}
[loadgen] 2022/07/14 15:01:22 query 'faith': matched 484
[loadgen] 2022/07/14 15:01:22 query 'hello': matched 349
[client] 2022/07/14 15:01:22 {"match_count":349}
[client] 2022/07/14 15:01:23 {"match_count":1036}
[loadgen] 2022/07/14 15:01:23 query 'friend': matched 1036
[loadgen] 2022/07/14 15:01:28 query 'tear': matched 463
...

لنؤكّد الآن ما إذا تم إرسال جميع الفترات بشكل صحيح إلى Cloud Trace. الوصول إلى Cloud Console والانتقال إلى "قائمة عمليات التتبُّع" ويمكن الوصول إليها بسهولة من مربّع البحث. بخلاف ذلك، يمكنك النقر على القائمة في اللوحة اليمنى. 8b3f8411bd737e06.png

بعد ذلك، تلاحظ أنّ الكثير من النقاط الزرقاء موزّعة على الرسم البياني لوقت الاستجابة. تمثّل كل نقطة أثرًا واحدًا.

3ecf131423fc4c40.png

انقر على أحدها وستظهر لك التفاصيل داخل التتبُّع. 4fd10960c6648a03.png

حتى من هذه النظرة السريعة البسيطة، يمكنك الحصول على الكثير من الإحصاءات. على سبيل المثال، من الرسم البياني الشلالي، يمكنك أن ترى أنّ سبب وقت الاستجابة هو في الغالب الامتداد المسمّى shakesapp.ShakespeareService/GetMatchCount. (راجِع الرقم 1 في الصورة أعلاه) يمكنك التأكّد من ذلك من جدول الملخّص. (يعرض العمود الأيسر مدة كل فترة.) بالإضافة إلى ذلك، كان هذا التتبُّع مخصّصًا لطلب البحث "صديق". (يُرجى الاطّلاع على الرقم 2 في الصورة أعلاه)

بالنظر إلى هذه التحليلات الموجزة، قد تدرك أنّك بحاجة إلى معرفة المزيد من النطاقات التفصيلية داخل طريقة GetMatchCount. مقارنةً بمعلومات stdout، يكون التصور قويًا. لمزيد من المعلومات حول تفاصيل Cloud Trace، يُرجى الانتقال إلى مستنداتنا الرسمية.

ملخّص

في هذه الخطوة، استبدلت أداة تصدير stdout بأداة Cloud Trace وعرضت عمليات التتبُّع على Cloud Trace. تعرّفت أيضًا على كيفية بدء تحليل عمليات التتبُّع.

التالي

في الخطوة التالية، ستعدّل رمز المصدر لخدمة الخادم من أجل إضافة نطاق فرعي في GetMatchCount.

7. إضافة نطاق فرعي لتحليل أفضل

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

3b63a1e471dddb8c.png

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

تسجيل جزء فرعي في الخادم

افتح main.go في الخادم وابحث عن الدالة readFiles. تطلب هذه الدالة من Google Cloud Storage استرداد جميع الملفات النصية الخاصة بأعمال شكسبير. في هذه الدالة، يمكنك إنشاء نطاق فرعي، مثل ما فعلته لقياس حالة تطبيق خادم HTTP في خدمة العميل.

step0/src/server/main.go

func readFiles(ctx context.Context, bucketName, prefix string) ([]string, error) {
        type resp struct {
                s   string
                err error
        }

        // step4: add an extra span
        span := trace.SpanFromContext(ctx)
        span.SetName("server.readFiles")
        span.SetAttributes(attribute.Key("bucketname").String(bucketName))
        defer span.End()
        // step4: end add span
        ...

هذا كل ما يتعلق بإضافة نطاق جديد. لنرى كيف يعمل ذلك من خلال تشغيل التطبيق.

تشغيل الخدمة المصغّرة وتأكيد التتبُّع

بعد التعديل، ما عليك سوى تشغيل المجموعة كالمعتاد باستخدام أمر skaffold.

skaffold dev

واختَر عملية تتبُّع واحدة باسم query.request من قائمة عمليات التتبُّع. سيظهر لك رسم بياني مشابه لمخطط الشلال للتتبُّع باستثناء نطاق جديد ضمن shakesapp.ShakespeareService/GetMatchCount. (النطاق المحاط بالمستطيل الأحمر أدناه)

3d4a891aa30d7a32.png

يمكنك الآن أن تستنتج من هذا الرسم البياني أنّ طلب Google Cloud Storage الخارجي يستغرق وقت استجابة كبيرًا، ولكن لا تزال هناك عوامل أخرى تتسبّب في معظم وقت الاستجابة.

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

ملخّص

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

8. تهانينا

لقد أنشأت عمليات تتبُّع موزّعة بنجاح باستخدام OpenTelemetry وأكّدت وقت استجابة الطلبات في جميع الخدمات المصغّرة على 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

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