عملکرد تولید را با Cloud Profiler تجزیه و تحلیل کنید

۱. مرور کلی

در حالی که توسعه‌دهندگان برنامه‌های کلاینت و وب frontend معمولاً از ابزارهایی مانند Android Studio CPU Profiler یا ابزارهای پروفایلینگ موجود در Chrome برای بهبود عملکرد کد خود استفاده می‌کنند، تکنیک‌های معادل آن به اندازه کافی در دسترس نبوده یا توسط کسانی که روی سرویس‌های backend کار می‌کنند، به خوبی پذیرفته نشده‌اند. Cloud Profiler همین قابلیت‌ها را برای توسعه‌دهندگان سرویس، صرف نظر از اینکه کد آنها روی پلتفرم Google Cloud یا جای دیگری اجرا می‌شود، به ارمغان می‌آورد.

95c034c70c9cac22.png

این ابزار اطلاعات مربوط به میزان استفاده از CPU و تخصیص حافظه را از برنامه‌های کاربردی شما جمع‌آوری می‌کند. این اطلاعات را به کد منبع برنامه نسبت می‌دهد و به شما کمک می‌کند تا بخش‌هایی از برنامه که بیشترین منابع را مصرف می‌کنند شناسایی کنید و در غیر این صورت ویژگی‌های عملکرد کد را روشن می‌کند. سربار کم تکنیک‌های جمع‌آوری به کار رفته در این ابزار، آن را برای استفاده مداوم در محیط‌های عملیاتی مناسب می‌سازد.

در این آزمایشگاه کد، شما یاد خواهید گرفت که چگونه Cloud Profiler را برای یک برنامه Go راه‌اندازی کنید و با نوع بینش‌هایی که این ابزار می‌تواند در مورد عملکرد برنامه ارائه دهد، آشنا خواهید شد.

آنچه یاد خواهید گرفت

  • نحوه پیکربندی یک برنامه Go برای پروفایل‌سازی با Cloud Profiler.
  • نحوه جمع‌آوری، مشاهده و تحلیل داده‌های عملکرد با Cloud Profiler.

آنچه نیاز دارید

  • یک پروژه پلتفرم ابری گوگل
  • یک مرورگر، مانند کروم یا فایرفاکس
  • آشنایی با ویرایشگرهای متن استاندارد لینوکس مانند Vim، EMACs یا Nano

چگونه از این آموزش استفاده خواهید کرد؟

فقط تا انتها بخوانید آن را بخوانید و تمرین‌ها را انجام دهید

تجربه خود را با پلتفرم ابری گوگل چگونه ارزیابی می‌کنید؟

تازه کار متوسط ماهر

۲. تنظیمات و الزامات

تنظیم محیط خودتنظیم

  1. وارد Cloud Console شوید و یک پروژه جدید ایجاد کنید یا از یک پروژه موجود دوباره استفاده کنید. اگر از قبل حساب Gmail یا Google Workspace ندارید، باید یکی ایجاد کنید .

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

شناسه پروژه را به خاطر بسپارید، یک نام منحصر به فرد در تمام پروژه‌های Google Cloud (نام بالا قبلاً گرفته شده و برای شما کار نخواهد کرد، متاسفیم!). بعداً در این آزمایشگاه کد به آن PROJECT_ID گفته خواهد شد.

  1. در مرحله بعد، برای استفاده از منابع گوگل کلود، باید پرداخت را در Cloud Console فعال کنید .

اجرای این آزمایشگاه کد، اگر اصلاً هزینه‌ای نداشته باشد، نباید هزینه زیادی داشته باشد. حتماً دستورالعمل‌های بخش «پاکسازی» را که به شما نحوه خاموش کردن منابع را آموزش می‌دهد، دنبال کنید تا پس از این آموزش، متحمل هزینه نشوید. کاربران جدید Google Cloud واجد شرایط برنامه آزمایشی رایگان ۳۰۰ دلاری هستند.

پوسته ابری گوگل

اگرچه می‌توان گوگل کلود را از راه دور و از طریق لپ‌تاپ شما مدیریت کرد، اما برای ساده‌تر کردن تنظیمات در این آزمایشگاه کد، از گوگل کلود شل ، یک محیط خط فرمان که در فضای ابری اجرا می‌شود، استفاده خواهیم کرد.

فعال کردن پوسته ابری

  1. از کنسول ابری، روی فعال کردن پوسته ابری کلیک کنید 4292cbf4971c9786.png .

bce75f34b2c53987.png

اگر قبلاً Cloud Shell را شروع نکرده‌اید، یک صفحه میانی (در پایین صفحه) به شما نمایش داده می‌شود که توضیح می‌دهد چیست. در این صورت، روی ادامه کلیک کنید (و دیگر هرگز آن را نخواهید دید). آن صفحه یکبار مصرف به این شکل است:

70f315d7b402b476.png

آماده‌سازی و اتصال به Cloud Shell فقط چند لحظه طول می‌کشد.

fbe3a0674c982259.png

این ماشین مجازی با تمام ابزارهای توسعه مورد نیاز شما پر شده است. این ماشین یک دایرکتوری خانگی ۵ گیگابایتی دائمی ارائه می‌دهد و در فضای ابری گوگل اجرا می‌شود که عملکرد شبکه و احراز هویت را تا حد زیادی بهبود می‌بخشد. بخش عمده‌ای از کار شما در این آزمایشگاه کد، اگر نگوییم همه، را می‌توان به سادگی با یک مرورگر یا کروم‌بوک انجام داد.

پس از اتصال به 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].

۳. به Cloud Profiler بروید

در کنسول ابری، با کلیک روی «نماینده» در نوار ناوبری سمت چپ، به رابط کاربری نمایه‌ساز بروید:

37ad0df7ddb2ad17.png

همچنین می‌توانید از نوار جستجوی کنسول ابری برای رفتن به رابط کاربری پروفایلر استفاده کنید: فقط "Cloud Profiler" را تایپ کنید و مورد یافت شده را انتخاب کنید. در هر صورت، باید رابط کاربری پروفایلر را با پیام "داده‌ای برای نمایش وجود ندارد" مانند زیر مشاهده کنید. این پروژه جدید است، بنابراین هنوز هیچ داده پروفایلی جمع‌آوری نشده است.

d275a5f61ed31fb2.png

حالا وقتشه که یه چیزی رو پروفایل کنیم!

۴. معیار را شرح دهید

ما از یک برنامه ساده 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" است که یک برنامه مصنوعی است که عامل پروفایلینگ در آن فعال است:

اصلی.برو

...
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، heap و thread را جمع‌آوری می‌کند. کد اینجا امکان جمع‌آوری پروفایل‌های mutex (که به عنوان "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 و تایپ کردن "exit" از آن خارج شوید.

۵. داده‌های پروفایلر را تجزیه و تحلیل کنید

حالا که مقداری داده جمع‌آوری کرده‌ایم، بیایید دقیق‌تر به آن نگاه کنیم. ما از یک برنامه‌ی مصنوعی (منبع آن در گیت‌هاب موجود است) استفاده می‌کنیم که رفتارهای معمول انواع مختلف مشکلات عملکردی را در محیط عملیاتی شبیه‌سازی می‌کند.

کدی که CPU را به شدت درگیر می‌کند

نوع پروفایل CPU را انتخاب کنید. پس از بارگذاری رابط کاربری، در نمودار شعله‌ای، چهار بلوک برگ برای تابع load را مشاهده خواهید کرد که در مجموع تمام مصرف CPU را به خود اختصاص می‌دهند:

fae661c9fe6c58df.png

این تابع به طور خاص برای مصرف تعداد زیادی از چرخه‌های CPU با اجرای یک حلقه‌ی فشرده نوشته شده است:

اصلی.برو

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

این تابع به طور غیرمستقیم از busyloop () از طریق چهار مسیر فراخوانی فراخوانی می‌شود: busyloop → { foo1 , foo2 } → { bar , baz } → load . عرض یک کادر تابع، هزینه نسبی مسیر فراخوانی خاص را نشان می‌دهد. در این حالت، هر چهار مسیر تقریباً هزینه یکسانی دارند. در یک برنامه واقعی، شما می‌خواهید روی بهینه‌سازی مسیرهای فراخوانی که از نظر عملکرد بیشترین اهمیت را دارند، تمرکز کنید. نمودار شعله‌ای، که به صورت بصری مسیرهای گران‌تر را با کادرهای بزرگتر برجسته می‌کند، شناسایی این مسیرها را آسان می‌کند.

می‌توانید از فیلتر داده‌های پروفایل برای اصلاح بیشتر نمایش استفاده کنید. برای مثال، سعی کنید یک فیلتر "نمایش پشته‌ها" اضافه کنید که "baz" را به عنوان رشته فیلتر مشخص می‌کند. باید چیزی شبیه به تصویر زیر ببینید که در آن فقط دو مسیر از چهار مسیر فراخوانی load() نمایش داده می‌شوند. این دو مسیر تنها مسیرهایی هستند که از تابعی با رشته "baz" در نام آن عبور می‌کنند. چنین فیلتری زمانی مفید است که می‌خواهید روی یک زیربخش از یک برنامه بزرگتر تمرکز کنید (برای مثال، به این دلیل که فقط مالک بخشی از آن هستید).

eb1d97491782b03f.png

کد حافظه محور

حالا به نوع پروفایل "Heap" بروید. مطمئن شوید که هر فیلتری را که در آزمایش‌های قبلی ایجاد کرده‌اید، حذف کرده‌اید. اکنون باید یک نمودار شعله‌ای ببینید که در آن allocImpl که توسط alloc فراخوانی می‌شود، به عنوان مصرف‌کننده اصلی حافظه در برنامه نمایش داده می‌شود:

f6311c8c841d04c4.png

جدول خلاصه بالای نمودار flame نشان می‌دهد که کل مقدار حافظه استفاده شده در برنامه به طور متوسط ​​حدود ۵۷.۴ مگابایت است که بیشتر آن توسط تابع allocImpl اختصاص داده شده است. با توجه به پیاده‌سازی این تابع، این موضوع تعجب‌آور نیست:

اصلی.برو

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

این تابع یک بار اجرا می‌شود، ۶۴ مگابایت را در تکه‌های کوچک‌تر اختصاص می‌دهد، سپس اشاره‌گرهایی به آن تکه‌ها را در یک متغیر سراسری ذخیره می‌کند تا از جمع‌آوری ناخواسته آنها جلوگیری کند. توجه داشته باشید که میزان حافظه‌ای که توسط profiler نشان داده می‌شود کمی با ۶۴ مگابایت متفاوت است: پروفایلر هیپ Go یک ابزار آماری است، بنابراین اندازه‌گیری‌ها سربار کمی دارند اما دقت بایتی ندارند. از دیدن اختلاف حدود ۱۰ درصدی مانند این تعجب نکنید.

کد متمرکز بر IO

اگر در انتخابگر نوع پروفایل، گزینه "Threads" را انتخاب کنید، نمایش به یک نمودار شعله‌ای تغییر می‌کند که در آن بیشتر عرض توسط توابع wait و waitImpl اشغال شده است:

ebd57fdff01dede9.png

در خلاصه نمودار flame بالا، می‌توانید ببینید که ۱۰۰ گوروتین وجود دارد که پشته فراخوانی خود را از تابع wait رشد می‌دهند. این کاملاً درست است، با توجه به اینکه کدی که این انتظارها را آغاز می‌کند به این شکل است:

اصلی.برو

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

این نوع پروفایل برای درک اینکه آیا برنامه زمان غیرمنتظره‌ای را در انتظار (مانند I/O) صرف می‌کند یا خیر، مفید است. چنین پشته‌های فراخوانی معمولاً توسط پروفایلر CPU نمونه‌برداری نمی‌شوند، زیرا آنها بخش قابل توجهی از زمان CPU را مصرف نمی‌کنند. شما اغلب می‌خواهید از فیلترهای "مخفی کردن پشته‌ها" با پروفایل‌های Threads استفاده کنید - به عنوان مثال، برای مخفی کردن تمام پشته‌هایی که با فراخوانی gopark, زیرا آنها اغلب گوروتین‌های بیکار هستند و نسبت به آنهایی که در I/O منتظر می‌مانند، جذابیت کمتری دارند.

نوع پروفایل threadها همچنین می‌تواند به شناسایی نقاطی در برنامه که threadها برای مدت طولانی منتظر یک mutex متعلق به بخش دیگری از برنامه هستند، کمک کند، اما نوع پروفایل زیر برای این کار مفیدتر است.

کد متمرکز بر رقابت

نوع پروفایل Contention، قفل‌های "مورد نیاز" در برنامه را شناسایی می‌کند. این نوع پروفایل برای برنامه‌های Go در دسترس است، اما باید با مشخص کردن " MutexProfiling: true " در کد پیکربندی عامل، به طور صریح فعال شود. این مجموعه با ثبت (تحت معیار "Contentions") تعداد دفعاتی که یک قفل خاص، هنگام باز شدن توسط goroutine A، منتظر باز شدن قفل توسط goroutine B دیگری بوده است، کار می‌کند. همچنین (تحت معیار "Delay") زمانی را که goroutine مسدود شده منتظر قفل بوده است، ثبت می‌کند. در این مثال، یک پشته contention واحد وجود دارد و کل زمان انتظار برای قفل 10.5 ثانیه بوده است:

83f00dca4a0f768e.png

کدی که این پروفایل را تولید می‌کند شامل ۴ گوروتین است که بر سر یک mutex با هم در حال رقابت هستند:

اصلی.برو

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

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

۶. خلاصه

در این آزمایش، یاد گرفتید که چگونه یک برنامه Go را می‌توان برای استفاده با Cloud Profiler پیکربندی کرد. همچنین یاد گرفتید که چگونه داده‌های عملکرد را با این ابزار جمع‌آوری، مشاهده و تجزیه و تحلیل کنید. اکنون می‌توانید مهارت جدید خود را در سرویس‌های واقعی که روی پلتفرم ابری گوگل اجرا می‌کنید، به کار ببرید.

۷. تبریک می‌گویم!

شما یاد گرفتید که چگونه Cloud Profiler را پیکربندی و استفاده کنید!

اطلاعات بیشتر

مجوز

این اثر تحت مجوز عمومی Creative Commons Attribution 2.0 منتشر شده است.