1. Introdução

Última atualização:14/07/2022
Observabilidade do aplicativo
Observabilidade e Continuous Profiler
Observabilidade é o termo usado para descrever um atributo de um sistema. Um sistema com observabilidade permite que as equipes depurem ativamente o sistema. Nesse contexto, três pilares da observabilidade (registros, métricas e rastreamentos) são a instrumentação fundamental para que o sistema adquira observabilidade.
Além dos três pilares da observabilidade, a criação de perfis contínuos é outro componente essencial para a observabilidade e está aumentando a base de usuários no setor. O Cloud Profiler é um dos criadores e oferece uma interface fácil para detalhar as métricas de desempenho nas pilhas de chamadas de aplicativos.
Este codelab é a parte 2 da série e aborda a instrumentação de um agente de criação de perfil contínuo. A Parte 1 aborda o rastreamento distribuído com o OpenTelemetry e o Cloud Trace, e você vai aprender a identificar melhor o gargalo dos microsserviços com a Parte 1.
O que você vai criar
Neste codelab, você vai instrumentar o agente do criador de perfil contínuo no serviço de servidor do "aplicativo Shakespeare" (também conhecido como Shakesapp), que é executado em um cluster do Google Kubernetes Engine. A arquitetura do Shakesapp é descrita abaixo:

- O Loadgen envia uma string de consulta ao cliente em HTTP.
- Os clientes transmitem a consulta do loadgen para o servidor em gRPC.
- O servidor aceita a consulta do cliente, busca todas as obras de Shakespeare em formato de texto no Google Cloud Storage, pesquisa as linhas que contêm a consulta e retorna o número da linha correspondente ao cliente.
Na parte 1, você descobriu que o gargalo existe em algum lugar no serviço do servidor, mas não conseguiu identificar a causa exata.
O que você vai aprender
- Como incorporar o agente do Profiler
- Como investigar o gargalo no Cloud Profiler
Este codelab explica como instrumentar um agente de criação de perfil contínuo no seu aplicativo.
O que é necessário
- Conhecimento básico de Go
- Conhecimento básico do Kubernetes
2. Configuração e requisitos
Configuração de ambiente autoguiada
Se você ainda não tem uma Conta do Google (Gmail ou Google Apps), crie uma. Faça login no Console do Google Cloud Platform ( console.cloud.google.com) e crie um novo projeto.
Se você já tiver um projeto, clique no menu suspenso de seleção no canto superior esquerdo do console:

e clique no botão "NEW PROJECT" na caixa de diálogo exibida para criar um novo projeto:

Se você ainda não tiver um projeto, uma caixa de diálogo como esta será exibida para criar seu primeiro:

A caixa de diálogo de criação de projeto subsequente permite que você insira os detalhes do novo projeto:

Lembre-se do código do projeto, um nome exclusivo em todos os projetos do Google Cloud. O nome acima já foi escolhido e não servirá para você. Ele será indicado mais adiante neste codelab como PROJECT_ID.
Em seguida, será preciso ativar o faturamento no Developers Console para usar os recursos do Google Cloud e ativar a API Cloud Trace, caso ainda não tenha feito isso.

A execução por meio deste codelab terá um custo baixo, mas poderá ser mais se você decidir usar mais recursos ou se deixá-los em execução. Consulte a seção "limpeza" no final deste documento. Os preços do Google Cloud Trace, do Google Kubernetes Engine e do Google Artifact Registry estão na documentação oficial.
- Preços do pacote de operações do Google Cloud | Pacote de operações
- Preços | Documentação do Kubernetes Engine
- Preços do Artifact Registry | Documentação do Artifact Registry
Novos usuários do Google Cloud Platform estão qualificados para uma avaliação sem custo financeiro de US$300, o que torna este codelab totalmente sem custo financeiro.
Configuração do Google Cloud Shell
Embora o Google Cloud e o Google Cloud Trace possam ser operados remotamente do seu laptop, neste codelab usaremos o Google Cloud Shell, um ambiente de linha de comando executado no Cloud.
O Cloud Shell é uma máquina virtual com base em Debian que contém todas as ferramentas de desenvolvimento necessárias. Ela oferece um diretório principal persistente de 5 GB, além de ser executada no Google Cloud. Isso aprimora o desempenho e a autenticação da rede. Isso significa que tudo que você precisa para este codelab é um navegador (sim, funciona em um Chromebook).
Para ativar o Cloud Shell no Console do Cloud, basta clicar em Ativar o Cloud Shell
. Leva apenas alguns instantes para provisionar e se conectar ao ambiente.


Depois de se conectar ao Cloud Shell, você já estará autenticado e o projeto estará configurado com seu PROJECT_ID.
gcloud auth list
Resposta ao comando
Credentialed accounts: - <myaccount>@<mydomain>.com (active)
gcloud config list project
Resposta ao comando
[core] project = <PROJECT_ID>
Se, por algum motivo, o projeto não estiver definido, basta emitir o seguinte comando:
gcloud config set project <PROJECT_ID>
Quer encontrar seu PROJECT_ID? Veja qual ID você usou nas etapas de configuração ou procure-o no painel do Console do Cloud:

O Cloud Shell também define algumas variáveis de ambiente por padrão, o que pode ser útil ao executar comandos futuros.
echo $GOOGLE_CLOUD_PROJECT
Resposta ao comando
<PROJECT_ID>
Defina a zona padrão e a configuração do projeto:
gcloud config set compute/zone us-central1-f
É possível escolher uma variedade de zonas diferentes. Para mais informações, consulte Regiões e zonas.
Configuração da linguagem Go
Neste codelab, usamos Go para todo o código-fonte. Execute o comando a seguir no Cloud Shell e confirme se a versão do Go é 1.17 ou mais recente:
go version
Resposta ao comando
go version go1.18.3 linux/amd64
Configurar um cluster do Google Kubernetes
Neste codelab, você vai executar um cluster de microsserviços no Google Kubernetes Engine (GKE). O processo deste codelab é o seguinte:
- Baixar o projeto de linha de base no Cloud Shell
- Criar microsserviços em contêineres
- Fazer upload de contêineres para o Google Artifact Registry (GAR)
- Implantar contêineres no GKE
- Modificar o código-fonte dos serviços para instrumentação de rastreamento
- Acesse a etapa 2
Ativar o Kubernetes Engine
Primeiro, configuramos um cluster do Kubernetes em que o Shakesapp é executado no GKE. Portanto, precisamos ativar o GKE. Acesse o menu "Kubernetes Engine" e pressione o botão "ATIVAR".

Agora você está pronto para criar um cluster do Kubernetes.
Criar cluster do Kubernetes
No Cloud Shell, execute o comando a seguir para criar um cluster do Kubernetes. Confirme se o valor da zona está na região que você vai usar para criar o repositório do Artifact Registry. Mude o valor da zona us-central1-f se a região do repositório não estiver cobrindo a zona.
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
Resposta ao comando
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
Configuração do Artifact Registry e do skaffold
Agora temos um cluster do Kubernetes pronto para implantação. Em seguida, preparamos um registro de contêineres para enviar e implantar contêineres. Para essas etapas, precisamos configurar um Artifact Registry (GAR) e o skaffold para usá-lo.
Configuração do Artifact Registry
Navegue até o menu "Artifact Registry" e pressione o botão "ATIVAR".

Depois de alguns instantes, o navegador de repositório do GAR vai aparecer. Clique no botão "CRIAR REPOSITÓRIO" e insira o nome dele.

Neste codelab, vou chamar o novo repositório de trace-codelab. O formato do artefato é "Docker" e o tipo de local é "Região". Escolha a região próxima àquela que você definiu para a zona padrão do Google Compute Engine. Por exemplo, o exemplo acima escolheu "us-central1-f", então aqui escolhemos "us-central1 (Iowa)". Em seguida, clique no botão "CRIAR".

Agora você vai ver "trace-codelab" no navegador do repositório.

Vamos voltar aqui mais tarde para verificar o caminho do registro.
Configuração do Skaffold
O Skaffold é uma ferramenta útil para criar microsserviços que são executados no Kubernetes. Ele lida com o fluxo de trabalho de criação, envio e implantação de contêineres de aplicativos com um pequeno conjunto de comandos. Por padrão, o Skaffold usa o Docker Registry como registro de contêiner. Portanto, é necessário configurar o Skaffold para reconhecer o GAR ao enviar contêineres.
Abra o Cloud Shell novamente e confirme se o Skaffold está instalado. O Cloud Shell instala o Skaffold no ambiente por padrão. Execute o comando a seguir e confira a versão do skaffold.
skaffold version
Resposta ao comando
v1.38.0
Agora, você pode registrar o repositório padrão para o Skaffold usar. Para conseguir o caminho do registro, acesse o painel do Artifact Registry e clique no nome do repositório que você acabou de configurar na etapa anterior.

Em seguida, você verá trilhas de navegação estrutural na parte de cima da página. Clique no ícone
para copiar o caminho do registro para a área de transferência.

Ao clicar no botão de cópia, você verá a caixa de diálogo na parte de baixo do navegador com a mensagem:
"us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab" foi copiado
Volte para o Cloud Shell. Execute o comando skaffold config set default-repo com o valor que você acabou de copiar do painel.
skaffold config set default-repo us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab
Resposta ao comando
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
Além disso, é necessário configurar o registro para a configuração do Docker. Execute este comando:
gcloud auth configure-docker us-central1-docker.pkg.dev --quiet
Resposta ao comando
{
"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
Agora você está pronto para a próxima etapa, que é configurar um contêiner do Kubernetes no GKE.
Resumo
Nesta etapa, você vai configurar o ambiente do codelab:
- Configurar o Cloud Shell
- Criou um repositório do Artifact Registry para o registro de contêineres
- Configurar o Skaffold para usar o Container Registry
- Criar um cluster do Kubernetes em que os microsserviços do codelab são executados
A seguir
Na próxima etapa, você vai instrumentar o agente do criador de perfil contínuo no serviço do servidor.
3. Criar, enviar e implantar os microsserviços
Baixe o material do codelab
Na etapa anterior, configuramos todos os pré-requisitos para este codelab. Agora você já pode executar microsserviços inteiros neles. O material do codelab está hospedado no GitHub. Faça o download dele para o ambiente shell do Cloud Shell com o seguinte comando git.
cd ~ git clone https://github.com/ymotongpoo/opentelemetry-trace-codelab-go.git cd opentelemetry-trace-codelab-go
A estrutura de diretórios do projeto é a seguinte:
.
├── 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
- manifests: arquivos de manifesto do Kubernetes
- proto: definição proto para a comunicação entre cliente e servidor
- src: diretórios para o código-fonte de cada serviço
- skaffold.yaml: arquivo de configuração do skaffold
Neste codelab, você vai atualizar o código-fonte localizado na pasta step4. Você também pode consultar o código-fonte nas pastas step[1-6] para ver as mudanças desde o início. A Parte 1 abrange as etapas de 0 a 4, e a Parte 2 abrange as etapas 5 e 6.
Executar o comando skaffold
Por fim, você está pronto para criar, enviar e implantar todo o conteúdo no cluster do Kubernetes que acabou de criar. Parece que contém várias etapas, mas o skaffold faz tudo por você. Vamos tentar com o seguinte comando:
cd step4 skaffold dev
Assim que você executar o comando, vai ver a saída de registro de docker build e poderá confirmar que eles foram enviados ao registro.
Resposta ao comando
... ---> 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
Depois do push de todos os contêineres de serviço, as implantações do Kubernetes começam automaticamente.
Resposta ao comando
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
Após a implantação, você verá os registros reais do aplicativo emitidos para stdout em cada contêiner, assim:
Resposta ao comando
[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
Neste momento, você quer ver todas as mensagens do servidor. Agora você está pronto para começar a instrumentar seu aplicativo com o OpenTelemetry para rastreamento distribuído dos serviços.
Antes de começar a instrumentar o serviço, desligue o cluster com Ctrl-C.
Resposta ao comando
...
[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
Resumo
Nesta etapa, você preparou o material do codelab no seu ambiente e confirmou que o Skaffold é executado conforme o esperado.
A seguir
Na próxima etapa, você vai modificar o código-fonte do serviço loadgen para instrumentar as informações de rastreamento.
4. Instrumentação do agente do Cloud Profiler
Conceito de criação de perfil contínua
Antes de explicar o conceito de criação de perfil contínua, precisamos entender o que é criação de perfil. A criação de perfil é uma das maneiras de analisar o aplicativo de forma dinâmica (análise dinâmica de programas) e geralmente é realizada durante o desenvolvimento do aplicativo no processo de teste de carga e assim por diante. Essa é uma atividade única para medir as métricas do sistema, como uso de CPU e memória, durante o período específico. Depois de coletar os dados de perfil, os desenvolvedores os analisam fora do código.
A criação de perfil contínuo é a abordagem estendida da criação de perfil normal: ela executa perfis de janela curta no aplicativo de longa execução periodicamente e coleta vários dados de perfil. Em seguida, ela gera automaticamente a análise estatística com base em um determinado atributo do aplicativo, como número da versão, zona de implantação, tempo de medição etc. Confira mais detalhes sobre o conceito na nossa documentação.
Como o destino é um aplicativo em execução, há uma maneira de coletar dados de perfil periodicamente e enviá-los para um back-end que pós-processa os dados estatísticos. Esse é o agente do Cloud Profiler, que você vai incorporar ao serviço do servidor em breve.
Incorporar o agente do Cloud Profiler
Abra o Editor do Cloud Shell pressionando o botão
no canto superior direito do Cloud Shell. Abra step4/src/server/main.go no explorador do painel esquerdo e encontre a função principal.
step4/src/server/main.go
func main() {
...
// step2. setup OpenTelemetry
tp, err := initTracer()
if err != nil {
log.Fatalf("failed to initialize TracerProvider: %v", err)
}
defer func() {
if err := tp.Shutdown(context.Background()); err != nil {
log.Fatalf("error shutting down TracerProvider: %v", err)
}
}()
// step2. end setup
svc := NewServerService()
// step2: add interceptor
interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider())
srv := grpc.NewServer(
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor(interceptorOpt)),
grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor(interceptorOpt)),
)
// step2: end adding interceptor
shakesapp.RegisterShakespeareServiceServer(srv, svc)
healthpb.RegisterHealthServer(srv, svc)
if err := srv.Serve(lis); err != nil {
log.Fatalf("error serving server: %v", err)
}
}
Na função main, você encontra um código de configuração para OpenTelemetry e gRPC, que foi feito na parte 1 do codelab. Agora, adicione a instrumentação para o agente do Cloud Profiler aqui. Assim como fizemos com initTracer(), você pode escrever uma função chamada initProfiler() para facilitar a leitura.
step4/src/server/main.go
import (
...
"cloud.google.com/go/profiler" // step5. add profiler package
"cloud.google.com/go/storage"
...
)
// step5: add Profiler initializer
func initProfiler() {
cfg := profiler.Config{
Service: "server",
ServiceVersion: "1.0.0",
NoHeapProfiling: true,
NoAllocProfiling: true,
NoGoroutineProfiling: true,
NoCPUProfiling: false,
}
if err := profiler.Start(cfg); err != nil {
log.Fatalf("failed to launch profiler agent: %v", err)
}
}
Vamos analisar as opções especificadas no objeto profiler.Config{}.
- Serviço: o nome do serviço que você pode selecionar e ativar no painel do criador de perfil.
- ServiceVersion: o nome da versão do serviço. É possível comparar conjuntos de dados de perfil com base nesse valor.
- NoHeapProfiling: desativa a criação de perfil de consumo de memória.
- NoAllocProfiling: desativa a criação de perfil de alocação de memória.
- NoGoroutineProfiling: desativa a criação de perfil de gorrotina
- NoCPUProfiling: desativa a criação de perfil da CPU
Neste codelab, vamos ativar apenas o perfil de CPU.
Agora, basta chamar essa função na main. Importe o pacote do Cloud Profiler no bloco de importação.
step4/src/server/main.go
func main() {
...
defer func() {
if err := tp.Shutdown(context.Background()); err != nil {
log.Fatalf("error shutting down TracerProvider: %v", err)
}
}()
// step2. end setup
// step5. start profiler
go initProfiler()
// step5. end
svc := NewServerService()
// step2: add interceptor
...
}
Você está chamando a função initProfiler() com a palavra-chave go. Como profiler.Start() bloqueia, é necessário executá-lo em outra gorrotina. Agora ele está pronto para ser criado. Execute go mod tidy antes da implantação.
go mod tidy
Agora implante o cluster com o novo serviço de servidor.
skaffold dev
Geralmente, leva alguns minutos para o gráfico de chamas aparecer no Cloud Profiler. Digite "profiler" na caixa de pesquisa na parte de cima e clique no ícone do Profiler.

Em seguida, você verá o seguinte gráfico de chamas.

Resumo
Nesta etapa, você incorporou o agente do Cloud Profiler ao serviço do servidor e confirmou que ele gera um gráfico de chamas.
A seguir
Na próxima etapa, você vai investigar a causa do gargalo no aplicativo com o gráfico de chamas.
5. Analisar o gráfico de chama do Cloud Profiler
O que é um gráfico de chama?
O gráfico de chamas é uma das maneiras de visualizar os dados de perfil. Para uma explicação detalhada, consulte nosso documento. No entanto, o resumo é:
- Cada barra expressa a chamada de método/função no aplicativo.
- A direção vertical é a pilha de chamadas, que cresce de cima para baixo.
- A direção horizontal é o uso de recursos. Quanto mais longa, pior.
Com isso em mente, vamos analisar o gráfico de chamas obtido.

Como analisar o gráfico de chamas
Na seção anterior, você aprendeu que cada barra no gráfico de chamas expressa a chamada de função/método, e o comprimento dela significa o uso de recursos na função/método. O gráfico de chamas do Cloud Profiler classifica a barra em ordem decrescente ou pelo comprimento da esquerda para a direita. Comece olhando o canto superior esquerdo do gráfico.

No nosso caso, fica claro que grpc.(*Server).serveStreams.func1.2 está consumindo a maior parte do Tempo de CPU. Ao analisar a pilha de chamadas de cima para baixo, percebemos que a maior parte do tempo é gasto em main.(*serverService).GetMatchCount, que é o gerenciador do servidor gRPC no serviço de servidor.
Em "GetMatchCount", você vê uma série de funções regexp: regexp.MatchString e regexp.Compile. Eles são do pacote padrão, ou seja, precisam ser bem testados de vários pontos de vista, incluindo a performance. Mas o resultado aqui mostra que o uso de recursos de tempo de CPU é alto em regexp.MatchString e regexp.Compile. Considerando esses fatos, a suposição aqui é que o uso de regexp.MatchString tem algo a ver com problemas de desempenho. Então, vamos ler o código-fonte em que a função é usada.
step4/src/server/main.go
func (s *serverService) GetMatchCount(ctx context.Context, req *shakesapp.ShakespeareRequest) (*shakesapp.ShakespeareResponse, error) {
resp := &shakesapp.ShakespeareResponse{}
texts, err := readFiles(ctx, bucketName, bucketPrefix)
if err != nil {
return resp, fmt.Errorf("fails to read files: %s", err)
}
for _, text := range texts {
for _, line := range strings.Split(text, "\n") {
line, query := strings.ToLower(line), strings.ToLower(req.Query)
isMatch, err := regexp.MatchString(query, line)
if err != nil {
return resp, err
}
if isMatch {
resp.MatchCount++
}
}
}
return resp, nil
}
É aqui que regexp.MatchString é chamado. Ao ler o código-fonte, você pode notar que a função é chamada dentro do loop for aninhado. Portanto, o uso dessa função pode estar incorreto. Vamos pesquisar o GoDoc de regexp.

De acordo com o documento, regexp.MatchString compila o padrão de expressão regular em todas as chamadas. Portanto, a causa do grande consumo de recursos é assim:
Resumo
Nesta etapa, você fez uma proposição da causa do consumo de recursos analisando o gráfico de chamas.
A seguir
Na próxima etapa, você vai atualizar o código-fonte do serviço do servidor e confirmar a mudança da versão 1.0.0.
6. Atualizar o código-fonte e comparar os gráficos de chamas
Atualizar o código-fonte
Na etapa anterior, você presumiu que o uso de regexp.MatchString tem algo a ver com o grande consumo de recursos. Vamos resolver isso. Abra o código e mude um pouco essa parte.
step4/src/server/main.go
func (s *serverService) GetMatchCount(ctx context.Context, req *shakesapp.ShakespeareRequest) (*shakesapp.ShakespeareResponse, error) {
resp := &shakesapp.ShakespeareResponse{}
texts, err := readFiles(ctx, bucketName, bucketPrefix)
if err != nil {
return resp, fmt.Errorf("fails to read files: %s", err)
}
// step6. considered the process carefully and naively tuned up by extracting
// regexp pattern compile process out of for loop.
query := strings.ToLower(req.Query)
re := regexp.MustCompile(query)
for _, text := range texts {
for _, line := range strings.Split(text, "\n") {
line = strings.ToLower(line)
isMatch := re.MatchString(line)
// step6. done replacing regexp with strings
if isMatch {
resp.MatchCount++
}
}
}
return resp, nil
}
Como você pode ver, agora o processo de compilação do padrão de expressão regular é extraído do regexp.MatchString e movido para fora do loop for aninhado.
Antes de implantar esse código, atualize a string de versão na função initProfiler().
step4/src/server/main.go
func initProfiler() {
cfg := profiler.Config{
Service: "server",
ServiceVersion: "1.1.0", // step6. update version
NoHeapProfiling: true,
NoAllocProfiling: true,
NoGoroutineProfiling: true,
NoCPUProfiling: false,
}
if err := profiler.Start(cfg); err != nil {
log.Fatalf("failed to launch profiler agent: %v", err)
}
}
Agora vamos ver como isso funciona. Implante o cluster com o comando skaffold.
skaffold dev
Depois de um tempo, recarregue o painel do Cloud Profiler e veja como ele está.

Mude a versão para "1.1.0" para ver apenas os perfis da versão 1.1.0. Como você pode perceber, o comprimento da barra de GetMatchCount foi reduzido, e a proporção de uso do tempo de CPU (ou seja, a barra ficou mais curta).

Não apenas analisando o gráfico de chamas de uma única versão, mas também comparando as diferenças entre duas versões.

Mude o valor da lista suspensa "Comparar com" para "Versão" e o valor de "Versão comparada" para "1.0.0", a versão original.

Você vai ver este tipo de gráfico de chamas. O formato do gráfico é o mesmo de 1.1.0, mas a coloração é diferente. No modo de comparação, o significado da cor é:
- Azul: o valor (consumo de recursos) reduzido
- Laranja: o valor (consumo de recursos) ganho
- Cinza: neutro
Com base na legenda, vamos analisar a função. Clique na barra que você quer ampliar para ver mais detalhes dentro da pilha. Clique na barra main.(*serverService).GetMatchCount. Ao passar o cursor sobre a barra, você verá os detalhes da comparação.

Ele diz que o tempo total de CPU é reduzido de 5,26 s para 2,88 s (o total é 10 s = janela de amostragem). Essa é uma grande melhoria!
Agora você pode melhorar a performance do aplicativo com base na análise dos dados de perfil.
Resumo
Nesta etapa, você fez uma edição no serviço do servidor e confirmou a melhoria no modo de comparação do Cloud Profiler.
A seguir
Na próxima etapa, você vai atualizar o código-fonte do serviço do servidor e confirmar a mudança da versão 1.0.0.
7. Etapa extra: confirmar a melhoria no diagrama de cascata do Trace
Diferença entre rastreamento distribuído e criação de perfil contínuo
Na parte 1 do codelab, você confirmou que era possível descobrir o serviço de gargalo nos microsserviços de um caminho de solicitação, mas não a causa exata do gargalo no serviço específico. Neste codelab da Parte 2, você aprendeu que o perfil contínuo permite identificar o gargalo dentro do serviço único das call stacks.
Nesta etapa, vamos analisar o gráfico de cascata do trace distribuído (Cloud Trace) e ver a diferença do perfil contínuo.
Este gráfico de cascata é um dos rastreamentos com a consulta "love". Está levando cerca de 6,7 segundos (6.700 ms) no total.

E isso é depois da melhoria para a mesma consulta. Como você pode ver, a latência total agora é de 1,5 s (1.500 ms), o que é uma grande melhoria em relação à implementação anterior.

O ponto importante aqui é que, no gráfico de cascata de rastreamento distribuído, as informações da pilha de chamadas não estão disponíveis, a menos que você instrumente períodos em todos os lugares. Além disso, os rastreamentos distribuídos se concentram apenas na latência entre os serviços, enquanto a criação de perfil contínuo se concentra nos recursos de computação (CPU, memória, linhas de execução do SO) de um único serviço.
Em outro aspecto, o rastreamento distribuído é a base de eventos, e o perfil contínuo é estatístico. Cada rastreamento tem um gráfico de latência diferente, e você precisa de um formato diferente, como distribuição, para entender a tendência das mudanças de latência.
Resumo
Nesta etapa, você verificou a diferença entre o rastreamento distribuído e o criação de perfil contínuo.
8. Parabéns
Você criou rastreamentos distribuídos com o OpenTelemetry e confirmou as latências de solicitação no microsserviço no Google Cloud Trace.
Para exercícios mais longos, você pode tentar os seguintes tópicos por conta própria.
- A implementação atual envia todos os intervalos gerados pela verificação de integridade. (
grpc.health.v1.Health/Check) Como filtrar esses períodos do Cloud Trace? A dica está aqui. - Correlacione registros de eventos com períodos e saiba como isso funciona no Google Cloud Trace e no Google Cloud Logging. A dica está aqui.
- Substitua um serviço por outro em um idioma diferente e tente instrumentá-lo com o OpenTelemetry para esse idioma.
Se quiser saber mais sobre o criador de perfis depois disso, passe para a parte 2. Nesse caso, você pode pular a seção de limpeza abaixo.
Limpeza
Depois deste codelab, pare o cluster do Kubernetes e exclua o projeto para evitar cobranças inesperadas no Google Kubernetes Engine, no Google Cloud Trace e no Google Artifact Registry.
Primeiro, exclua o cluster. Se você estiver executando o cluster com skaffold dev, basta pressionar Ctrl+C. Se você estiver executando o cluster com skaffold run, execute o seguinte comando:
skaffold delete
Resposta ao comando
Cleaning up... - deployment.apps "clientservice" deleted - service "clientservice" deleted - deployment.apps "loadgen" deleted - deployment.apps "serverservice" deleted - service "serverservice" deleted
Depois de excluir o cluster, no painel de menu, selecione "IAM e administrador" > "Configurações" e clique no botão "ENCERRAR".

Em seguida, insira o ID do projeto (e não o nome) no formulário da caixa de diálogo e confirme o encerramento.