Skip to content

🔐 ENCERRADO - POC Filas RabbitMQ e cache Redis baseado no curso `Go e Gin` com melhorias

Notifications You must be signed in to change notification settings

jtonynet/api-gin-rest

Repository files navigation

Estudo API Rest em Golang com Gin


go Docker DotEnv Ubunto GitHub MermaidJS VsCode PostgresSQL Swagger Gatling Redis Rabbitmq

Badge Status


🕸️ Minhas Redes:

linkedin dev.to gmail Twitter instagram


⤴️ Índice


📗 Sobre

Este projeto visa aprimorar a API de Alunos do curso Go e Gin: criando API rest com simplicidade de forma estritamente educativa. Continuo incorporando padrões e melhorias para estudar aplicações práticas.

Não considero colisões nos números de CPF e RG, pois o objetivo é criar uma API que lide com alta carga de inserções no momento, simulando um game day (dia de uso intenso em condições adversas)

Para alcançar essas melhorias, adotei as seguintes medidas que não estavam presentes no curso original:

  • Técnicas de debbuging
  • Teste de carga com Gatling
  • Técnicas de Caching usando redis
  • Técnica de Fila com messageBroker para criacao de aluno usando RabbitMQ
  • POC de Escalabilidade Horizontal nos workers
  • Feature Flag para acionar caching e messageBroker
  • Profiling da aplicação para identificar os pontos de "stress".
    • Profiling acionados por feature flag
    • Profiling em middleware

O projeto foi desenvolvido no SO Ubuntu e testado tanto no Ubuntu quanto no Windows, informações de desenvolvimento estão voltadas para o sistema operacional Linux.

⤴️ voltar


💻 Rodando o Projeto

Crie uma cópia do arquivo sample.env com o nome .env e rode o comando docker compose up (de acordo com sua versão do docker compose) no diretorio raiz do projeto:

$ docker compose up

✍️ Observação:

🪟 Troubleshooting com Windows Configurações de atributos do Git que podem afetar o caractere de fim de linha não estão funcionando como esperado. Portanto, para executar o projeto no Windows, será necessário fazer a alteração no arquivo ./tests/gatling/entrypoint.sh. Converta o arquivos de LF para CRLF no seu editor de texto de preferência.

Aguarde até que as imagens sejam criadas e acesse:

Rotas REST:

  • https://localhost:8080/alunos Rota para API
  • https://localhost:8080/aluno/{uuid} Rota para API
  • https://localhost:8080/aluno/cpf/{cpf} Rota para API
  • etc... *

*maiores detalhes de rotas em: swagger

Rotas de uso de infra/suporte:

  • https://localhost:8080/readiness Rota de readiness
  • https://localhost:8080/liveness Rota de liveness
  • https://localhost:15672 Rota de RabbitMQ (verificar .env para senha)

Rotas de uso de desenvolvimento:

  • https://localhost:8080/swagger/index.html Rota para documentação Swagger
  • https://localhost:8082 Rota para ultimo resultado de teste de carga
  • https://localhost:8080/debug/pprof Rota de Profiling, disponível apenas caso PPROF_FEATURE_FLAG_ENABLED=1. Consulte Profiling para maiores informações.

Feature Flags:

🚩 Flag ✔️ Efeito quando ligada
PPROF_CPU_FEATURE_FLAG_ENABLED Habilita rota de Profiling direto na controller
CACHE_FEATURE_FLAG_ENABLED Habilita estratégia de cache em redis para as rotas via middleware
POST_ALUNO_AS_MESSAGE_FEATURE_FLAG_ENABLED Rota de criação de aluno publica mensagens via middleware para workers consumirem ao invés de criar direto no DB

Escalando Workers:

A feature flag POST_ALUNO_AS_MESSAGE_FEATURE_FLAG_ENABLED quando acionada faz o sistema enviar mensagens de criação de alunos para o RabbitMQ na rota POST aluno. No arquivo docker-compose.yml. Você pode ajustar a quantidade de réplicas do worker, que começa com 1, para aumentar a capacidade de inserção de dados no banco de dados. Para que essa abordagem surta efeito comente a linha container_name: worker-gin e hostname: worker-gin. Nomear containers conflita com a funcionalidade replicas do docker

101    worker-gin:
102        deploy:
103          replicas: 1
104 #      container_name: worker-gin
105 #      hostname: worker-gin

Recomendações para Devs:

Embora seja desnecessária a instalação local de nada além do Docker para levantar o projeto, pode haver a necessidade de desenvolver/debbugar localmente.

Recomendo a instalação do GVM para controle de versões da linguagem

Recomendo a instalação da extensão Golang do VsCode

⤴️ voltar


📊 Diagramas


Fluxo do Serviço Alunos:

graph LR
  subgraph Ações Admin Alunos
    A(fa:fa-user ADMIN User)
    B["fa:fa-globe Cria novo aluno"]
    C["fa:fa-globe Obtém a lista completa de alunos"]
    D["fa:fa-globe Busca aluno por uuid"]
    E["fa:fa-globe Deleta aluno por uuid"]
    F["fa:fa-globe Edita aluno por uuid"]
    G["fa:fa-globe Busca aluno por CPF"]
  end

  subgraph Backend 
    subgraph Message Broker
        RabbitMQ(["fa:fa-envelope  Aluno-RabbitMQ"])
    end

    subgraph CMD
      subgraph WORKER
        Worker["fa:fa-gears  Aluno-Worker"]
      end

      subgraph API
        CriaNovoAluno["fa:fa-code CriaNovoAluno"]
        ExibeTodosAlunos["fa:fa-code ExibeTodosAlunos"]
        BuscaAlunoPorUUId["fa:fa-code BuscaAlunoPorUUId"]
        DeletaAluno["fa:fa-code DeletaAluno"]
        EditaAluno["fa:fa-code EditaAluno"]
        BuscaAlunoPorCPF["fa:fa-code BuscaAlunoPorCPF"]
      end

      subgraph CACHE
        Aluno-Redis(["fa:fa-memory Aluno-Redis"])
      end 

      subgraph Models
        Aluno["fa:fa-cube Aluno"]
      end
    end

    subgraph DATABASE
      Aluno-DB[("fa:fa-database Aluno-DB")]
    end 
   
  end

  A --> B
  A --> C
  A --> D
  A --> E
  A --> F
  A --> G

  C -->|GET| ExibeTodosAlunos
  D -->|GET| BuscaAlunoPorUUId
  E -->|DELETE| DeletaAluno
  F -->|PATCH| EditaAluno
  G -->|GET| BuscaAlunoPorCPF

  ExibeTodosAlunos --> Aluno-Redis
  BuscaAlunoPorUUId --> Aluno-Redis
  BuscaAlunoPorCPF --> Aluno-Redis
  Aluno-Redis ---|cached| Aluno
  DeletaAluno --> Aluno
  EditaAluno --> Aluno

  Aluno -->|Queries| Aluno-DB
  B -->|POST| CriaNovoAluno

  CriaNovoAluno -.->|Publica| RabbitMQ
  RabbitMQ -.->|Consome| Worker

  Worker --> Aluno
Loading

Sequência de criação de aluno:

sequenceDiagram
    participant A as ADMIN User
    participant B as "Cria novo aluno"
    participant CriaNovoAluno as CriaNovoAluno
    participant RabbitMQ as Aluno-RabbitMQ
    participant Worker as Aluno-Worker

    A ->> B: Inicia a criação de um novo aluno
    B ->> CriaNovoAluno: Envia a solicitação de criação
    CriaNovoAluno -->> RabbitMQ: Produz mensagem
    RabbitMQ -->> Worker: Consome mensagem

Loading

⤴️ voltar


📰 Gerando documentação com swagger

Como a imagem api-gin-rest rodando, digite:

$ docker exec -ti api-gin-rest swag init --parseDependency --parseInternal  --generalInfo cmd/api/main.go

Para os desenvolvedores que irão manipular o código ou se inspirar para seus próprios desenvolvimentos, há uma particularidade na documentação Swagger. O comando padrão do swaggo/gin-swagger (uma ferramenta que gera documentação Swagger para Go) não consegue ler structs que utilizam gorm.Model, e isso não está explicitamente mencionado em sua documentação. Pesquisando por uma solução, encontrei o comando apropriado para a geração.

⤴️ voltar


📷 Imagens do Projeto

API

Swagger 1

Swagger 2

Teste de Carga Gatling *Teste Inicial, ainda estamos melhorando a API para performar volumes mais elevados de requests em proximas PR`s

⤴️ voltar


🚋 Teste de Carga

Com o projeto instalado e em execução após o comando docker compose up, acesse a rota que renderiza o resultado do teste mais recente em https://localhost:8082. Caso você tenha acabado de iniciar o ambiente, nenhum teste terá ocorrido até o momento. Em um novo terminal e, estando na raiz do projeto, execute o comando:

docker exec -ti gatling-api-test /entrypoint run-test

Aguarde alguns segundos para o aquecimento dos testes (que inclui o download de dependências, caso não existam, e a execução dos próprios testes). Assim que os testes forem concluídos, o endpoint https://localhost:8082 apresentará os resultados.

Toda vez que desejar executar os testes novamente, basta rodar o comando a seguir: docker exec -ti gatling-api-test /entrypoint run-test.

Estrutura da pasta de testes do Gatling:

  $ tree
  api-gin-rest
  └── tests
  |    └── gatling
  |        ├── bundle # Binários e arquivos instalados do Gatling
  |        |   ├── .keep
  |        |   └── ... # Diretórios e arquivos gatling instalados após primeiro teste
  |        ├── results # Resultados dos testes
  |        |   ├── history # Histórico com todos os testes já performados e a pagina default
  |        |   |   ├── default # Dados de teste padrão, exibidos quando nenhum teste ainda foi performado
  |        |   |   └── ... # Diretórios de testes já performados
  |        |   └── latest # Arquivos do último teste performado
  |        |       ├── .keep
  |        |       └── ... # Diretórios e arquivos do resultado mais recente
  |        ├── user-files
  |        |   ├── resources # Arquivos de recursos utilizados nos testes: tsv, etc...
  |        |   |   └── api-gin-rest 
  |        |   |       └── alunos.tsv # Arquivo de dados de post paylod de alunos
  |        |   └── simulations # Pasta dos roteiros de testes, simulações
  |        |       └── api-gin-rest
  |        |           └── AlunosSimulation.scala # Roteiro de testes em scala
  |        ├── Dockerfile
  |        └── entrypoin.sh # As automações do Gatling estão aqui.
  |
  $ tree
  .

Usamos uma imagem com o Gatling instalado para performar testes de carga de maneira automatizada. A imagem responsável por fornecer essa saída também é responsável por processar o teste.


Limpando a instalação do Gatling e removendo históricos de testes:

Importante: Isso não limpa as inserções feitas no banco de dados.

docker exec -ti gatling-api-test /entrypoint clean-test

Limpando o Banco de Dados:

Isso limpa as inserções feitas no banco de dados.

docker exec -ti gatling-api-test /entrypoint clean-db

Configurando o Teste

Caso deseje alterar as configurações padrão do teste, modifique o arquivo tests/gatling/user-files/simulations/AlunosSimulation.scala. O método setUp lhe proporciona flexibilidade na criação de cenários de simulação.

  setUp(
    testAlunos.inject(rampUsers(1000).during(20.seconds))
  ).protocols(httpProtocol)

Debbuging do Gatling:

Visualização de logs de requisições do Gatling (apenas em ambiente local para fins de depuração):

Após a instalação do Gatling, que ocorre na primeira vez que você solicita a execução de um teste, vá até o arquivo api-gin-rest/tests/gatling/bundle/conf/logback.xml e descomente a linha 13.

11  	<!-- uncomment and set to DEBUG to log all failing HTTP requests -->
12  	<!-- uncomment and set to TRACE to log all HTTP requests -->
13  	<logger name="io.gatling.http.engine.response" level="TRACE" />

⤴️ voltar


🔍 Profilling

API:

O profiling da aplicação para fins de testes e validação está vinculado às rotas. O processo não é executado de maneira dockerizada, necessitando ter o Go e o Graphviz instalados na sua máquina.

$ sudo apt-get install graphviz

Para ativar o profiling de rotas, basta alterar o valor da variável de ambiente PPROF_FEATURE_FLAG_ENABLED para 1 no arquivo .env:

PPROF_FEATURE_FLAG_ENABLED=1

Com isso, a seguinte rota fica ativa e apresenta resultados dos profiles disponíveis:

https://localhost:8080/debug/pprof

Podemos agora, com Go e Graphviz na máquina hospedeira, ligar a coleta de métricas. Observe a imagem com dois terminais, no terminal 1 foi inserido o comando:

$ go tool pprof https://localhost:8080/debug/pprof/profile

Isso fará com que, durante os próximos trinta segundos, as requests feitas na API gerem massa de dados para o profiler.

Assim que o comando go tool pprof for iniciado, em outro terminal ja tenha o comando do Teste de Carga preparado para rodar e gerar insumos para o pprof. Comando no terminal 2:

$ docker exec -ti gatling-api-test /entrypoint run-test

Após o Teste de Carga rodar sua saida sera similar a seguinte:

No terminal do pprof o cursor deve estar aguardando o proximo comando para continuar o profiling, digite web no cursor e de enter, aguarde seu navegador o exibir a árvore de processamento de sua aplicação.

Fetching profile over HTTP from https://localhost:8080/debug/pprof/profile
Saved profile in /home/jtony/pprof/pprof.main.samples.cpu.003.pb.gz
File: main
Type: cpu
Time: Oct 10, 2023 at 5:27pm (-03)
Duration: 30.01s, Total samples = 410ms ( 1.37%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) web

A saida deve ser similar a:

Bibliografia: Profiling gin with pprof


⤴️ voltar


🔨 Ferramentas

As seguintes ferramentas foram usadas na construção do projeto:

⤴️ voltar


👏 Boas Práticas

Seguindo boas práticas de desenvolvimento:

⤴️ voltar


🔢 Versões

As tags de versões estao sendo criadas manualmente a medida que os estudos avançam com melhorias notáveis no projeto. Cada funcionalidade é desenvolvida em uma branch a parte quando finalizadas é gerada tag e mergeadas em master.

Para obter mais informações, consulte o Histórico de Versões.

⤴️ voltar


🤖 Uso de IA:

O cabeçalho desta página foi criado com o auxílio de inteligência artificial e um mínimo de retoque e construção no Gimp Logo do Gimp

Foram utilizados os seguintes prompts para sua criação no Bing IA:

Gopher com roupa de estudante bebendo gin "Gopher simbolo da linguagem golang azul em cores cartoon chapadas em uniforme escolar segurando um copo de drink com uma garrafde gin no chaco e fundo branco"(sic)

IA também é utilizada em minhas pesquisas e estudos como ferramenta de apoio; no entanto, artes e desenvolvimento são, sobretudo, atividades criativas humanas.

Contrate artistas para projetos comerciais ou mais elaborados e Aprenda Engenhosidade!

⤴️ voltar

About

🔐 ENCERRADO - POC Filas RabbitMQ e cache Redis baseado no curso `Go e Gin` com melhorias

Resources

Stars

Watchers

Forks

Packages

No packages published