Vamos fazer um rende-zão agora? Deixa eu dar um overview na infraestrutura, em como você implementar, e aí aos poucos eu vou entrando no código e depois eu quero mostrar visualmente o Jaeger funcionando. Então, vamos utilizar o Jaeger aqui, mas as ferramentas que nós já usamos, enfim, elas também eventualmente suportam o conceito de tracing, tá? Por exemplo, o Splunk consegue suportar a solução de tracing. Você pode também optar por uma solução mais sofisticada, como um APM, um Application Performance Monitoring, enfim. Além do IEGRA também, tem outros componentes como o Zipkin. Então, enfim, tem uma gama de opções de tracing. É importante você utilizar aquela que fizer mais sentido, levando em consideração o custo-benefício da solução. Até porque muitas das soluções são pagas, elas não permitem uso gratuito. E uma vez o Jaeger definindo como solução de tracing que vamos utilizar, você tem várias formas de instrumentar a aplicação para capturar os eventos de tracing daquela aplicação em si e disponibilizar para visualização no serviço de tracing que é o Jaeger. Algumas das formas de você fazer isso, por exemplo, é utilizando o Micrometer ou o Spring Cloud Sleuth, por exemplo, ou até o Open Telemetry. E aí você pode usar o Open Telemetryry com Java Agent, por exemplo, ou não. A gente vai utilizar aqui no exemplo uma instrumentação onde eu utilizo o Micrometer como componente para captura dos eventos de tracing e o OpenTelemetry para envio desses eventos de tracing para o serviço do Jaeger. Vamos começar pela infraestrutura do Jaeger. Vamos utilizar Docker Compose, e aqui eu tenho uma implementação mais simples do Jaeger, que é onde eu declaro o serviço Jaeger aqui. A imagem eu estou pegando o Jaeger Tracing All-in-One. Tem opções mais específicas do Jaeger, inclusive tem uma opção onde você tem o serviço do Jager UI você também tem o serviço do coletor separado enfim aqui eu tô utilizando a opção é all-in-one que tem tudo dentro dele tá os mecanismos de captura dos eventos e a parte de UI enfim o nome do container Jager mecanismo de resiliência no caso de falha e reinicializar e aqui é importante reforçar as portas que você vai utilizar para comunicação o serviço do Jager tem vários entry points de comunicação aqui eu estou expondo a porta 16 686 que é a porta do UI, da tela, do console do serviço do Jager, e a 4318 é a porta TCP para a captura dos eventos, e o OpenTelemetry utiliza o protocolo GRPC para essa comunicação. Então, essa porta também suporta GRPC para a captura dos eventos. Se você quiser saber mais sobre quais portas que você pode expor, enfim, do Jager, na documentação do serviço do Jager, que está aqui, jagertracing.io, você tem algumas instruções de como você sobe o serviço e se integra. Algumas portas, por exemplo, aceita o protocolo UDP, outras portas HTTP, descendo aqui, por exemplo, você tem a parte do Collectors, os Collectors e você também tem outras portas específicas, enfim. Então, você pode utilizar essa documentação como adicional, enfim, a tudo que eu trouxe aqui neste vídeo para você. Bom, já tem essa infraestrutura implementada vamos voltar aqui no IntelJ aqui na pasta infra e agro eu tenho docker-compose eu tenho basicamente a mesma implementação uma vez feita essa configuração você pode vir aqui na pasta e rodar docker-compose app, vou rodar com o menos d para ele subir o serviço do Jager em background e aqui eu posso já acessar via browser através da porta localhost e aí ele já tem o serviço disponível aqui rodando vou voltar para o código porque eu quero agora alterar a aplicação, instrumentá-la para que ela disponibilize os dados de tracing, e aí a gente conseguir fazer uma boa navegação no Jaeger, utilizando já esses dados. Agora, do lado da aplicação, o que você precisa saber e implementar? Você precisa da biblioteca do Actuator, da biblioteca Micrometer Tracing Bridge Hotel, lembrando o que eu disse no começo, você tem várias formas de fazer essa implementação. O caminho utilizando o micrometer mais OpenTelemetry é esse que eu vou mostrar. Aqui, outra dependência que você precisa é o exporter, o exporter OpenTelemetry, exporter TLP. Aqui embaixo, opcionalmente, se você precisar capturar eventos de integração com a base de dados, Se você precisar capturar eventos de integração com a base de dados, eventos de carry, onde você, utilizando o JPA, por exemplo, você interage com o banco de dados e quer capturar esses eventos, você precisa também dessa biblioteca, que é o Data Source Micrometer Spring Boot, e ele está no grupo, no pacote aqui, ttddyy.observation. E por fim, vamos, precisamos fazer a parametrização lá na application.properties, application.yaml, mas como estamos utilizando o docker-compose, eu vou parametrizar essas variáveis de ambiente na própria aplicação, via docker-compose. Aqui você tem que basicamente parametrizar duas coisas. A primeira é o OpenTelemetry, o Tracing Endpoint. Esse endpoint é o endpoint para o qual ele vai enviar os eventos de Trace. Observe que eu estou utilizando o HTTP, protocolo aqui aberto, Host Docker Internal, porque aí eu estou me comunicando com a ponte, o bridge do Docker, a infraestrutura de rede do docker, para ele se comunicar com o host, porque eu não estou dividindo o mesmo network do do Jaeger com a aplicação vou subir a apartado a porta que eu comentei, que eu expus para comunicação, para envio de traces barra v1, barra traces é o path para envio desses eventos. E o segundo parâmetro que você precisa saber é a questão da probabilidade, do sampling. Eu estou utilizando o modelo probabilístico, mas aqui eu estou definindo que eu estou enviando praticamente todos os eventos, não estou removendo eventos. Aqui eu estou dizendo a probabilidade de um, ou seja, de cem por cento de que todos os eventos vão aparecer no serviço do então apenas uma configuração simples aqui, mas é é esse ponto que você vai determinar se você tiver utilizando estratégia de probabilidade probabilística aqui é o momento que você calibraria esse percentual, esse valor. E aqui vão algumas considerações sobre a configuração da aplicação para uma boa captura dos eventos de Tracing. Quando você fala de comunicação HTTP e você utiliza REST Template, por exemplo, do Java, é importante garantir que esse REST Template esteja implementado utilizando o Builder do REST Template por mais que você implemente alguma customização você pode configurar o REST Template agregar algumas customizações mas é importante você usar o Builder porque as bibliotecas que eu falei anteriormente aqui que eu comentei, elas já vão incluir alguns dados no REST Template que você precisa também usar. Se você não fizer isso dessa forma, se você não utilizar o REST Template Builder, você pode acabar perdendo configurações relevantes do serviço do Jaeger, como interceptors, por exemplo, e são esses interceptors que vão injetar os dados de contexto na comunicação. Ou seja, qualquer comunicação que você fizer via HTTP, esses dados de contexto sem essa configuração vão acabar se perdendo, tá? Porque você não, você sobrescreveu a configuração das bibliotecas do OpenTelemetry, enfim, e do micrômetro, e são justamente esses interceptors que fariam essa injeção de contexto. e são justamente esses interceptors que fariam essa indicação de contexto. E lembrando, acabei de comentar, mas vale reforçar é que se você precisa também capturar eventos de banco de dados, é importante utilizar a biblioteca que eu comentei, que é o DataSource Micrometer Spring Boot, desse pacote aqui. Bom, vamos olhar como é que está a aplicação. E antes de mostrar aqui direto a infraestrutura, eu quebrei a aplicação em duas. Não fisicamente, não no seu code base, mas virtualmente quando eu faço deploy via Docker Compose. Eu tenho a mesma infraestrutura aqui, mesmo source, mesmo code base. Em order eu tenho aqui os componentes de order, product aqui também, mas virtualmente, na hora de provisionar essa infraestrutura, eu subo serviços diferentes e eles se comunicam via HTTP e não mais utilizando a mesma infraestrutura de banco de dados que eu comentei. O banco de dados eu não quebrei, deixei o mesmo, mas os componentes apartados para a gente entender como que essa comunicação, interprocessos, interserviços ocorreria no Jaeger. Antes de entrar no código, deixa eu mostrar como está a infraestrutura. No Docker Compose eu tenho o banco de dados, não mudei, mesma configuração. Agora, Products, eu não tenho mais uma API não única tem agora o nome do container componente aqui de resiliência em caso de parar de reinicializar o build tô informando mesmo docker file para configuração da aplicação as áreas de ambiente para comunicação com banco de dados e aqui começa a mudar e chavear para o serviço apropriado. O Spring Profiles Active eu utilizo para chavear e aí eu estou fazendo esse chaveamento para o profile. Então, se tem algum componente, alguma classe que só faz sentido para o serviço de Product, eu vou anotar com arroba profile e aí definir que aquele objeto só vai ser carregado, por exemplo, em Product, na aplicação de Product. Spring Application Name eu estou chamando de Store Products API, porque eu quero que esse mesmo dado, inclusive, ele vai evoluir para o serviço do Jaeger, o Jaeger e as bibliotecas aqui do Micrometer, do OpenTelemetry, utiliza esse mesmo parâmetro para identificar a aplicação, os eventos daquela aplicação no serviço do Jaeger. As informações do banco de dados e aqui os dados de integração para a integração com o serviço do Jaeger. Aqui o endpoint, aqui de novo estou utilizando o host docker internal para a comunicação da aplicação com o serviço do Jager, a porta com a três dezoito e o e aqui é o mecanismo probabilístico, né? O sampling, mas eu quero capturar todos os eventos, então, probabilidade de cem por cento de captura. Essa aplicação especificamente ela sobe na porta oitenta e oitenta, né? No aqui eh mas externamente, para acesso externo, através da minha máquina, do host, ela vai utilizar a porta 8080. E mais abaixo, tem a aplicação de orders agora, container name orders, a mesma configuração praticamente, a diferença é que o Spring Profiles Active agora é order, o nome da aplicação Store Orders API, a porta ainda é 8080 e ela depende do serviço de Product aqui. Indo agora na parte de código, o que mudou de fato? Em Source Main Resources, aqui no application YAML, eu tenho aqui o API products endpoint URL que é o URL, o endpoint que eu quero fazer com que a aplicação de orders se comunique com a aplicação de products para consultar os dados do produto antes eu ia direto no repository na base de dados para consultar os dados do product, agora não eu vou consultar as orders e quero consultar os products do produto. Agora não, eu vou consultar as orders e quero consultar os products em todo momento que eu tiver a interação entre order e product. Aqui ainda também eu criei configurações de application.yml específicos por profile, mas a configuração praticamente a mesma, então tem application order, application product a diferença é que como eu estou dividindo mesmo a base de dados e aí eu quero fazer um disclaimer aqui, essa configuração está longe de ser ideal, numa boa arquitetura de microserviços é interessante cada aplicação, cada componente tem a sua camada de storage porque aí você garante total desacopalamento entre serviços ou seja, toda comunicação interprocesso ela é feita utilizando de fato comunicação REST comunicação HTTP ou utilizando um mecanismo de fila mas não a base de dados de fato aqui eu fiz algo um pouco menos exaustivo mesmo para a gente conseguir ter um cheiro ou uma pequena experiência de como seria essa integração e como isso ficaria visível no Jager. Então, aqui é a mesma praticamente configuração, porque eu utilizo a mesma infraestrutura de dados, mas a única diferença aqui é a aplicação de product é o que vai determinar as configurações do esquema da base, mas a aplicação de order não. Essa é a diferença. Inclusive aqui no código ainda em Java agora aqui no Store Application Startup eu faço algum pré-carregamento de dados. Já injeto algumas informações na aplicação de dados de produto ou de orders, enfim, para que a gente conseguisse já utilizar as rotas de GET, por exemplo. Mas, de novo, longe de ser a configuração ideal, é ideal que eu tivesse uma base de dados separada. E agora, mostrando o código especificamente, só dando uma repassada e aí salientaram que de fato mudou. Aqui no config eu tenho o REST template como eu comentei, estou criando o REST template a partir do REST template builder. E só um disclaimer aqui que eu esqueci de comentar, se você utiliza FAN para comunicação, FAN client, comentar, se você utiliza FAN para comunicação, FAN Client, talvez você tenha alguma limitação para a captura do tracing desses eventos e talvez precise recorrer a alguma arquitetura de AOP ou programação orientada a aspecto, tá? Para você conseguir garantir que os dados sejam injetados no contexto. Acredito que tenha bibliotecas ou mecanismos de captura um pouco mais sofisticados que já suprem esse problema. Por exemplo, se você utilizar o Java Agent, que aí você tem um agent capturando todos os eventos que entram e saem da sua aplicação. Mas, pode fazer um disclaimer aqui, pode ser que tenha um desafio aqui para você utilizar. O REST Template é uma solução de longa data no Spring, ele está aí presente por bastante tempo e ele é bastante versátil, enfim, permite extensão dele, inclusão de interceptors, enfim, por isso que eu optei por utilizá-lo e buscar aqui uma simplicidade na solução. Além da configuração do REST template, aqui eu tenho o código de produtos, que eu não mudei praticamente. Aqui eu tenho o controller de produtos, que permite a busca de todos os produtos cadastrados, a criação de produtos, a busca por um produto por determinado ID. E essa é a rota que nós vamos mais usar aqui. E, claro, a opção de deletar um produto por determinado ID e essa rota que nós vamos mais usar aqui e claro a opção de deletar um produto. O que mudou de fato foi em order controller aqui no controller na rota de criação de produtos aqui aqui mais para baixo na hora de criar um produto novo antigamente eu fazia consulta direto utilizando o Product Repository pra consultar os dados do produto na base, procurava pelo ID, se eu não encontrasse eu retornava uma exceção, mas agora eu estou fazendo a busca via requisição HTTP. Eu tenho esse Order Products Crosscut aqui que tem a implementação do Find por ID por ele basicamente faz o uso do usando aquele que eu comentei lá na eh ponto eh ele monta o RL pegando aquela aquela aquele que eu configurei mais o ID do produto e faço requisição HTTP. De novo, longe de ser a configuração ideal, mas aqui estou indo pela simplicidade apenas para ilustrar o uso do IEGER e de uma solução de tracing distribuído. Uma vez obtido o produto, ele segue aqui com o que precisa ser feito, por exemplo, pegar o preço do produto, multiplicar pela quantidade daquele produto que foi incluído no carrinho, para eu conseguir calcular o preço total daquela order. Outros lugares que eu também mexi foi aqui na busca por uma única order order eu consulto a ordem por aí de eu não encontro ela retorna erro mas o que mudou de fato aqui aqui inclui esse código onde eu pego a os itens que foram incluídos que estão naquela ordem né o order items vamos assim naquele carrinho de compras e para cada item que o consulto pega o ID do produto e consulto na mesma rota de consulta os dados do produto e incluo aquele produto naquela consulta. E um terceiro lugar que eu alterei foi na busca de orders aqui, todas as orders, aquela mesma rota que incluímos paginação. Nessa rota, além de consultar as orders aqui dentro da base eu percorro então a lista de itens, faço um flat map inclusive porque eu quero percorrer agora todos os itens, lembrando que essa rota traz todas as as orders da base e cada order tem os seus items ou seja, cada solicitação cada pedido tem o seu carrinho. Eu quero listar todo o carrinho, todos os produtos de todas as orders. Então, eu utilizo esse conceito de flat map, onde eu listo todos os items e para cada item eu vou fazendo a busca. E aqui eu fiz, e aí de novo, não quero entrar no mérito de discussão arquitetural se é a melhor solução, aqui você pode argumentar que poderia utilizar uma solução, sei lá, CQRS por exemplo, ou utilizar um conceito de API mais sofisticada, por exemplo, como GraphQL para fazer agregação de serviços, enfim, ou utilizar um Composite Pattern, aqui a minha intenção foi mostrar apenas o valor da solução de tracing e aqui pode ser um exemplo de enfim, pode ser que a arquitetura que a gente está utilizando, pode ser que ela se revele na hora de utilizar o serviço de tracing como uma má arquitetura e aí a partir do serviço de tracing você de fato colher o benefício da solução, conseguir identificar esse tipo de problema e atuar de forma mais tempestiva possível. Observe que aqui antigamente eu tinha apenas a consulta por order, e aí eu tratava aqueles dados que vinham, fazia a consulta no repository, ele trazia a order e os produtos vinculados ali. Dessa vez não, eu quero forçar que eu estou buscando produto e compondo no resultado da busca e mostrar aqui no resultado final na consulta.