Alura > Cursos de Programação > Cursos de Python > Conteúdos de Python > Primeiras aulas do curso Mensageria com Python e Kafka: integração assíncrona em sistemas distribuídos

Mensageria com Python e Kafka: integração assíncrona em sistemas distribuídos

Kafka - Introdução

Apresentando o instrutor e o curso

Olá! Meu nome é Stephen Batista e atualmente sou Stephen Genie em uma empresa de meios de pagamento. Tenho 20 anos de experiência em desenvolvimento de software e, ao longo desse tempo, trabalhei para diversas empresas em diferentes setores. Hoje, gostaria de apresentar um curso especial: o curso de mensageria com Kafka.

Audiodescrição: Stephen é um homem de pele clara, com cabelo curto e barba. Ele veste uma camisa social e está em um ambiente de escritório, com uma mesa ao fundo e um computador ao lado.

Introduzindo o conteúdo do curso

Neste curso, nós aprenderemos por que o Kafka está sendo amplamente utilizado por grandes empresas. Vamos explorar em profundidade o funcionamento do Kafka e também implementar um software de checkout, responsável pela integração de meios de pagamento, inventário e pedidos. Neste curso, trabalharemos com o Kafka, naturalmente. Seja muito bem-vindo e esperamos por você no curso.

Kafka - Fluxo do projeto atual

Introduzindo o curso de mensageria com Kafka

Olá! Sejam bem-vindos ao nosso curso de mensageria com Kafka. Antes de começarmos a prática, precisamos entender qual é o projeto que vamos abordar para, então, modificar e implementar a mensageria.

O fluxo que vamos trabalhar é, basicamente, um fluxo de checkout. Pensem em um e-commerce, onde é necessário um processo de checkout. Temos aqui a parte do checkout. Por que o checkout é tão importante? Imaginem que fazemos parte do time de checkout em uma grande empresa. Dentro dessa grande empresa, existem vários times responsáveis por outros sistemas.

Explicando a integração entre times no e-commerce

Por exemplo, podemos mencionar o time do site, que inclui a página inicial, e também o time mobile. Tudo que não está destacado em verde representa outros times. Esses dois times, após selecionarem um produto e colocarem-no no carrinho, seja via site ou mobile, precisam acionar o time de checkout, que é o nosso time. Nosso time de checkout será responsável por se integrar com outros times.

E que times são esses? Podemos citar, por exemplo, o time de pagamento, que é responsável por fazer integrações com todos os métodos de pagamento.

Detalhando a importância da integração com outros times

Quando trabalhamos em um e-commerce, não temos contrato apenas com um método de pagamento; podemos ter contrato com vários métodos e até alterá-los. Se um desses métodos apresentar problemas, outro pode continuar funcionando, tornando o sistema mais resiliente. Embora o time de payment cuide disso, precisamos nos integrar com eles.

Outro time com o qual podemos nos integrar é o de inventory, responsável por retirar produtos do estoque, evitando que sejam comprados novamente. Um terceiro time importante é o de pedidos, responsável por criar o pedido e enviar o produto até a casa do cliente. Este é o cenário em que nos encontramos.

Analisando o código atual de checkout

Atualmente, nosso código é o de checkout. Todas essas integrações já existem e estão funcionando, mas de uma forma diferente. Precisamos implementar a mensageria para que tudo funcione corretamente. Antes de explicar o que vamos mudar, vamos analisar nosso código para entender sua estrutura atual.

Kafka - Arquitetura do projeto

Introduzindo o projeto e suas tecnologias

Este é o projeto que nós vamos receber, e ele representa todo o fluxo que foi explicado anteriormente. Ao ler o Readme, podemos entender melhor sobre o projeto e as integrações que realizamos. Utilizamos o FastAPI, que consideramos um dos melhores frameworks web para Python. Embora o curso não foque tanto no FastAPI, ele se concentra principalmente no Kafka.

Nossa aplicação atual integra diversos serviços. Estamos utilizando o Python 3.13, o FastAPI, o SQLAlchemy como um ORM, e o PostgreSQL como banco de dados. Também utilizamos o V como Package Manager, uma excelente biblioteca para Python. Se ainda não trabalhamos com o V, após começar a utilizá-lo, dificilmente desejaremos outra ferramenta, como o EntryPipe. O V instala a versão necessária do Python e as bibliotecas, gerenciando tudo de forma eficiente. Para nós, há um antes e depois no gerenciamento de bibliotecas em projetos Python com a introdução do V.

Clonando o repositório e simulando integrações

Para começar, precisamos clonar o repositório do projeto. Isso pode ser feito com o seguinte comando:

git clone https://github.com/StephanyBatista/checkout-commerce.git
cd checkout-commerce

Além disso, estamos utilizando o Arimok para simular integrações. O Arimok é uma ferramenta valiosa que merece uma aula exclusiva, pois é muito eficaz em ambientes de desenvolvimento. Imagine o seguinte cenário: somos o time de checkout e realizamos integrações com outros times da companhia. Como testar nosso ambiente de desenvolvimento com essa dependência? O Arimok resolve esse problema simulando conexões como um fake HTTP. Ele é extremamente útil, pois em ambientes de desenvolvimento, não precisamos integrar com outros sistemas, podendo resolver tudo localmente. Vale a pena estudar o Arimok, embora existam outras ferramentas semelhantes. Não defendemos uma ferramenta específica, mas sim o processo de eliminar dependências em ambientes de desenvolvimento, o que facilita e acelera a produção.

Instalando dependências e executando scripts

Para baixar todas as bibliotecas necessárias, basta executar um vsync. Na parte superior, em "terminal", ao abrir um novo terminal e executar uvsync, todas as dependências serão instaladas, incluindo o venv, que é o ambiente virtual do Python. Isso permite instalar bibliotecas de forma segura, evitando conflitos globais com outros projetos que podem estar em versões diferentes.

uv sync

Além disso, temos um randev.sh para execução. Ao executar ./uv randev, o comando será executado. Se preferirmos executar o comando manualmente, não há problema. Por isso, criamos o randev.sh, que executa o código uv run, seguindo a sintaxe do uv para execução. O uv corne é um padrão do Python, e o projeto está localizado na pasta "app", no arquivo main.

Configurando permissões e executando a aplicação

Para garantir que o script run-dev.sh tenha permissão de execução, utilizamos o seguinte comando:

# execute to permission: chmod +x run-dev.sh
uv run uvicorn app.main:app --host 0.0.0.0 --port 8005 --reload

A instância do objeto do aplicativo de fetch API é chamada de app. Aqui estão o host e a porta que serão utilizados. Também está configurado para ser executado via reload. Ou seja, toda vez que precisarmos alterar esse código, o próprio uvicorn fará o reload. Esta é, basicamente, a arquitetura do nosso projeto.

Para executar o script, utilizamos:

./run-dev.sh

Utilizando o wiremock para simulações

Nós temos o wiremock, que são as pastas do mapeamento do HTTP que esperamos. Normalmente, não inventamos isso por conta própria. Utilizamos a documentação de algum time ou empresa. A partir dessa documentação, criamos esse mapeamento. Por exemplo, no inventário, criamos apenas o sucesso. Isso significa que toda vez que fizermos um POST para essa URL, ele retornará sucesso 200, incluindo o header. Poderíamos incluir várias informações no header, como application/json. O JSON body é como ele retornará, ou seja, um JSON com success: true, uma mensagem e um timestamp formatado em tempo de execução. Quando nos conectarmos ao time de inventário, é esse body que será retornado. Não precisamos integrar com esse time.

Aqui está um exemplo de configuração para o wiremock do inventário:

{
    "priority": 5,
    "request": {
        "method": "POST",
        "url": "/inventory/deduct"
    },
    "response": {
        "status": 200,
        "headers": {
            "Content-Type": "application/json"
        },
        "jsonBody": {
            "success": true,
            "message": "Products deducted from inventory successfully",
            "timestamp": "{{now format='yyyy-MM-dd HH:mm:ss'}}"
        }
    }
}

Configurando o wiremock para pagamentos

Existem outros mapeamentos também. Este é apenas do inventário, mas há outros, como o do Payment. No Payment, existem vários. Por exemplo, se quisermos testar que o saldo está insuficiente, o processo é semelhante. Temos um método POST, a URL é esta, e toda vez que passarmos um valor dentro do nosso objeto amount maior que 1.000, retornaremos um 422 e indicaremos que o status é failed. É por isso que o wiremock é bastante útil.

Aqui está um exemplo de configuração para o wiremock do pagamento:

{
    "priority": 1,
    "request": {
        "method": "POST",
        "url": "/payments/process",
        "bodyPatterns": [
            {
                "matchesJsonPath" : "$[?(@.amount > 1000)]"
            }
        ]
    },
    "response": {
        "status": 422,
        "body": "{\"status\":\"failed\",\"error\":\"insufficient_funds\",\"message\":\"Insufficient funds\"}",
        "headers": {
            "Content-Type": "application/json"
        }
    }
}

Configurando e executando serviços com docker-compose

Temos o docker-compose, que define o relacionamento de todos os serviços que vamos executar. Precisamos executar o comando docker-compose up. Aqui está o nome do container, as variáveis de ambiente e o serviço. Cada serviço utiliza o wiremock. Temos a imagem, a porta e o mapeamento do volume. Tudo que estiver na pasta "Payment" do nosso projeto será direcionado para /home/wiremock. Esse mapeamento evita a necessidade de configuração manual. Já deixamos todos os arquivos prontos, então, quando levantarmos o ambiente, tudo estará configurado.

Aqui está a configuração do docker-compose para o serviço de PostgreSQL:

version: '3.8'

services:
  postgres:
    image: postgres:16-alpine
    container_name: checkout-postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: checkout_db
    ports:
      - "5442:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U checkout_user -d checkout_db"]
      interval: 10s
      timeout: 5s
      retries: 5

E aqui está a configuração para o serviço de pagamento:

  payment-service:
    image: wiremock/wiremock:latest
    container_name: payment-service-mock
    ports:
      - "8081:8080"
    volumes:
      - ./wiremock/payment:/home/wiremock
    command: ["--global-response-templating", "--verbose"]
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/__admin"]
      interval: 30s
      timeout: 10s
      retries: 3

Executando o ambiente completo

Para executar, basta usar o comando docker-compose up -d para que o processo não fique bloqueado. Ele levantará todos os serviços necessários, como o PostgreSQL, o serviço de pedido, o serviço de inventário e o serviço de pagamento.

docker compose up -d

Em seguida, utilizamos o comando sh run dev, que automaticamente configura tudo, incluindo a migração, que já está configurada.

./run-dev.sh

Concluindo e preparando para futuras alterações

Este projeto é um exemplo de como funciona no mundo real. Trabalhamos nele há um ou dois anos, recebendo evoluções constantes. Agora, encontramos um problema e precisamos fazer uma alteração, mas isso será abordado nos próximos vídeos.

Sobre o curso Mensageria com Python e Kafka: integração assíncrona em sistemas distribuídos

O curso Mensageria com Python e Kafka: integração assíncrona em sistemas distribuídos possui 148 minutos de vídeos, em um total de 49 atividades. Gostou? Conheça nossos outros cursos de Python em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Python acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas