1. Введение
Последнее обновление: 14 июля 2022 г.
Наблюдаемость приложения
Наблюдаемость и непрерывный профилировщик
Наблюдаемость — это термин, используемый для описания атрибута системы. Система с возможностью наблюдения позволяет командам активно отлаживать свою систему. В этом контексте три столпа наблюдаемости; журналы, метрики и трассировки — это фундаментальные инструменты, позволяющие системе обеспечить наблюдаемость.
Кроме того, в дополнение к трем столпам наблюдения, непрерывное профилирование является еще одним ключевым компонентом наблюдаемости и расширяет базу пользователей в отрасли. Cloud Profiler является одним из создателей и предоставляет простой интерфейс для детализации показателей производительности в стеках вызовов приложений.
Эта кодовая лаборатория является второй частью серии и посвящена инструментированию агента непрерывного профилирования. В части 1 рассматривается распределенная трассировка с помощью OpenTelemetry и Cloud Trace, а в части 1 вы узнаете больше о том, как лучше выявлять узкие места микросервисов.
Что ты построишь
В этой лаборатории кода вы собираетесь внедрить агент непрерывного профилирования в серверную службу «приложения Shakespeare» (также известного как Shakesapp), которое работает в кластере Google Kubernetes Engine. Архитектура Shakesapp описана ниже:
- Loadgen отправляет строку запроса клиенту по HTTP.
- Клиенты передают запрос от генератора нагрузки на сервер в gRPC.
- Сервер принимает запрос от клиента, извлекает все произведения Шекспира в текстовом формате из Google Cloud Storage, ищет строки, содержащие запрос, и возвращает номер строки, соответствующей клиенту.
В части 1 вы обнаружили, что где-то в серверной службе существует узкое место, но не смогли определить точную причину.
Что вы узнаете
- Как встроить агент профилировщика
- Как исследовать узкое место в Cloud Profiler
В этой кодовой лаборатории объясняется, как использовать агент непрерывного профилирования в вашем приложении.
Что вам понадобится
- Базовые знания Го
- Базовые знания Kubernetes
2. Настройка и требования
Самостоятельная настройка среды
Если у вас еще нет учетной записи Google (Gmail или Google Apps), вам необходимо ее создать . Войдите в консоль Google Cloud Platform ( console.cloud.google.com ) и создайте новый проект.
Если у вас уже есть проект, щелкните раскрывающееся меню выбора проекта в левом верхнем углу консоли:
и нажмите кнопку «НОВЫЙ ПРОЕКТ» в появившемся диалоговом окне, чтобы создать новый проект:
Если у вас еще нет проекта, вы должны увидеть подобное диалоговое окно, чтобы создать свой первый:
Последующий диалог создания проекта позволяет вам ввести детали вашего нового проекта:
Запомните идентификатор проекта, который является уникальным именем для всех проектов Google Cloud (имя, указанное выше, уже занято и не подойдет вам, извините!). Позже в этой лаборатории он будет называться PROJECT_ID.
Далее, если вы еще этого не сделали, вам необходимо включить выставление счетов в консоли разработчика, чтобы использовать ресурсы Google Cloud и включить Cloud Trace API .
Выполнение этой кодовой лаборатории не должно стоить вам больше нескольких долларов, но может стоить больше, если вы решите использовать больше ресурсов или оставите их включенными (см. раздел «Очистка» в конце этого документа). Цены на Google Cloud Trace, Google Kubernetes Engine и Google Artifact Registry указаны в официальной документации.
- Цены на операционный пакет Google Cloud | Операционный пакет
- Цены | Документация по движку Kubernetes
- Цены на реестр артефактов | Документация по реестру артефактов
Новые пользователи 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». (подготовка и подключение к среде займет всего несколько минут).
После подключения к 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:
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). Процесс этой кодовой лаборатории выглядит следующим образом:
- Загрузите базовый проект в Cloud Shell.
- Встраивайте микросервисы в контейнеры
- Загрузите контейнеры в реестр артефактов Google (GAR).
- Развертывание контейнеров в GKE
- Измените исходный код служб для инструментов трассировки.
- Перейти к шагу 2
Включить движок Kubernetes
Сначала мы настраиваем кластер Kubernetes, в котором Shakesapp работает на GKE, поэтому нам нужно включить GKE. Перейдите в меню «Kubernetes Engine» и нажмите кнопку ВКЛЮЧИТЬ.
Теперь вы готовы создать кластер Kubernetes.
Создать кластер Kubernetes
В Cloud Shell выполните следующую команду, чтобы создать кластер Kubernetes. Подтвердите, что значение зоны соответствует региону , который вы будете использовать для создания репозитория реестра артефактов. Измените значение зоны 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
Реестр артефактов и настройка скаффолда
Теперь у нас есть кластер Kubernetes, готовый к развертыванию. Затем мы готовим реестр контейнеров для отправки и развертывания контейнеров. Для этих шагов нам нужно настроить реестр артефактов (GAR) и скаффолд для его использования.
Настройка реестра артефактов
Перейдите в меню «Реестр артефактов» и нажмите кнопку ВКЛЮЧИТЬ.
Через несколько секунд вы увидите браузер репозитория GAR. Нажмите кнопку «СОЗДАТЬ РЕПОЗИТОРИЙ» и введите имя репозитория.
В этой кодовой лаборатории я называю новый репозиторий trace-codelab
. Формат артефакта — «Docker», а тип местоположения — «Регион». Выберите регион, близкий к тому, который вы установили для зоны по умолчанию Google Compute Engine. Например, в этом примере выше выбрано «us-central1-f», поэтому здесь мы выбираем «us-central1 (Айова)». Затем нажмите кнопку «СОЗДАТЬ».
Теперь вы видите «trace-codelab» в браузере репозитория.
Мы вернемся сюда позже, чтобы проверить путь к реестру.
Установка строительных лесов
Skaffold — удобный инструмент, когда вы работаете над созданием микросервисов, работающих в Kubernetes. Он управляет рабочим процессом создания, отправки и развертывания контейнеров приложений с помощью небольшого набора команд. Skaffold по умолчанию использует реестр Docker в качестве реестра контейнеров, поэтому вам необходимо настроить skaffold для распознавания GAR при отправке контейнеров.
Снова откройте Cloud Shell и убедитесь, что скаффолд установлен. (Cloud Shell по умолчанию устанавливает скаффолд в среду.) Выполните следующую команду и посмотрите версию скаффолда.
skaffold version
Вывод команды
v1.38.0
Теперь вы можете зарегистрировать репозиторий по умолчанию для использования скаффолдом. Чтобы получить путь к реестру, перейдите на панель мониторинга реестра артефактов и щелкните имя репозитория, который вы только что настроили на предыдущем шаге.
Затем вы увидите следы хлебных крошек в верхней части страницы. Нажмите значок, чтобы скопировать путь реестра в буфер обмена.
При нажатии кнопки копирования вы увидите диалоговое окно в нижней части браузера с сообщением типа:
«us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab» скопирован.
Вернитесь в облачную оболочку. Запустите команду 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, в котором работают микросервисы Codelab.
Дальше
На следующем этапе вы подключите агент непрерывного профилирования к серверной службе.
3. Создавайте, распространяйте и развертывайте микросервисы.
Загрузите материал codelab
На предыдущем шаге мы создали все необходимые условия для этой лаборатории кода. Теперь вы готовы запускать поверх них целые микросервисы. Материалы codelab размещены на 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: определение протокола для связи между клиентом и сервером.
- src: каталоги с исходным кодом каждого сервиса.
- skaffold.yaml: файл конфигурации для skaffold.
В этой лаборатории кода вы обновите исходный код, расположенный в папке step4
. Вы также можете обратиться к исходному коду в папках step[1-6]
для изменений с самого начала. (Часть 1 охватывает шаги с 0 по 4, а часть 2 — шаги 5 и 6)
Запустить команду скаффолда
Наконец, вы готовы собирать, отправлять и развертывать весь контент в только что созданном кластере 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
После развертывания вы увидите фактические журналы приложений, отправляемые на стандартный вывод в каждом контейнере, например:
Вывод команды
[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
Краткое содержание
На этом этапе вы подготовили материал codelab в своей среде и подтвердили ожидаемое выполнение скаффолда.
Дальше
На следующем шаге вы измените исходный код службы loadgen для инструментирования информации трассировки.
4. Инструментарий агента Cloud Profiler
Концепция непрерывного профилирования
Прежде чем объяснять концепцию непрерывного профилирования, нам необходимо сначала понять, что такое профилирование. Профилирование является одним из способов динамического анализа приложения (динамического анализа программы) и обычно выполняется при разработке приложения, в процессе нагрузочного тестирования и т.п. Это разовое действие для измерения системных показателей, таких как использование ЦП и памяти, в течение определенного периода. После сбора данных профиля разработчики анализируют их из кода.
Непрерывное профилирование — это расширенный подход обычного профилирования: он периодически запускает профили коротких окон для долго работающего приложения и собирает кучу данных профиля. Затем он автоматически генерирует статистический анализ на основе определенного атрибута приложения, такого как номер версии, зона развертывания, время измерения и т. д. Более подробную информацию о концепции вы найдете в нашей документации .
Поскольку целью является работающее приложение, существует способ периодически собирать данные профиля и отправлять их на какой-либо сервер, который выполняет постобработку статистических данных. Это агент Cloud Profiler, и вы скоро собираетесь внедрить его в серверную службу.
Встроить агент Cloud Profiler
Откройте редактор Cloud Shell, нажав кнопку в правом верхнем углу Cloud Shell. Откройте
step4/src/server/main.go
в проводнике на левой панели и найдите функцию main.
шаг 4/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()
для удобства чтения.
шаг 4/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 : отключить профилирование горутины.
- NoCPUProfiling : отключить профилирование ЦП.
В этой кодовой лаборатории мы включаем только профилирование ЦП.
Теперь вам нужно просто вызвать эту функцию в main
функции. Обязательно импортируйте пакет Cloud Profiler в блок импорта.
шаг 4/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. Введите «профилировщик» в поле поиска вверху и щелкните значок «Профилировщик».
Затем вы увидите следующий график пламени.
Краткое содержание
На этом этапе вы внедрили агент Cloud Profiler в службу сервера и подтвердили, что он генерирует график пламени.
Дальше
На следующем этапе вы исследуете причину узкого места в приложении с помощью графика пламени.
5. Анализ графика пламени Cloud Profiler
Что такое график пламени?
Flame Graph — один из способов визуализации данных профиля. Подробное объяснение можно найти в нашем документе , но краткое изложение таково:
- Каждая полоса выражает вызов метода/функции в приложении.
- Вертикальное направление — это стек вызовов; стек вызовов растет сверху вниз
- Горизонтальное направление — использование ресурсов; чем дольше, тем хуже.
Учитывая это, давайте посмотрим на полученный график пламени.
Анализ графика пламени
В предыдущем разделе вы узнали, что каждая полоска на графике пламени отражает вызов функции/метода, а ее длина означает использование ресурса функцией/методом. График пламени Cloud Profiler сортирует полосу в порядке убывания или по длине слева направо. Сначала вы можете начать смотреть в верхнем левом углу графика.
В нашем случае очевидно, что grpc.(*Server).serveStreams.func1.2
потребляет большую часть процессорного времени, и, просматривая стек вызовов сверху вниз, большая часть времени тратится на main.(*serverService).GetMatchCount
— обработчик сервера gRPC в серверной службе.
В разделе GetMatchCount вы видите ряд функций регулярного выражения : regexp.MatchString
и regexp.Compile
. Они из стандартной упаковки: то есть должны быть хорошо протестированы со многих точек зрения, в том числе и производительности. Но результат здесь показывает, что использование ресурсов процессорного времени высоко в regexp.MatchString
и regexp.Compile
. Учитывая эти факты, предполагается, что использование regexp.MatchString
как-то связано с проблемами производительности. Итак, давайте прочитаем исходный код, в котором используется функция.
шаг 4/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.MatchString
компилирует шаблон регулярного выражения при каждом вызове. Итак, причина большого потребления ресурсов звучит так.
Краткое содержание
На этом этапе вы сделали предположение о причине потребления ресурсов, проанализировав график пламени.
Дальше
На следующем шаге вы обновите исходный код службы сервера и подтвердите изменение с версии 1.0.0.
6. Обновите исходный код и сравните графики пламени.
Обновите исходный код
На предыдущем шаге вы предположили, что использование regexp.MatchString
как-то связано с большим потреблением ресурсов. Итак, давайте решим это. Откройте код и немного измените эту часть.
шаг 4/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()
.
шаг 4/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 и посмотрите, как она выглядит.
Обязательно измените версию на "1.1.0"
чтобы вы видели только профили версии 1.1.0. Как вы можете заметить, длина полосы GetMatchCount уменьшилась, а коэффициент использования процессорного времени (т.е. полоса стала короче).
Не только просматривая график пламени одной версии, вы также можете сравнить различия между двумя версиями.
Измените значение раскрывающегося списка «Сравнить с» на «Версия» и измените значение «Сравниваемая версия» на «1.0.0», исходную версию.
Вы увидите такой график пламени. Форма графика такая же, как в 1.1.0, но расцветка другая. В режиме сравнения цвет означает следующее:
- Синий : значение (потребление ресурсов) уменьшено.
- Оранжевый : полученная ценность (потребление ресурсов).
- Серый : нейтральный
Учитывая легенду, давайте более подробно рассмотрим функцию. Нажав на полосу, которую хотите увеличить, вы сможете увидеть более подробную информацию внутри стека. Нажмите кнопку main.(*serverService).GetMatchCount
. Также, наведя курсор на полосу, вы увидите детали сравнения.
В нем говорится, что общее время процессора уменьшено с 5,26 с до 2,88 с (всего 10 с = окно выборки). Это огромное улучшение!
Теперь вы можете повысить производительность своего приложения за счет анализа данных профиля.
Краткое содержание
На этом этапе вы внесли изменения в службу сервера и подтвердили улучшение режима сравнения Cloud Profiler.
Дальше
На следующем шаге вы обновите исходный код службы сервера и подтвердите изменение с версии 1.0.0.
7. Дополнительный шаг: подтвердите улучшение водопада трассировки.
Разница между распределенной трассировкой и непрерывным профилированием
В первой части лабораторной работы по коду вы подтвердили, что можете определить узкое место во всех микросервисах для пути запроса, но не можете выяснить точную причину узкого места в конкретной службе. В ходе этой лабораторной работы по коду части 2 вы узнали, что непрерывное профилирование позволяет выявлять узкие места внутри отдельного сервиса по стекам вызовов.
На этом этапе давайте рассмотрим каскадный график распределенной трассировки (Cloud Trace) и увидим отличие от непрерывного профилирования.
Этот водопадный граф является одной из трасс с запросом «любовь». Всего это занимает около 6,7 с (6700 мс).
И это после улучшения того же запроса. Как вы сказали, общая задержка теперь составляет 1,5 с (1500 мс), что является огромным улучшением по сравнению с предыдущей реализацией.
Важным моментом здесь является то, что в каскадной диаграмме распределенной трассировки информация стека вызовов недоступна, если вы не инструментируете диапазоны повсюду. Кроме того, распределенные трассировки фокусируются только на задержке между службами, тогда как непрерывное профилирование фокусируется на компьютерных ресурсах (ЦП, память, потоки ОС) одной службы.
В другом аспекте распределенная трасса является базой событий, непрерывный профиль является статистическим. Каждая трасса имеет свой график задержки, и вам нужен другой формат, например распределение , чтобы получить тенденцию изменения задержки.
Краткое содержание
На этом этапе вы проверили разницу между распределенной трассировкой и непрерывным профилированием.
8. Поздравления
Вы успешно создали распределенные трассировки с помощью OpenTelemery и подтвердили задержки запросов в микросервисе в Google Cloud Trace.
Для расширенных упражнений вы можете самостоятельно попробовать следующие темы.
- Текущая реализация отправляет все промежутки, созданные в ходе проверки работоспособности. (
grpc.health.v1.Health/Check
) Как отфильтровать эти промежутки из Cloud Traces? Подсказка здесь . - Сопоставьте журналы событий с интервалами и посмотрите, как это работает в 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 и администратор» > «Настройки», а затем нажмите кнопку «ЗАВЕРШИТЬ».
Затем введите идентификатор проекта (а не имя проекта) в форму в диалоговом окне и подтвердите закрытие.