1. Introdução
Última atualização:14/07/2022
Observabilidade do aplicativo
Observabilidade e perfil contínuo
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 traces são a instrumentação fundamental para que o sistema adquira observabilidade.
Além dos três pilares da observabilidade, a criação de perfil contínua é outro componente principal para observabilidade e está expandindo a base de usuários no setor. O Cloud Profiler é um dos criadores e fornece uma interface fácil para detalhar as métricas de desempenho nas pilhas de chamadas do aplicativo.
Este codelab é a segunda parte 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ê aprende a identificar melhor o gargalo dos microsserviços na parte 1.
O que você vai criar
Neste codelab, você vai instrumentar o agente de criação de perfil contínuo no serviço de servidor do aplicativo Shakespeare. (conhecido como Shakesapp) 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 passam a consulta do loadgen para o servidor no gRPC
- O servidor aceita a consulta do cliente, busca todos os trabalhos do Shakespare 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 criador de perfil
- Como investigar o gargalo da garrafa 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 em Go.
- Noções básicas 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ê. Faremos referência a ele mais adiante neste codelab como PROJECT_ID.
Em seguida, você precisará 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 indicados 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 gratuita de US$ 300, o que torna este codelab totalmente gratuito.
Configuração do Google Cloud Shell
Embora o Google Cloud e o Google Cloud Trace possam ser operados remotamente do seu laptop, neste codelab vamos usar 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 . O provisionamento e a conexão ao ambiente devem levar apenas alguns instantes.
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 de idioma do 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ê executará um cluster de microsserviços no Google Kubernetes Engine (GKE). O processo deste codelab é o seguinte:
- Faça o download do projeto de referência no Cloud Shell
- Crie microsserviços em contêineres
- Fazer upload de contêineres no Google Artifact Registry (GAR)
- Implantar contêineres no GKE
- Modificar o código-fonte dos serviços para instrumentação de rastreamento
- Ir para 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ê já pode criar um cluster do Kubernetes.
Crie o 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. Altere o valor da zona us-central1-f
se a região do repositório não cobrir 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 Container Registry para enviar e implantar contêineres. Para estas etapas, precisamos configurar um Artifact Registry (GAR) e skaffold para usá-lo.
Configuração do Artifact Registry
Navegue até o menu do "Artifact Registry". e pressione o botão ATIVAR.
Depois de alguns instantes, você verá o navegador do repositório do GAR. Clique em "CRIAR REPOSITÓRIO". e insira o nome do repositório.
Neste codelab, nomeio o novo repositório como trace-codelab
. O formato do artefato é "Docker" e o tipo é "Região". Escolha a região próxima àquela que você definiu para a zona padrão do Google Compute Engine. Por exemplo, este exemplo escolhe "us-central1-f" acima, então aqui escolhemos "us-central1 (Iowa)". Em seguida, clique no botão "CRIAR" .
Agora você vai encontrar o "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 a criação de microsserviços 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 para ele.
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 encontrar o caminho do registro, acesse o painel do Artifact Registry e clique no nome do repositório que você configurou na etapa anterior.
Em seguida, você verá trilhas de navegação estrutural na parte superior da página. Clique no ícone para copiar o caminho do registro para a área de transferência.
Ao clicar no botão "Copiar", você vê a caixa de diálogo na parte inferior do navegador com a seguinte 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, você precisa configurar o registro para o 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 está tudo pronto para a próxima etapa de configuração de um contêiner do Kubernetes no GKE.
Resumo
Nesta etapa, você vai configurar seu ambiente do codelab:
- Configurar o Cloud Shell
- Criou um repositório do Artifact Registry para o Container Registry
- Configurar o skaffold para usar o Container Registry
- Criou 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, transferir e implantar os microsserviços
Faça o download do material do codelab
Na etapa anterior, configuramos todos os pré-requisitos para este codelab. Agora está tudo pronto para executar microsserviços inteiros neles. O material do codelab está hospedado no GitHub. Faça o download dele para o ambiente do Cloud Shell com o comando git abaixo.
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
- manifestos: 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ê atualizará o código-fonte localizado na pasta step4
. Também é possível consultar o código-fonte em pastas step[1-6]
para conferir as mudanças desde o início. (A Parte 1 abrange as etapas 0 a 4, e a Parte 2 abrange as etapas 5 e 6)
Executar o comando skaffold
Por fim, você já pode criar, enviar e implantar todo o conteúdo no cluster do Kubernetes que acabou de criar. Parece que tem várias etapas, mas o skaffold na verdade faz tudo por você. Vamos tentar fazer isso com o seguinte comando:
cd step4 skaffold dev
Assim que executar o comando, você verá a saída do registro de docker build
e poderá confirmar que eles foram enviados com sucesso para o 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
Após o envio de todos os contêineres de serviço, as implantações do Kubernetes são iniciadas 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, os registros do aplicativo serão emitidos para stdout em cada contêiner, da seguinte forma:
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
Observe que, neste momento, você quer ver todas as mensagens do servidor. Agora está tudo pronto para começar a instrumentar seu aplicativo com o OpenTelemetry para o rastreamento distribuído dos serviços.
Antes de começar a instrumentar o serviço, encerre seu 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 as execuções do skaffold 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 da criação contínua de perfis
Antes de explicar o conceito de criação de perfil contínua, precisamos entender isso primeiro. A criação de perfil é uma das maneiras de analisar o aplicativo dinamicamente (análise dinâmica do programa) e geralmente é realizada durante o desenvolvimento do aplicativo, no processo de teste de carga e assim por diante. Essa é uma atividade de tomada ú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 do perfil, os desenvolvedores os analisam fora do código.
A criação de perfil contínua é a abordagem estendida da criação de perfil normal: ele executa perfis de janela curta no aplicativo de longa duraçã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 e assim por diante. 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 algum back-end que pós-processa os dados estatísticos. Esse é o agente do Cloud Profiler e você vai incorporá-lo ao serviço de servidor em breve.
Incorporar o agente do Cloud Profiler
Pressione o botão no canto superior direito para abrir o Editor do Cloud Shell. Abra
step4/src/server/main.go
no explorador no 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
, há um código de configuração para o OpenTelemetry e o gRPC, o que foi feito na parte 1 do codelab. Agora você vai adicionar a instrumentação para o agente do Cloud Profiler aqui. Assim como fizemos com o initTracer()
, é possível 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 dar uma olhada nas opções especificadas no objeto profiler.Config{}
.
- Serviço: o nome do serviço que pode ser selecionado e ativado no painel do criador de perfil
- ServiceVersion: o nome da versão do serviço. Você pode comparar conjuntos de dados de perfil com base nesse valor.
- NoHeapProfiling: desative a criação do perfil de consumo de memória.
- NoAllocProfiling: desative o perfil de alocação de memória.
- NoGoroutineProfiling: desative a criação do perfil do goroutine
- NoCPUProfiling: desative a criação de perfil da CPU.
Neste codelab, vamos ativar apenas a criação de 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
. Porque profiler.Start()
fica bloqueado, então você precisa executá-lo em outra goroutine. Agora está tudo pronto para a criação. 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, esse gráfico leva alguns minutos para aparecer no Cloud Profiler. Digite "profiler" na caixa de pesquisa na parte de cima e clique no ícone do Profiler.
Você verá o seguinte gráfico de chama.
Resumo
Nesta etapa, você incorporou o agente do Cloud Profiler ao serviço do servidor e confirmou que ele gera um gráfico de chama.
A seguir
Na próxima etapa, você investigará a causa do gargalo no aplicativo com o gráfico de chama.
5. Analisar o gráfico de chama do Cloud Profiler
O que é o Flame Graph?
O gráfico de chama é uma das maneiras de visualizar os dados do perfil. Para uma explicação detalhada, consulte nosso documento, mas o resumo é:
- Cada barra expressa a chamada de método/função no aplicativo
- A direção vertical é a pilha de chamadas. a pilha de chamadas aumenta de cima para baixo
- A direção horizontal é o uso de recursos. quanto mais tempo, pior.
Por isso, vamos analisar o gráfico de chama recebido.
Como analisar o gráfico de chama
Na seção anterior, você aprendeu que cada barra no gráfico em degradê expressa a chamada da função/método, e o comprimento dela significa o uso de recursos na função/método. O gráfico de chama do Cloud Profiler classifica a barra na ordem decrescente ou o comprimento da esquerda para a direita. Você pode começar a olhar primeiro no canto superior esquerdo do gráfico.
No nosso caso, está explícito que grpc.(*Server).serveStreams.func1.2
está consumindo a maior parte do tempo de CPU e que, ao analisar a pilha de chamadas de cima para baixo, isso é gasto na maior parte do tempo em main.(*serverService).GetMatchCount
, que é o gerenciador do servidor gRPC no serviço do servidor.
Em GetMatchCount, há 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 muitos pontos de vista, incluindo o desempenho. Mas o resultado aqui mostra que o uso de recursos de tempo de CPU é alto no regexp.MatchString
e no regexp.Compile
. Considerando esses fatos, supomos que o uso de regexp.MatchString
tem algo a ver com problemas de desempenho. Então, vamos ler o código-fonte onde 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 }
Esse é o lugar em 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 procurar o GoDoc do regexp.
De acordo com o documento, regexp.MatchString
compila o padrão de expressão regular em cada chamada. A causa do grande consumo de recursos é assim.
Resumo
Nesta etapa, você presumiu a causa do consumo de recursos analisando o gráfico de chama.
A seguir
Na próxima etapa, você atualizará o código-fonte do serviço de servidor e confirmará a alteração da versão 1.0.0.
6. Atualizar o código-fonte e diferenciar os gráficos de chama
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 }
Agora, o processo de compilação do padrão regexp é extraído do regexp.MatchString
e removido do loop "for" aninhado.
Antes de implantar esse código, atualize a string da 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 conferir como isso funciona. Implante o cluster com o comando skaffold.
skaffold dev
Depois de um tempo, recarregue o painel do Cloud Profiler para ver o resultado.
Mude a versão para "1.1.0"
para ver apenas os perfis da versão 1.1.0. Como você pode ver, 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).
Além de analisar o gráfico de chama de uma única versão, você também pode comparar as diferenças entre elas.
Mudar o valor de "Comparar com" lista suspensa como "Versão". e alterar o valor de "Versão comparada" para "1.0.0", a versão original.
Você verá esse tipo de gráfico de chama. A forma do gráfico é a mesma que 1.1.0, mas a cor é diferente. No modo de comparação, o que a cor significa é:
- Azul: o valor (consumo de recursos) foi reduzido.
- Laranja: o valor (consumo de recursos) ganho.
- Cinza: neutro
Com base na legenda, vamos analisar a função mais de perto. Clique na barra em que você quer aumentar o zoom para ver mais detalhes na pilha. Clique em main.(*serverService).GetMatchCount
barra. Além disso, ao passar o cursor sobre a barra, você verá os detalhes da comparação.
Ela informa que o tempo total de CPU é reduzido de 5,26s para 2,88s (o total é de 10s = janela de amostragem). Isso é uma grande melhoria!
Agora é possível melhorar o desempenho do aplicativo com a análise dos dados do 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ê atualizará o código-fonte do serviço de servidor e confirmará a alteração da versão 1.0.0.
7. Etapa extra: confirmar a melhoria na hierarquia do Trace
Diferença entre rastreamento distribuído e criação de perfil contínua
Na parte 1 do codelab, você confirmou que podia descobrir o serviço de gargalo nos microsserviços para um caminho de solicitação e que não conseguiu descobrir a causa exata do gargalo no serviço específico. Neste codelab da parte 2, você aprendeu que a criação de perfil contínua permite identificar o gargalo dentro do serviço único das pilhas de chamadas.
Nesta etapa, vamos revisar o gráfico de cascata do trace distribuído (Cloud Trace) e observar a diferença da criação de perfil contínua.
Este gráfico de cascata é um dos traces com a consulta "love". Está levando cerca de 6,7 s (6.700 ms) no total.
E isso ocorre após a melhoria para a mesma consulta. Como você sabe, 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 os períodos em todos os lugares. Além disso, os traces distribuídos se concentram apenas na latência entre os serviços, enquanto a criação de perfil contínua se concentra nos recursos do computador (CPU, memória, linhas de execução do SO) de um único serviço.
Em outro aspecto, o trace distribuído é a base de eventos. O perfil contínuo é estatístico. Cada trace tem um gráfico de latência diferente, e você precisa de um formato diferente, como distribuição, para acompanhar a tendência das mudanças de latência.
Resumo
Nesta etapa, você verificou a diferença entre o trace distribuído e a criação de perfil contínua.
8. Parabéns
Você criou traces distribuídos com o OpenTelemery e confirmou as latências de solicitação no microsserviço no Google Cloud Trace.
Para exercícios prolongados, você pode experimentar os tópicos a seguir por conta própria.
- A implementação atual envia todos os períodos gerados pela verificação de integridade. (
grpc.health.v1.Health/Check
) Como você filtra esses períodos do Cloud Trace? A dica está aqui. - Relacione logs de eventos com períodos e veja como isso funciona no Google Cloud Trace e no Google Cloud Logging. A dica está aqui.
- Substitua alguns serviços por um em outro idioma e tente instrumentá-los com o OpenTelemetry para esse idioma.
Além disso, se você quiser saber mais sobre o criador de perfil depois disso, avance para a parte 2. Nesse caso, pule a seção de limpeza abaixo.
Limpeza
Após este codelab, interrompa o cluster do Kubernetes e exclua o projeto para não receber 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, em seguida, clique em "DESLIGAR" .
Em seguida, insira o ID do projeto (não o nome do projeto) no formulário na caixa de diálogo e confirme o encerramento.