Este post é um dos quatro tutoriais que ajudam você a colocar em prática os conceitos do Microservices de março de 2023: Comece a fornecer microsserviços :
Uma arquitetura de microsserviços traz muitos benefícios, incluindo maior autonomia da equipe e maior flexibilidade em dimensionamento e implantação. Por outro lado, quanto mais serviços houver em um sistema (e um aplicativo de microsserviços pode ter dezenas ou até centenas), mais difícil será manter uma imagem clara da operação geral do sistema. Como escritores e mantenedores de sistemas de software complexos, sabemos da importância vital de ter essa imagem clara. Observabilidade as ferramentas nos dão o poder de construir essa imagem em vários serviços e infraestrutura de suporte.
Neste tutorial, destacamos um tipo muito importante de observabilidade para aplicativos de microsserviços: rastreamento. Antes de começar, vamos definir alguns termos comumente usados ao discutir observabilidade:
Podemos usar todos esses conceitos para obter insights sobre o desempenho dos nossos microsserviços. O rastreamento é uma parte particularmente útil de uma estratégia de observabilidade porque os rastros oferecem uma “visão geral” do que está acontecendo em vários componentes, geralmente fracamente acoplados, quando uma solicitação é feita. Também é uma maneira particularmente eficaz de identificar gargalos de desempenho.
Este tutorial usa o kit de ferramentas de rastreamento do OpenTelemetry (OTel), um padrão de código aberto independente de fornecedor para coleta, processamento e exportação de telemetria que está rapidamente ganhando popularidade. Na concepção da OTel, um rastreamento divide um fluxo de dados, que pode envolver vários serviços, em uma série de “pedaços” organizados cronologicamente que podem ajudar você a entender facilmente:
Se você não conhece o OTel, consulte O que é OpenTelemetry? para uma introdução completa ao padrão e considerações para implementá-lo.
Este tutorial se concentra no rastreamento das operações de um aplicativo de microsserviços com OTel. Nos quatro desafios deste tutorial, você aprenderá a rastrear uma solicitação por meio do seu sistema e responder a perguntas sobre seus microsserviços:
Esses desafios ilustram nosso processo recomendado ao configurar o rastreamento pela primeira vez. Os passos são:
Observação: Nossa intenção neste tutorial é ilustrar alguns conceitos básicos sobre telemetria, não mostrar a maneira correta de implantar microsserviços em produção. Embora use uma arquitetura real de “microsserviços”, há algumas ressalvas importantes:
Este diagrama ilustra a arquitetura geral e o fluxo de dados entre os microsserviços e outros elementos usados no tutorial.
Os dois microsserviços são:
Três peças de infraestrutura de suporte são:
Tirando o OTel de cena por um momento, podemos nos concentrar na sequência de eventos que estamos rastreando: o que acontece quando um usuário envia uma nova mensagem de bate-papo e o destinatário é notificado sobre isso.
O fluxo se divide assim:
Ao mesmo tempo:
Ao configurar a instrumentação de telemetria, é melhor começar com um conjunto de objetivos para instrumentação mais definidos do que “enviar tudo e esperar por insights”. Temos três objetivos principais de telemetria para este tutorial:
Observe que essas metas estão relacionadas tanto à operação técnica do sistema quanto à experiência do usuário.
Para concluir o tutorial em seu próprio ambiente, você precisa:
Um ambiente compatível com Linux/Unix
Observação: As atividades neste tutorial que envolvem o rastreamento do NGINX não funcionam em processadores baseados em ARM porque o módulo OpenTelemetry para NGINX não é compatível. (Isso inclui arquiteturas Linux aarch64 e máquinas Apple com o chip M1 ou M2.) As atividades que envolvem os serviços de mensageiro e notificador funcionam em todas as arquiteturas.
bash
(mas todo o código e comandos são fornecidos e explicados, para que você ainda possa ter sucesso com conhecimento limitado)Node.js 19.x ou posterior
o asdf
para obter exatamente a mesma versão do Node.js usada no tutorial.curl
(já instalado na maioria dos sistemas)Observação: O tutorial usa o JavaScript SDK porque os serviços de mensagens e notificações são escritos em Node.js. Você também configura o recurso de instrumentação automática do OTel (também chamado de autoinstrumentação ) para ter uma ideia do tipo de informação disponível no OTel. O tutorial explica tudo o que você precisa saber sobre o SDK OTel Node.js, mas para mais detalhes, consulte a documentação do OTel .
No seu diretório inicial, crie o diretório microservices-march e clone os repositórios do GitHub para este tutorial nele. (Você também pode usar um nome de diretório diferente e adaptar as instruções adequadamente.)
Observação: Ao longo do tutorial, o prompt na linha de comando do Linux é omitido para facilitar a cópia e colagem dos comandos no seu terminal. O til ( ~
) representa seu diretório inicial.
mkdir ~/microservices-marchcd ~/microservices-march
git clone https://github.com/microservices-march/messenger --branch mm23-metrics-start
git clone https://github.com/microservices-march/notifier --branch mm23-metrics-start
git clone https://github.com/microservices-march/platform --branch mm23-metrics-start
Neste desafio, você inicia o serviço de mensagens e configura a autoinstrumentação OTel para enviar telemetria ao console.
Mude para o repositório da plataforma e inicie o Docker Compose:
cd ~/microservices-march/platformdocker compose up -d --build
Isso inicia o RabbitMQ e o Jaeger, que serão usados em desafios subsequentes.
‑d
instrui o Docker Compose a se desvincular dos contêineres quando eles forem iniciados (caso contrário, os contêineres permanecerão anexados ao seu terminal).--build
instrui o Docker Compose a reconstruir todas as imagens na inicialização. Isso garante que as imagens que você está executando permaneçam atualizadas mesmo com quaisquer alterações potenciais nos arquivos.Mude para o diretório do aplicativo no repositório do messenger e instale o Node.js (você pode substituir por um método diferente, se desejar):
cd ~/microservices-march/messenger/appasdf instalar
Instalar dependências:
instalação npm
Inicie o banco de dados PostgreSQL para o serviço de mensagens :
docker compose up -d
Crie o esquema e as tabelas do banco de dados e insira alguns dados iniciais:
npm executar refresh-db
Com a autoinstrumentação OTel, você não precisa modificar nada na base de código do messenger para configurar o rastreamento. Em vez de ser importado para o próprio código do aplicativo, toda a configuração de rastreamento é definida em um script que é importado para o processo do Node.js em tempo de execução.
Aqui você configura a autoinstrumentação do serviço de mensagens com o destino mais básico para rastreamentos, o console. No Desafio 2 , você alterará a configuração para enviar rastros ao Jaeger como um coletor externo.
Ainda trabalhando no diretório do aplicativo do repositório do messenger , instale os pacotes principais do OTel Node.js:
npm install @opentelemetry/sdk-node@0.36.0 \
@opentelemetry/auto-instrumentations-node@0.36.4
Essas bibliotecas fornecem a seguinte funcionalidade:
@opentelemetry/sdk-node
– Geração e exportação de dados OTel@opentelemetry/auto-instrumentations-node
– Configuração automática com configuração padrão de todas as instrumentações Node.js mais comunsObservação: Uma peculiaridade da OTel é que seus SDKs JavaScript são divididos em partes muito, muito pequenas. Então você instalará mais alguns pacotes apenas para o exemplo básico neste tutorial. Para entender quais pacotes você pode precisar para realizar tarefas de instrumentação além daquelas abordadas neste tutorial, leia os (muito bons) guias de introdução do OTel e dê uma olhada no repositório GitHub do OTel.
Crie um novo arquivo chamado tracing.mjs para conter o código de instalação e configuração para o rastreamento OTel:
rastreamento de toque.mjs
No seu editor de texto preferido, abra tracing.mjs e adicione este código:
//1
importar opentelemetry de "@opentelemetry/sdk-node";
importar { getNodeAutoInstrumentations } de "@opentelemetry/auto-instrumentations-node";
//2
const sdk = new opentelemetry.NodeSDK({
traceExporter: new opentelemetry.tracing.ConsoleSpanExporter(),
instrumentações: [getNodeAutoInstrumentations()],
});
//3
sdk.start();
O código faz o seguinte:
Cria uma nova instância do NodeSDK e a configura para:
ConsoleSpanExporter
).Use o autoinstrumentador como conjunto básico de instrumentação. Esta instrumentação carrega todas as bibliotecas de autoinstrumentação mais comuns. No tutorial os relevantes são:
@opentelemetry/instrumentation-pg
para a biblioteca de banco de dados Postgres ( pg
)@opentelemetry/instrumentation-express
para a estrutura Node.js Express@opentelemetry/instrumentation-amqplib
para a biblioteca RabbitMQ ( amqplib
)Inicie o serviço de mensagens , importando o script de autoinstrumentação que você criou na Etapa 3.
nó --import ./tracing.mjs index.mjs
Depois de um momento, muitas saídas relacionadas ao rastreamento começam a aparecer no console (seu terminal):
...
{
traceId: '9c1801593a9d3b773e5cbd314a8ea89c',
parentId: indefinido,
traceState: indefinido,
nome: 'fs statSync',
id: '2ddf082c1d609fbe',
tipo: 0,
carimbo de data e hora: 1676076410782000,
duração: 3,
atributos: {},
status: { código: 0 },
eventos: [],
links: []
}
...
Observação: Deixe a sessão do terminal aberta para reutilização no Desafio 2.
Existem muitas ferramentas que você pode usar para visualizar e analisar rastros, mas este tutorial usa o Jaeger . Jaeger é uma estrutura de rastreamento distribuída de ponta a ponta , simples e de código aberto, com uma interface de usuário baseada na web integrada para visualizar intervalos e outros dados de rastreamento. A infraestrutura fornecida no repositório da plataforma inclui o Jaeger (você o iniciou na Etapa 1 do Desafio 1), para que você possa se concentrar na análise de dados em vez de lidar com ferramentas complexas.
O Jaeger pode ser acessado no endpoint http://localhost:16686 no seu navegador, mas se você acessar o endpoint agora, não há nada para ver sobre seu sistema. Isso ocorre porque os rastros que você está coletando no momento estão sendo enviados para o console! Para visualizar dados de rastreamento no Jaeger, você precisa exportar os rastreamentos usando o formato do protocolo OpenTelemetry (OTLP).
Neste desafio, você instrumentará o fluxo do usuário principal configurando a instrumentação para:
Como lembrete, usar a autoinstrumentação do OTel significa que você não modifica nada na base de código do messenger para configurar o rastreamento. Em vez disso, toda a configuração de rastreamento está em um script que é importado para o processo Node.js em tempo de execução. Aqui você altera o destino dos rastros gerados pelo serviço de mensagens do console para um coletor externo (Jaeger neste tutorial).
Ainda trabalhando no mesmo terminal do Desafio 1, e no diretório do aplicativo do repositório do messenger , instale o pacote Node.js do exportador OTLP:
npm install @opentelemetry/exporter-trace-otlp-http@0.36.0
A biblioteca @opentelemetry/exporter-trace-otlp-http
exporta informações de rastreamento no formato OTLP via HTTP. É usado ao enviar telemetria para um coletor externo OTel.
Abra o tracing.mjs (que você criou e editou no Desafio 1) e faça estas alterações:
Adicione esta linha ao conjunto de instruções de importação
no topo do arquivo:
importar { OTLPTraceExporter } de "@opentelemetry/exporter-trace-otlp-http";
Altere o “exportador” que você fornece ao SDK OTel do exportador de console usado no Desafio 1 para um que possa enviar dados OTLP por HTTP para um coletor compatível com OTLP. Substituir:
traceExporter:novo opentelemetry.tracing.ConsoleSpanExporter(),
com:
traceExporter: novo OTLPTraceExporter({ cabeçalhos: {} }),
Observação: Para simplificar, o tutorial assume que o coletor reside no local padrão, http://localhost:4318/v1/traces . Em um sistema real, é uma boa ideia definir o local explicitamente.
Pressione Ctrl+c
para interromper o serviço de mensagens , que você iniciou neste terminal na Etapa 4 de Configurar a instrumentação automática do OTel enviada ao console . Em seguida, reinicie-o para usar o novo exportador configurado na Etapa 2:
^cnode --import ./tracing.mjs index.mjs
Inicie uma segunda sessão de terminal separada. (As instruções subsequentes chamam este de terminal do cliente e o terminal original – usado nas etapas 1 e 3 – de terminal do mensageiro .) Aguarde cerca de dez segundos e envie uma solicitação de verificação de integridade ao serviço de mensagens (você pode executar isso algumas vezes se quiser ver vários rastros):
curl -X OBTER http://localhost:4000/health
Esperar dez segundos antes de enviar a solicitação ajuda a tornar seu rastro mais fácil de encontrar, porque ele vem depois dos muitos rastros que a autoinstrumentação gera quando o serviço é iniciado.
Em um navegador, acesse a interface do usuário do Jaeger em http://localhost:16686 e verifique se o exportador OTLP está funcionando conforme o esperado. Clique em Pesquisar na barra de título e, no menu suspenso no campo Serviço , selecione o serviço cujo nome começa com unknown_service . Clique no botão Encontrar rastros :
Clique em um traço no lado direito da janela para exibir uma lista dos intervalos nele contidos. Cada intervalo descreve as operações, às vezes envolvendo vários serviços, que foram executadas como parte do rastreamento. O intervalo jsonParser na captura de tela mostra quanto tempo levou para executar a parte jsonParser
do código de tratamento de solicitações do serviço de mensagens .
Conforme observado na Etapa 5, o nome do serviço exportado pelo OTel SDK ( unknown_service ) não é significativo. Para corrigir isso, no terminal do Messenger pressione Ctrl+c
para interromper o serviço de mensagens . Em seguida, instale mais alguns pacotes do Node.js:
^c
npm install @opentelemetry/semantic-conventions@1.10.0 \
@opentelemetry/resources@1.10.0
Essas duas bibliotecas fornecem a seguinte funcionalidade:
@opentelemetry/semantic-conventions
– Define os atributos padrão para rastros conforme definido na especificação OTel.@opentelemetry/resources
– Define um objeto (recurso) que representa a fonte que gera os dados OTel (neste tutorial, o serviço de mensagens ).Abra tracing.mjs em um editor de texto e faça estas alterações:
Adicione estas linhas ao conjunto de instruções de importação
no topo do arquivo:
importar { Recurso } de "@opentelemetry/resources"; importar { AtributosDeRecursosSemânticos } de "@opentelemetry/convenções-semanticas";
Crie um recurso
chamado messenger
na chave correta na especificação OTel adicionando a seguinte linha após a última instrução de importação
:
const resource = novo Recurso({ [SemanticResourceAttributes.SERVICE_NAME]: "mensageiro",
});
Passe o objeto de recurso
para o construtor NodeSDK adicionando a linha destacada em laranja entre as linhas em preto:
const sdk = new opentelemetry.NodeSDK({ resource, traceExporter: new OTLPTraceExporter({ headers: {} }), instrumentações: [getNodeAutoInstrumentations()], });
Reinicie o serviço de mensagens :
nó --import ./tracing.mjs index.mjs
Aguarde cerca de dez segundos e, no terminal do cliente (que você abriu na Etapa 4), envie outra solicitação de verificação de integridade ao servidor (você pode executar o comando algumas vezes se quiser ver vários rastros):
curl -X OBTER http://localhost:4000/health
Observação: Deixe o terminal do cliente aberto para reutilização na próxima seção e o terminal do mensageiro aberto para reutilização no Desafio 3.
Confirme se um novo serviço chamado messenger aparece na interface do usuário do Jaeger no navegador (isso pode levar alguns segundos e talvez seja necessário atualizar a interface do usuário do Jaeger):
Selecione messenger no menu suspenso Serviço e clique no botão Encontrar rastros para ver todos os rastros recentes originados do serviço de messenger (a captura de tela mostra os 2 mais recentes de 20):
Clique em um traço para exibir os intervalos nele contidos. Cada intervalo é devidamente marcado como originário do serviço de mensagens :
Agora inicie e configure a autoinstrumentação para o serviço notificador , executando basicamente os mesmos comandos das duas seções anteriores para o serviço de mensagens .
Abra uma nova sessão de terminal (chamada de terminal notificador nas etapas subsequentes). Mude para o diretório do aplicativo no repositório do notificador e instale o Node.js (você pode substituir por um método diferente, se desejar):
cd ~/microservices-march/notifier/appasdf install
Instalar dependências:
instalação npm
Inicie o banco de dados PostgreSQL para o serviço notificador :
docker compose up -d
Crie o esquema e as tabelas do banco de dados e insira alguns dados iniciais:
npm executar refresh-db
Instale os pacotes OTel Node.js (para uma descrição do que os pacotes fazem, consulte as Etapas 1 e 3 em Configurar a instrumentação automática do OTel enviada ao console ):
npm install @opentelemetry/auto-instrumentations-node@0.36.4 \
@opentelemetry/exporter-trace-otlp-http@0.36.0 \
@opentelemetry/resources@1.10.0 \
@opentelemetry/sdk-node@0.36.0 \
@opentelemetry/semantic-conventions@1.10.0
Crie um novo arquivo chamado tracing.mjs :
rastreamento de toque.mjs
No seu editor de texto preferido, abra tracing.mjs e adicione o seguinte script para colocar o OTel SDK em funcionamento:
importar opentelemetry de "@opentelemetry/sdk-node";
importar { getNodeAutoInstrumentations } de "@opentelemetry/auto-instrumentations-node";
importar { OTLPTraceExporter } de "@opentelemetry/exporter-trace-otlp-http";
importar { Resource } de "@opentelemetry/resources";
importar { SemanticResourceAttributes } de "@opentelemetry/semantic-conventions";
const resource = new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: "notifier",
});
const sdk = new opentelemetry.NodeSDK({
resource,
traceExporter: new OTLPTraceExporter({ headers: {} }),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.iniciar();
Observação: Este script é exatamente o mesmo que o do serviço de mensagens , exceto que o valor no campo SemanticResourceAttributes.SERVICE_NAME
é notifier
.
Inicie o serviço de notificação com a autoinstrumentação OTel:
nó --import ./tracing.mjs index.mjs
Aguarde cerca de dez segundos e, no terminal do cliente, envie uma solicitação de verificação de integridade ao serviço notificador . Este serviço está escutando na porta 5000 para evitar conflito com o serviço de mensagens escutando na porta 4000:
curl http://localhost:5000/saúde
Observação: Deixe os terminais do cliente e do notificador abertos para reutilização no Desafio 3.
Confirme se um novo serviço chamado notificador aparece na interface do usuário do Jaeger no navegador:
Para o NGINX, você configura o rastreamento manualmente em vez de usar o método de autoinstrumentação OTel. Atualmente, a maneira mais comum de instrumentar o NGINX usando o OTel é usar um módulo escrito em C. Módulos de terceiros são uma parte importante do ecossistema NGINX, mas exigem algum trabalho para serem configurados. Este tutorial faz a configuração para você. Para obter informações básicas, consulte Compilando módulos dinâmicos de terceiros para NGINX e NGINX Plus em nosso blog.
Inicie uma nova sessão de terminal (o terminal NGINX ), altere o diretório para a raiz do repositório do messenger e crie um novo diretório chamado load-balancer , além de novos arquivos chamados Dockerfile , nginx.conf e opentelemetry_module.conf :
cd ~/microservices-march/messenger/mkdir balanceador de carga
cd balanceador de carga
toque Dockerfile
toque nginx.conf
toque opentelemetry_module.conf
No seu editor de texto preferido, abra o Dockerfile e adicione o seguinte (os comentários explicam o que cada linha faz, mas você pode criar e executar o contêiner do Docker sem entender tudo):
DE --platform=amd64 nginx:1.23.1 # Substitua o arquivo nginx.conf pelo nosso próprio COPY nginx.conf /etc/nginx/nginx.conf # Defina a versão do módulo NGINX OTel ARG OPENTELEMETRY_CPP_VERSION=1.0.3 # Defina o caminho de pesquisa para bibliotecas compartilhadas usadas ao compilar e executar o NGINX ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/opentelemetry-webserver-sdk/sdk_lib/lib # 1. Baixe a versão mais recente do modelo Consul e do módulo do servidor web OTel C++, otel-webserver-module ADD https://github.com/open-telemetry/opentelemetry-cpp-contrib/releases/download/webserver%2Fv${OPENTELEMETRY_CPP_VERSION} /opentelemetry-webserver-sdk-x64-linux.tgz /tmp EXECUTE apt-get update \ && apt-get install -y --no-install-recommends dumb-init unzip \ # 2. Extraia os arquivos do módulo && tar xvfz /tmp/opentelemetry-webserver-sdk-x64-linux.tgz -C /opt \ && rm -rf /tmp/opentelemetry-webserver-sdk-x64-linux.tgz \ # 3. Instale e adicione a diretiva 'load_module' no topo do arquivo de configuração principal do NGINX && /opt/opentelemetry-webserver-sdk/install.sh \ && echo "load_module /opt/opentelemetry-webserver-sdk/WebServerModule/Nginx/1.23.1/ngx_http_opentelemetry_module.so;\n$(cat /etc/nginx/nginx.conf)" > /etc/nginx/nginx.conf # 4. Copiar no arquivo de configuração para o módulo NGINX OTel COPY opentelemetry_module.conf /etc/nginx/conf.d/opentelemetry_module.conf EXPOSE 8085 STOPSIGNAL SIGQUIT
Abra nginx.conf e adicione o seguinte:
eventos {}
http {
include /etc/nginx/conf.d/opentelemetry_module.conf;
mensageiro upstream {
servidor localhost:4000;
}
servidor {
listen 8085;
localização / {
proxy_pass http://messenger;
}
}
}
Este arquivo de configuração NGINX extremamente básico informa ao NGINX para:
Observação: Isso é bem próximo da configuração real do NGINX como um proxy reverso e balanceador de carga em um ambiente de produção. A única grande diferença é que o argumento para a diretiva server
no bloco upstream
geralmente é um nome de domínio ou endereço IP em vez de localhost
.
Abra opentelemetry_module.conf e adicione o seguinte:
NginxModuleEnabled LIGADO;NginxModuleOtelSpanExporter otlp;
NginxModuleOtelExporterEndpoint localhost:4317;
NginxModuleServiceName messenger-lb;
NginxModuleServiceNamespace MicroservicesMarchDemoArchitecture;
NginxModuleServiceInstanceId DemoInstanceId;
NginxModuleResolveBackends LIGADO;
NginxModuleTraceAsError LIGADO;
Crie uma imagem Docker contendo o NGINX e o módulo NGINX OTel:
docker build -t messenger-lb .
Inicie o contêiner Docker para o proxy reverso NGINX e o balanceador de carga:
docker run --rm --name messenger-lb -p 8085:8085 --network="host" messenger-lb
No terminal do cliente, envie uma solicitação de verificação de integridade ao serviço de mensagens por meio do proxy reverso NGINX e do balanceador de carga (não há necessidade de esperar antes de enviar esta solicitação):
curl http://localhost:8085/saúde
Observação: Deixe os terminais NGINX e do cliente abertos para reutilização no Desafio 3.
No navegador, confirme se o novo serviço messenger-lb está listado na interface do usuário do Jaeger junto com os serviços que você iniciou anteriormente. Pode ser necessário recarregar a interface do usuário do Jaeger no seu navegador.
Em Arquitetura e Fluxo do Usuário , descrevemos os estágios do fluxo do usuário, mas para recapitular:
E os objetivos da implementação da telemetria são:
Neste desafio, você aprenderá a avaliar se os traços gerados pela instrumentação OTel atendem aos objetivos mencionados acima. Primeiro, você testa o sistema e cria alguns rastros . Em seguida, você inspeciona o rastreamento de um fluxo de mensagens e as seções dele geradas pelo NGINX , o serviço de mensagens e o serviço de notificação .
No terminal do cliente, configure uma conversa e envie algumas mensagens entre dois usuários:
curl -X POST \
-H "Tipo de conteúdo: application/json" \
-d '{"participant_ids": [1, 2]}' \
'http://localhost:8085/conversations'
curl -X POST \
-H "ID do usuário: 1" \
-H "Tipo de conteúdo: application/json" \
-d '{"content": "Esta é a primeira mensagem"}' \
'http://localhost:8085/conversations/1/messages'
curl -X POST \
-H "User-Id: 2" \
-H "Tipo de conteúdo: application/json" \
-d '{"content": "Esta é a segunda mensagem"}' \
'http://localhost:8085/conversations/1/messages'
Uma saída como a seguinte é gerada pelo serviço notificador e aparece no terminal do notificador:
Nova_mensagem recebida: {"type":"new_message","channel_id":1,"user_id":1,"index":1,"participant_ids":[1,2]}Enviando notificação de nova mensagem via sms para 12027621401
Nova_mensagem recebida: {"type":"new_message","channel_id":1,"user_id":2,"index":2,"participant_ids":[1,2]}
Enviando notificação de nova mensagem via e-mail para the_hotstepper@kamo.ze
Enviando notificação de nova mensagem via sms para 19147379938
Abra a interface do usuário do Jaeger no navegador, selecione messenger-lb no menu suspenso Serviço e clique no botão Encontrar rastros . Uma lista de rastros é exibida, começando no início do fluxo. Clique em qualquer traço para exibir detalhes sobre ele, como nesta captura de tela:
Clique e explore um pouco. Antes de prosseguir, considere como as informações nos rastros apoiam seus objetivos de instrumentação, conforme listados na introdução do Desafio 3. Perguntas pertinentes incluem:
Comece com o intervalo NGINX, que tem 11 intervalos filho dentro do intervalo pai. Como a configuração atual do NGINX é muito simples, os intervalos filho não são muito interessantes e simplesmente mostram o tempo gasto em cada etapa do ciclo de vida de processamento de solicitações do NGINX. No entanto, o intervalo pai (o primeiro) contém alguns insights interessantes:
Em Tags você vê os seguintes atributos:
http.method
– POST
(em termos REST significa criação)http.status_code
–201
(indicando criação bem-sucedida)http.target
– conversations/1/messages
(o ponto final da mensagem)Juntas, essas três informações se combinam para dizer: “Uma solicitação POST
foi enviada para /conversations/1/messages
e a resposta foi201
(criado com sucesso)”. Isso corresponde às etapas 1 e 4a em Arquitetura e fluxo do usuário ).
webengine.name
mostra que esta é a parte NGINX da solicitação.Além disso, como os intervalos para messenger e notifier estão aninhados dentro do intervalo messenger-lb conversations/1
(conforme mostrado na captura de tela em Prepare-se para ler os rastros ), você pode dizer que a solicitação enviada ao serviço de messenger por meio do proxy reverso NGINX atingiu todos os componentes esperados no fluxo.
Essas informações atendem ao objetivo porque você pode ver que o proxy reverso NGINX fazia parte do fluxo.
Na lista de intervalos rotulados como messenger-lb , observe o intervalo mais recente (está no final da lista) para ver quanto tempo levou a parte NGINX da solicitação. Na captura de tela, o intervalo começou em 589 microssegundos (µs) e durou 24µs, o que significa que a operação completa do proxy reverso levou apenas 613µs – cerca de 0,6 milissegundos (ms). (Os valores exatos serão diferentes quando você executar o tutorial.)
Em uma configuração como essa, na maioria das vezes os valores são úteis apenas em relação a outras medições e variam entre os sistemas. Neste caso, porém, esta operação claramente não corre o risco de se aproximar dos cinco segundos de duração.
Essas informações satisfazem o objetivo porque você pode ver que as operações do NGINX não levaram nem perto de cinco segundos. Se houver uma operação muito lenta no fluxo, ela deve estar acontecendo mais tarde.
A camada de proxy reverso do NGINX não inclui nenhuma informação sobre isso, então você pode passar para os intervalos do messenger .
A seção de serviço de mensagens do rastreamento contém outros 11 intervalos. Novamente, a maioria dos intervalos filho diz respeito às etapas básicas que o framework Express usa ao processar uma solicitação e não são muito interessantes. No entanto, o intervalo pai (o primeiro) novamente contém alguns insights interessantes:
Em Tags você vê os seguintes atributos:
http.method
– POST
(novamente, em termos REST, isso significa criação)http.route
– /conversations/:conversationId/messages
(a rota da mensagem)http.target
– /conversations/1/messages
(o ponto final da mensagem)Essas informações satisfazem o objetivo porque nos mostram que o serviço de mensagens fazia parte do fluxo e que o ponto final atingido era o novo ponto final da mensagem.
Conforme indicado na captura de tela a seguir, a parte do mensageiro do rastreamento começou em 1,28 ms e terminou em 36,28 ms, para um tempo total de 35 ms. A maior parte desse tempo foi gasto analisando JSON ( middleware
-
jsonParser
) e, em maior extensão, conectando-se ao banco de dados ( pg-pool.connect
e tcp.connect
).
Isso faz sentido, já que diversas consultas SQL também são feitas no processo de escrita da mensagem. Isso, por sua vez, sugere que você pode querer aumentar a configuração de autoinstrumentação para capturar o tempo dessas consultas. (O tutorial não mostra essa instrumentação adicional, mas no Desafio 4 você cria manualmente intervalos que podem ser usados para encapsular consultas de banco de dados.)
Essa informação satisfaz o objetivo porque mostra que as operações do mensageiro não levam nem perto de cinco segundos. Se houver uma operação muito lenta no fluxo, ela deve estar acontecendo mais tarde.
Assim como os intervalos do NGINX, os intervalos do mensageiro não incluem essas informações, então você pode passar para os intervalos do notificador .
A seção do notificador do rastreamento contém apenas dois intervalos:
do processo
chat_queue
– Confirma que o serviço notificador processou um evento da fila de mensagens chat_queuepg-pool.connect
– Mostra que após processar o evento o serviço notificador fez algum tipo de conexão com seu banco de dadosAs informações disponíveis nesses períodos cumprem apenas parcialmente o objetivo de compreender cada etapa. Você pode ver que o serviço notificador chegou ao ponto de consumir o evento da fila, mas não sabe se:
Isso indica que você precisa fazer o seguinte para entender completamente o fluxo do serviço do notificador :
Observando o tempo geral dos intervalos do serviço do notificador , você vê que a solicitação passou 30,77 ms na seção do notificador do fluxo. No entanto, como não há intervalos que sinalizem o “fim” de todo o fluxo (o envio de notificações ao destinatário), não é possível determinar o tempo total desta seção do fluxo ou o tempo geral de conclusão da operação.
No entanto, você pode ver que um intervalo de processo
chat_queue
para o serviço notificador começou em 6,12 ms, 2 ms depois que um intervalo de envio
chat_queue
começou para o serviço de mensagens em 4,12 ms.
Essa meta é atingida porque você sabe que o notificador consumiu um evento 2 ms depois que ele foi despachado pelo serviço de mensagens . Diferentemente da meta 2, para atingir essa meta você não precisa saber se o evento foi totalmente processado ou quanto tempo isso levou.
Com base na nossa análise dos traços produzidos pela atual autoinstrumentação OTel, fica claro que:
Muitos desses vãos não são úteis em sua forma atual:
manipulador
de solicitações
e todas as operações de banco de dados. Alguns dos intervalos de middleware (como expressInit
e corsMiddleware
) não parecem relevantes e podem ser removidos.Faltam vãos críticos para o seguinte:
Isto significa que a instrumentação básica cumpre o último objetivo:
Entretanto, não há informações suficientes para cumprir os dois primeiros objetivos:
Neste desafio, você otimiza a instrumentação OTel com base nas análises de traços feitas no Desafio 3. Isso inclui remover spans desnecessários , criar novos spans personalizados e confirmar que o evento consumido pelo serviço notificador é o mesmo gerado pelo serviço de mensagens.
No seu editor de texto preferido, abra o arquivo tracing.mjs no diretório do aplicativo do repositório do messenger e adicione o seguinte no final da lista de instruções de importação na parte superior:
const IGNORED_EXPRESS_SPANS = novo Conjunto([ "middleware - expressInit",
"middleware - corsMiddleware",
]);
Isso define um conjunto de nomes de intervalos, derivados da lista de intervalos mostrada na captura de tela a seguir da interface do usuário do Jaeger, a serem omitidos do rastreamento porque não fornecem informações úteis para esse fluxo. Você pode decidir que outros intervalos listados na captura de tela também não são necessários e adicioná-los à lista de IGNORED_EXPRESS_SPANS
.
Adicione filtros à configuração de autoinstrumentação para omitir intervalos que você não deseja, alterando a linha destacada em laranja:
const sdk = new opentelemetry.NodeSDK({ resource, traceExporter: new OTLPTraceExporter({ headers: {} }), instrumentações: [getNodeAutoInstrumentations()], });
para isso:
const sdk = new opentelemetry.NodeSDK({ resource, traceExporter: new OTLPTraceExporter({ headers: {} }), instrumentações: [ getNodeAutoInstrumentations({ "@opentelemetry/instrumentation-express": { ignoreLayers: [ (name) => { return IGNORED_EXPRESS_SPANS.has(name); }, ], }, }), ], });
A função getNodeAutoInstrumentations
faz referência ao conjunto de intervalos definidos na Etapa 1 para filtrá-los do rastreamento gerado por @opentelemetry/instrumentation-express
. Em outras palavras, a instrução return
é resolvida como verdadeira
para um intervalo que pertence a IGNORED_EXPRESS_SPANS
, e a instrução ignoreLayers
remove esse intervalo do rastreamento.
No terminal do messenger, pressione Ctrl+c
para interromper o serviço de messenger . Em seguida, reinicie-o:
^cnode --import ./tracing.mjs index.mjs
Aguarde cerca de dez segundos e, no terminal do cliente, envie uma nova mensagem:
curl -X POST \ -H "ID do usuário: 2" \
-H "Tipo de conteúdo: application/json" \
-d '{"content": "Esta é a segunda mensagem"}' \
'http://localhost:8085/conversations/1/messages'
Verifique novamente os spans do messenger na IU do Jaeger. Os dois spans do middleware
, expressInit
e corsMiddleware
, não aparecem mais (você pode comparar com a captura de tela do Objetivo 2 de Examine a Seção do messenger do Trace no Desafio 3.
Nesta seção, você toca no código do aplicativo pela primeira vez. A autoinstrumentação gera uma grande quantidade de informações sem exigir alterações no aplicativo, mas alguns insights só são possíveis pela instrumentação de partes específicas da lógica de negócios.
Para o novo fluxo de mensagens que você está instrumentando, um exemplo disso é rastrear o envio de notificações para o destinatário da mensagem.
Abra index.mjs no diretório do aplicativo do repositório do notificador . Este arquivo contém toda a lógica de negócios do serviço. Adicione a seguinte linha no final da lista de instruções de importação
no topo do arquivo:
importar { trace } de "@opentelemetry/api";
Substitua este código (aproximadamente na linha 91 do arquivo):
para (deixe pref de preferências) { console.log( `Enviando notificação de nova mensagem via${pref.address_type} para${pref.address} ` ); }
com:
const tracer = trace.getTracer("notifier"); // 1tracer.startActiveSpan( // 2 "notification.send_all", { attributes: { user_id: msg.user_id, }, }, (parentSpan) => { for (let pref of preferences) { tracer.startActiveSpan( // 3 "notification.send", { attributes: { // 4 notification_type: pref.address_type, user_id: pref.user_id, }, }, (span) => { console.log( `Enviando notificação de nova mensagem via${pref.address_type} para${pref.address} ` ); span.end(); // 5 } ); } parentSpan.end(); // 6 } );
O novo código faz o seguinte:
rastreador
, que é um objeto global para interagir com rastros OTel.notification.send_all
e define o atributo user_id
para identificar o remetente da mensagem.notification.send
é criado em notification.send_all
. Cada notificação gera um novo intervalo.Define mais atributos para o intervalo filho:
notification_type
– Um de sms
ou e-mail
user_id
– O ID do usuário que recebe a notificaçãode notificação
filho.notification.send_all
.Ter um intervalo pai garante que cada operação de “enviar notificação” seja relatada, mesmo que nenhuma preferência de notificação seja descoberta para o usuário.
No terminal do notificador, pressione Ctrl+c
para interromper o serviço do notificador . Em seguida, reinicie-o:
^cnode --import ./tracing.mjs index.mjs
Aguarde cerca de dez segundos e, no terminal do cliente, envie uma nova mensagem:
curl -X POST \ -H "ID do usuário: 2" \
-H "Tipo de conteúdo: application/json" \
-d '{"content": "Esta é a segunda mensagem"}' \
'http://localhost:8085/conversations/1/messages'
Verifique novamente os spans do notificador na IU do Jaeger. Você vê o span pai e dois spans filhos, cada um com uma operação de “enviar notificação”:
Agora você pode atingir totalmente o primeiro e o segundo objetivos, porque pode ver todas as etapas pelas quais uma solicitação passa durante o novo fluxo de mensagens. Os tempos em cada intervalo expõem qualquer atraso entre qualquer uma dessas etapas.
Há mais uma coisa que você precisa para ter uma visão completa do fluxo. O evento que está sendo processado pelo serviço notificador é, na verdade, o mesmo que foi despachado pelo serviço de mensagens ?
Você não precisa fazer nenhuma alteração explícita para conectar os dois traços – mas também não quer confiar cegamente na mágica da autoinstrumentação.
Com isso em mente, adicione algum código de depuração rápido para verificar se o rastreamento que começa no serviço NGINX é realmente o mesmo (tem o mesmo ID de rastreamento) que aquele consumido pelo serviço notificador .
Abra o arquivo index.mjs no diretório do aplicativo do repositório do messenger e faça estas alterações:
Adicione a seguinte linha no final da lista de instruções de importação na parte superior:
importar { trace } de "@opentelemetry/api";
Adicione as linhas destacadas em laranja abaixo da linha existente em preto:
função assíncrona createMessageInConversation(req, res) { const tracer = trace.getActiveSpan(); console.log("TRACE_ID: ", tracer.spanContext().traceId);
As novas linhas imprimem o TRACE_ID
de dentro da função no messenger que manipula a criação de uma nova mensagem.
Abra o arquivo index.mjs no diretório do aplicativo do repositório do notificador e adicione a linha destacada em laranja abaixo da linha existente em preto:
exportar função assíncrona handleMessageConsume(canal, msg, manipuladores) { console.log("RABBIT_MQ_MESSAGE: ", msg);
A nova linha imprime o conteúdo completo do evento AMQP recebido pelo serviço notificador .
Pare e reinicie os serviços do messenger e do notifier executando estes comandos nos terminais do messenger e do notifier:
^cnode --import ./tracing.mjs index.mjs
Aguarde cerca de dez segundos e, no terminal do cliente, envie uma mensagem novamente:
curl -X POST \ -H "ID do usuário: 2" \
-H "Tipo de conteúdo: application/json" \
-d '{"content": "Esta é a segunda mensagem"}' \
'http://localhost:8085/conversations/1/messages'
Veja os logs dos serviços de mensagens e notificações . O log do serviço de mensagens inclui uma linha como esta que relata o ID de rastreamento de uma mensagem (o ID real será diferente quando você executar o tutorial):
ID_DO_RASTREIO: 29377a9b546c50be629c8e64409bbfb5
Da mesma forma, o log do serviço notificador relata um ID de rastreamento na saída como este:
_spanContext: {
traceId: '29377a9b546c50be629c8e64409bbfb5', spanId: 'a94e9462a39e6dbf', traceFlags: 1,
traceState: indefinido
},
Os IDs de rastreamento correspondem no console, mas como uma etapa final, você pode compará-los ao ID de rastreamento na IU do Jaeger. Abra a IU no endpoint de ID de rastreamento relevante (o seu será diferente, mas neste exemplo é http://localhost:16686/trace/29377a9b546c50be629c8e64409bbfb5 ) para ver o rastreamento inteiro. O traço de Jaeger confirma que:
Observação: Em um sistema de produção real, você removeria o código adicionado nesta seção depois de confirmar que o fluxo está funcionando conforme o esperado.
Você criou alguns contêineres e imagens ao longo deste tutorial! Use estas instruções para removê-los.
Para remover qualquer contêiner Docker em execução:
docker rm $(docker stop messenger-lb)
Para remover o serviço de plataforma e os serviços de banco de dados do messenger e do notifier :
cd ~/microservices-march/plataforma && docker compose down
cd ~/microservices-march/notificador && docker compose down
cd ~/microservices-march/mensageiro && docker compose down
Parabéns, você concluiu o tutorial!
E, no entanto, você mal arranhou a superfície de como seria uma configuração de rastreamento ideal! Em um ambiente de produção, você pode querer adicionar coisas como spans personalizados para cada consulta de banco de dados e metadados adicionais em todos os spans descrevendo detalhes de tempo de execução, como o ID do contêiner de cada serviço. Você também pode implementar os outros dois tipos de dados OTel (métricas e registro) para dar a você uma imagem completa da saúde do seu sistema.
Para continuar sua educação em microsserviços, confira Microservices março de 2023. Na Unidade 4: Gerencie o caos e a complexidade dos microsserviços com observabilidade , você aprenderá sobre as três principais classes de dados de observabilidade, a importância da infraestrutura e do alinhamento de aplicativos e maneiras de começar a analisar dados profundos.
"Esta postagem do blog pode fazer referência a produtos que não estão mais disponíveis e/ou não têm mais suporte. Para obter as informações mais atualizadas sobre os produtos e soluções F5 NGINX disponíveis, explore nossa família de produtos NGINX . O NGINX agora faz parte do F5. Todos os links anteriores do NGINX.com redirecionarão para conteúdo semelhante do NGINX no F5.com."