Vamos fazer já já um hands-on de tracing, mas queria trazer alguns pontos aqui antes para garantir que estamos pisando no mesmo terreno de conhecimento. Distributed tracing, tracing distribuído, se popularizou muito em 2010, em volta de 2010, por conta do advento dos sistemas distribuídos. Então, onde você tinha o monolito, agora você começava a ter serviços espalhados, pulverizados com seu domínio isolado buscando uma separação de responsabilidades hoje isso ainda é muito mais presente por conta das arquiteturas mais novas de micro serviços o que você já tinha de forma pulverizada agora você tem muito mais e como saber se você tem algum tipo de problema como gargalo, ou como você saber qual componente está dando algum tipo de erro, e isso está cascateando por toda a sua infraestrutura. Então, é isso que eu quero trazer, garantir que a gente está pisando no mesmo terreno de conhecimento. E vamos conhecer algumas terminologias para trabalhar com o serviço de tracing, em especial com o serviço do Jaeger. Imagina que você tem a seguinte integração aqui, o serviço A que se comunica com o serviço B e o B se comunica com o serviço C e com o serviço D e aí a comunicação flui dessa ordem aqui e aí voltando para o serviço A, no passo final ele se comunica com o serviço E. O que a gente tem aqui que norteia essa comunicação são os dados de contexto que são passados em cada uma dessas comunicações e, claro, o serviço de borda que foi o serviço inicial, o pioneiro dessa comunicação. foi o serviço inicial, o pioneiro dessa comunicação. No serviço de Tracing, em especial o Jaeger, você tem a linha do tempo e os dados são estruturados da seguinte maneira. A comunicação é definida como Spans, onde você tem cada serviço aqui como um span e esse span ele vai trazer claro uma inter-relação entre os serviços que estão sendo chamados mas essa comunicação ela é definida graficamente como um span e aí você tem o trace que é informado logo no início se a primeira requisição não tem o trace id é informado logo no início se a primeira requisição não tem o trace ID o ID único, ele é gerado e ele sabe que o trace começa a partir dali e aí uma vez definindo agora pelo trace eu consigo correlacionar cada uma dessas requisições em uma única visualização vamos entender no detalhe como como os dados são passados, informação de contexto, por exemplo, definição do trace ID. Vou trazer um exemplo aqui. De novo, você tem uma aplicação, um serviço A, esse serviço se integra ao serviço B, componente B, e o componente B se integra ao C, por exemplo, tá? Apenas pra ilustração. Cada um desses componentes, esses serviços, vai ser retratado como Spams. E aí você tem cada um desse serviço vai ter um ID de Spam naquela comunicação, naquele Trace especificamente, naquele rastro. E aí você tem o serviço é o primeiro serviço com spam por exemplo ABC123 serviço segundo aqui é o spam vai ser definido o IDDF456 e o último serviço aqui o spam GHI789 por. Quando é feita a primeira comunicação, a primeira integração, você não tem o Trace ID. Então, o serviço de tracing precisa primeiro definir um Trace ID para aquela comunicação. Uma vez definido, ele olha o seguinte, no cabeçalho da requisição eu tenho esse Trace ID, se não, aí ele define o primeiro Trace ID, o chave, o principal. Uma vez definido esse Trace ID, esse Trace ID flui de serviço para serviço no processo de integração, de intercomunicação, de comunicação interprocessos. O Trace ID é passado para a comunicação entre o primeiro serviço ao próximo, com o parent ID apontando para o serviço chamador, para o serviço anterior. Chegou no serviço do meio, ele faz a mesma coisa. Ele pega aquela requisição, ele pega aqueles dados de contexto, e ao chamar o serviço C ele passa o trace ID original e o parent ID é o dado dele mesmo ou seja, o span ID do chamador que no caso é o serviço do meio invocando o terceiro serviço e esses dados é o que a gente chama de trace context ou dados de contexto de comunicação isso. E esses dados é o que a gente chama de Trace Context, ou dados de contexto de comunicação. E com isso, esses dados, uma vez capturados e armazenados na base do serviço de Tracing, você consegue agora fazer correlações, buscas, etc. Talvez você que está assistindo tenha tido pouco contato com essa solução, visto muito pouco sobre serviço de Tracing distribuído, e talvez ainda tenha uma pergunta em si que é, qual é o benefício disso? No que isso pode de fato ajudar? Qual é o diferencial dessa solução? Um dos benefícios é a detecção de gargalo. Imagina que você tem essa topologia em que você tem um serviço que chama outro serviço, esse serviço bifurca onde ele chama um outros serviços aqui filhos e esses demais se integram com os seus respectivos, enfim, componentes, tem as suas dependências também. Supondo que nesse caminho aqui debaixo o último serviço da ponta retorna em 40 milissegundos. O tempo total desse serviço aqui em verde é o tempo que ele leva para processar somado ao tempo dos seus componentes. Aqui eu não estou entrando no critério de paralelismo, de multitrading, não. Imagina que o serviço ele chama de forma cascateada, enfim, aqui eu também não estou entrando no mérito também se ele chama dentro de um laço, de um loop, ele tem um loop e ele tem que chamar várias vezes o serviço externo, não, não estou entrando nesse critério, está nesse mérito, apenas para ilustração de forma simplificada. Então, o tempo mínimo daquele serviço, desse serviço aqui em verde, ele é pelo menos a soma do tempo que ele leva para processar, ou seja, o tempo interno dele, com pelo menos uma chamada do serviço aqui na ponta do qual ele depende. Então, pelo menos 100 milissegundos a latência desse serviço. Olhando aqui para cima, a mesma coisa. O serviço da ponta, supondo que ele retorne os dados em 20 milissegundos, e lembrando que não precisa necessariamente ser uma API, um microserviço, pode ser até um banco de dados, por exemplo. O serviço que chama esse componente aqui, ele tem o mesmo mesmo conceito. Esse tempo de latência, ele vai ser agregado à latência do serviço que está chamando. O problema é que, supondo que esse serviço tenha algum problema, ele chama de repente esse serviço em várias vezes ou ele tem algum problema interno de fato memory leak, alguma coisa que pode estar trazendo alguma alguma degradação de performance nele, você não sabe, a causa raiz não sabe o motivo, mas você sabe que pelo serviço de tracing ele começa a apresentar um tempo de resposta superior ao que a gente entende que é o ideal. Exemplo aqui, ele está retornando em 1 segundo e 20 milissegundos. Então, além do tempo dos serviços dependentes, vou agregar o tempo que ele está levando para processar. E aí, imagina que é uma linha de produção. Se esse serviço começa a trazer impacto, enfim, ele tem uma resposta degradada, esse impacto vai ser refletido e propagado em toda a sua topologia. Então você tem agora esse serviço que chama esses serviços aqui embaixo, então esse de baixo e o que está com a performance degradada. E, de novo, o tempo dele vai ser agregado, o tempo de resposta total vai ser agregado a esse serviço aqui do meio. E aí, aqui é um problema, porque ele não vai deixar de receber requisições da ponta, da extremidade do Edge Service. Ele vai continuar recebendo requisições, porém ele vai demorar muito tempo e isso pode acabar congestionando esse serviço aqui. Ele vai ficar, por um lado, ele acaba tendo uma ociosidade porque ele vai ficar esperando o I.O. de rede aqui da infraestrutura por conta do tempo de resposta, ele vai ficar esperando. Por outro lado lado ele vai ficar congestionando enfileirando várias requisições daqui da ponta, e aí que você começa a identificar problemas na sua topologia, então o tempo de resposta ele vai ser agregado e aí no final você pode acabar trazendo impacto vou trazer ainda mais um problema. Imagina que você implementou um controle de SLA um pouco mais rígido, então você determinou um timeout muito rígido para o tempo de resposta das aplicações. Supondo que esse tempo tenha expirado, tenha rolado um timeout de fato, você pode acabar gerando outros erros. Então, o serviço pode entender, né, esse serviço aqui, mais do meio, pode entender que esse que tá aqui em cima em vermelho, ele começou, ele tá indisponível pelo tempo que ele tá levando pra responder, e aí ele entender, olha, deu time out, o serviço tá indisponível, esse erro vai ser propagado até a ponta para quem está chamando. Uma outra situação é controle de erros. Imagina que você vai ter muitos serviços rodando simultaneamente e a solução de Tracing distribuído permite você reduzir o raio de busca em relação aos serviços que estão com defeito. Numa arquitetura de microserviços, em determinadas empresas, reduzir o raio de busca em relação ao serviço que está com defeito. Numa arquitetura de microserviços, em determinadas empresas, pode ser que você tenha em torno de 200, 300, até 1.000 serviços, 1.000 aplicações rodando para atender um negócio específico, enfim, um propósito específico. Saber qual serviço está com defeito é exaustivo. E o serviço de tracing consegue ajudar a gente a reduzir o raio de busca para aquele que está trazendo de fato problema. Então nesse cenário, imagine que você tem a mesma topologia, o serviço aqui, alguns serviços retornando sempre ok, mas de novo aquele serviço está com erro, e aí por que que deu erro? Pode ser, enfim, vários problemas, você não sabe ainda a causa raiz a causa raiz você vai identificar através do serviço de login enfim, de repente você consegue antecipar a detecção desse problema com um bom serviço de métricas, com alarming o ponto é, você não sabe ainda o motivo, pode ser memory leak pode ser volume muito alto de requisições problema de rede, de network problema de rede, de network, problema de disco, de storage, enfim. Pode ser vários problemas. Então, ele apresentou erro e esse erro vai ser propagado em toda a sua arquitetura. E o último conceito é o conceito de sampling, ou amostragem. Quero garantir que estamos pisando também no mesmo terreno de conhecimento nesse tema antes de avançarmos para o hands-on. Qual que é o ponto do sampling e por que ele é importante para a topologia de arquitetura com Tracing distribuído e habilitado? Todos os eventos que você capturar da aplicação, ele vai ocupar espaço em disco, storage, e pior, vai consumir banda de rede. Ou seja, são recursos que, se você está no mention premises ou um pouco mais com recursos limitados, enfim, são componentes que você precisa controlar a escassez deles. E aí o conceito de sampling promove justamente esse equilíbrio, de forma que você capture o máximo de eventos que você entender que é importante e relevante, mas não consumir tanta largura de banda, de rede da empresa ou da sua cloud, da sua infraestrutura na nuvem, por exemplo. E aí, qual que é o problema disso? Porque pode dar a sensação de que você está perdendo dados. Alguns eventos de tracing, por exemplo, eles não serão capturados. E aí pode dar essa sensação de que, onde estão os dados de tracing da minha aplicação? E quais são as estratégias de sampling que o Jaeger, por exemplo, adota? O Jaeger adota, por exemplo, a primeira é o constante. É onde você define um valor constante para a captura dos dados. Supondo que você defina o parâmetro 1, por exemplo, ele vai capturar todos os eventos ou 0, capturar nada. Probabilístico significa que você tem uma randomicidade para a captura dos eventos. E aí, com base nesse critério de randomicidade, de probabilidade, a solução, o serviço de captura dos dados, ele define se captura aquele evento de tracing, aquele rastro ou não. Supondo que você defina um valor de 0.1, então significa que de 1 entre 10 eventos será gravado, será capturado e registrado no serviço do Jaeger. Os demais serão descartados. A terceira estratégia é o Rate Limit. Aqui você define uma taxa, uma frequência pré-definida, um ritmo para a captura dos eventos. Então, supondo que você queira definir que dentro de um segundo, você quer sempre que 10 eventos sejam capturados, ou 2 eventos sejam capturados, você define esse parâmetro 2 no parâmetro específico. E o último é o remoto. no parâmetro específico. E o último é o remoto. É onde você determina que você delega a responsabilidade para o serviço do Jäger decidir qual é a... como que ele vai... enfim, qual a estratégia que ele vai adotar para cada serviço especificamente. É interessante essa última estratégia, porque agora você pode adotar uma configuração centralizada. Em vez de utilizar uma solução por serviço de forma isolada. Importante lembrar que cada estratégia, uma vez adotada uma estratégia para comunicação, ela é propagada em toda a comunicação, em toda a integração. Estou pegando o exemplo que eu trouxe aqui do serviço A, se comunicando com B, se comunicando com C. A estratégia de sampling é adotada no início da comunicação, logo na entrada. Uma vez determinando essa estratégia, ele propaga isso para as demais comunicações, e por que isso? porque se você identificar que você precisa capturar aquele evento de tracing você precisa capturar todos os dados daquela comunicação você não pode perder porque senão o tracing ficaria sem sentido, mas uma vez determinando que aquele tracing não será capturado, ele será descartado de acordo com a estratégia de sampling então ele obviamente vai ignorar toda aquela comunicação subsequente para aquele trace ID especificamente