Os testes de performance são essenciais para garantir que sua aplicação seja capaz de suportar cenários reais de uso, especialmente sob grande carga de tráfego. A implementação sistemática de testes de performance, a análise dos resultados e a otimização com base nas conclusões obtidas podem melhorar significativamente a responsividade, estabilidade e escalabilidade de seu sistema.
Este processo começa com a preparação do ambiente. Primeiro, é necessário ter uma aplicação funcional – no nosso caso, utilizaremos um protótipo simples, denominado "protoapp" – e um framework de testes. Para essa tarefa, o framework escolhido foi o Locust, uma ferramenta de teste baseada na sintaxe do Python. A documentação oficial do Locust, que pode ser acessada em docs.locust.io, fornece uma explicação detalhada de como utilizar a ferramenta.
Preparando o Ambiente de Testes
Para começar, é necessário garantir que o Locust esteja instalado em seu ambiente virtual. Isso pode ser feito com o comando:
Uma vez que o Locust esteja instalado, a próxima etapa consiste na configuração de um arquivo que definirá o comportamento dos usuários simulados interagindo com a aplicação em teste. Este arquivo, denominado locustfile.py, deve ser criado na raiz do seu projeto.
Aqui está um exemplo básico de como o conteúdo do arquivo pode ser estruturado:
Neste arquivo, definimos uma classe ProtoappUser, que representa um usuário acessando a aplicação em teste. O endereço do host é o da aplicação local, e a tarefa associada é uma requisição GET para o endpoint "/home".
Executando o Teste de Performance
Com o arquivo locustfile.py configurado, o próximo passo é iniciar o servidor FastAPI. Para isso, execute o comando:
Em seguida, em uma nova janela do terminal, execute o Locust com o seguinte comando:
Isso iniciará a interface web do Locust no endereço http://localhost:8089, onde você poderá configurar os parâmetros do teste. A interface permite ajustar diversos aspectos, como o número máximo de usuários simultâneos (Concurrent Users) e a taxa de aumento de usuários (Ramp-Up Rate), para simular diferentes cenários de tráfego.
Além disso, o Locust pode ser executado em modo headless, ou seja, sem a interface web. Nesse caso, o comando seria:
Neste exemplo, o comando simula 10 usuários acessando a aplicação ao mesmo tempo, com uma taxa de 1 usuário por segundo.
Integrando os Testes de Performance ao Pipeline CI/CD
Os testes de performance não devem ser limitados apenas à fase inicial do desenvolvimento. É fundamental que eles sejam integrados ao pipeline de Integração Contínua e Entrega Contínua (CI/CD). Isso permite que a aplicação seja testada automaticamente antes de ser implantada, ajudando a identificar problemas de performance de forma precoce, quando são mais fáceis de resolver.
O Locust pode ser incorporado ao pipeline CI/CD para rodar testes a cada atualização de código, simulando o tráfego e garantindo que o desempenho da aplicação seja mantido ou melhorado com o tempo. Ao adotar essa abordagem, é possível realizar testes contínuos, tornando o processo de desenvolvimento mais robusto e eficiente.
Monitoramento e Análise dos Resultados
Durante e após a execução dos testes, é essencial monitorar os resultados de forma detalhada. O Locust fornece métricas cruciais como tempo de resposta, taxas de erro, e o número de requisições por segundo. Essas métricas são fundamentais para avaliar como a aplicação se comporta sob carga e identificar gargalos de performance.
Além disso, é recomendável realizar uma análise pós-teste para comparar os resultados com os objetivos de performance da aplicação, como tempos de resposta aceitáveis e a quantidade máxima de usuários simultâneos. Esse processo de análise e ajuste contínuo é vital para manter a qualidade do software em longo prazo.
É importante destacar que testes de performance não são apenas sobre gerar tráfego. Eles envolvem a avaliação das diferentes partes da aplicação sob carga, incluindo a infraestrutura, o banco de dados e a capacidade de escalabilidade. Para aplicações de alto tráfego, muitas vezes é necessário otimizar tanto o código quanto a arquitetura para garantir uma performance estável e eficiente.
O Uso do Locust para Simulação de Tráfego
O Locust não se limita a testes simples de carga. A ferramenta permite a criação de cenários de tráfego mais complexos, simulando diferentes tipos de usuários e comportamentos. Por exemplo, é possível criar tarefas específicas que imitam navegações mais complexas, interações com formulários ou até mesmo chamadas de APIs externas.
Isso oferece uma flexibilidade significativa, permitindo que os testes se aproximem mais da experiência real de uso de uma aplicação, o que é crucial para identificar problemas que poderiam surgir em cenários de alta demanda.
Segurança e Escalabilidade
Além de realizar testes de performance, outro aspecto fundamental para aplicações de alta demanda é garantir a segurança e escalabilidade. Embora o Locust seja excelente para avaliar o desempenho da aplicação sob carga, é igualmente importante verificar como a aplicação se comporta quando submetida a diferentes tipos de ataques, como DoS (Denial of Service), e como ela gerencia o aumento de usuários de maneira eficiente.
A escalabilidade de uma aplicação pode ser testada simulando um aumento gradual no tráfego e monitorando a capacidade do sistema em lidar com esse aumento sem degradação de performance. Isso ajuda a garantir que a aplicação possa crescer conforme necessário, sem comprometer a experiência do usuário.
Como Integrar o FastAPI com Redis para Melhoria de Desempenho e Cache de Dados
Ao desenvolver aplicações web eficientes e de alta performance, uma das melhores práticas é utilizar cache para armazenar dados temporários que são frequentemente acessados. O FastAPI, um framework Python de alto desempenho para a construção de APIs, possui integração fácil com diversos sistemas de cache, incluindo Redis. A seguir, detalhamos como você pode utilizar o Redis para otimizar a performance de um endpoint do FastAPI, melhorando assim a experiência do usuário e a escalabilidade da aplicação.
Em primeiro lugar, a integração de cache com Redis em FastAPI começa com a verificação da existência de dados armazenados no Redis antes de buscar as informações de uma fonte primária, como um banco de dados ou um sistema de pesquisa como o Elasticsearch. No caso do nosso exemplo, vamos considerar que desejamos armazenar a lista dos "dez principais artistas" de um país específico. O fluxo de trabalho para isso é simples:
-
Inicialmente, verificamos se a chave associada ao país já existe no Redis. Se a chave estiver presente, os dados são retornados diretamente do cache, o que garante um tempo de resposta mais rápido para o usuário. Isso é feito com o comando:
-
Caso os dados não estejam disponíveis no cache, buscamos as informações diretamente no Elasticsearch. Essa busca pode gerar uma exceção se a consulta for inválida (por exemplo, quando um país não existe ou a consulta retorna um erro). Para lidar com isso, capturamos a exceção e retornamos uma mensagem de erro para o usuário:
-
Após a consulta bem-sucedida ao Elasticsearch, extraímos os dados relevantes e os formatamos de acordo com o que precisamos para a nossa aplicação. A lista de artistas é montada e pronta para ser armazenada no Redis, para que possa ser utilizada em futuras requisições sem a necessidade de uma nova consulta ao Elasticsearch.
-
A última etapa é armazenar os dados no Redis com um tempo de expiração. Isso significa que, após um período de tempo pré-definido, os dados serão automaticamente removidos do cache, forçando a consulta ao Elasticsearch novamente. Esse tempo de expiração é importante para garantir que o cache não seja usado por um período excessivo, evitando a exibição de dados desatualizados:
A partir desse momento, qualquer nova requisição ao mesmo endpoint durante a próxima hora (3600 segundos) retornará os dados diretamente do Redis, economizando tempo de processamento e melhorando a performance da aplicação.
A grande vantagem dessa estratégia é a significativa redução do tempo de resposta em chamadas subsequentes, já que a consulta ao Elasticsearch, que pode ser relativamente lenta dependendo do tamanho dos dados, é evitada. Em vez disso, o Redis fornece os dados de maneira muito mais rápida. Quando o tempo de expiração do cache é atingido, a aplicação automaticamente faz uma nova consulta ao Elasticsearch e armazena os dados atualizados.
Além disso, existem bibliotecas como o fastapi-cache que facilitam ainda mais a implementação de caching no FastAPI, oferecendo decorações para cache, suporte a múltiplas bases de dados de cache (incluindo Redis e memória local), e controle do tempo de vida dos dados armazenados. A utilização dessas bibliotecas pode simplificar a configuração de cache em múltiplos endpoints de forma mais automatizada.
Ao trabalhar com Redis e FastAPI, é essencial lembrar que o gerenciamento de cache deve ser feito com cautela. É importante definir corretamente o tempo de expiração para evitar que os dados fiquem desatualizados. Em sistemas onde a frequência de atualização dos dados é alta, o uso de um tempo de expiração menor pode ser preferível. Por outro lado, em sistemas mais estáveis, onde os dados não mudam frequentemente, é possível aumentar esse tempo, economizando recursos.
Outro ponto a considerar é a possível complexidade do gerenciamento do cache quando múltiplos micro-serviços ou instâncias de servidores estão envolvidos. A configuração de um Redis em cluster ou o uso de mecanismos de cache distribuído pode ser necessário para garantir que todos os servidores tenham acesso consistente aos dados em cache.
A implementação de cache com Redis no FastAPI é apenas um dos muitos aspectos que podem ser otimizados para melhorar o desempenho da sua aplicação. Além de melhorar a velocidade de resposta dos endpoints, o uso de cache também pode reduzir a carga nos sistemas de backend, diminuindo a quantidade de consultas a bancos de dados e outros sistemas de armazenamento de dados.
Em suma, a integração do FastAPI com Redis oferece uma solução poderosa para otimizar a performance de APIs, especialmente em cenários onde os dados são frequentemente acessados mas raramente alterados. Com uma configuração simples e práticas adequadas de gerenciamento de cache, é possível criar aplicações web rápidas e escaláveis, proporcionando uma experiência mais eficiente para os usuários.
Como Gerenciar Conexões WebSocket no FastAPI
Durante o processo de conexão, o servidor deve verificar a mensagem recebida: "Welcome to the chat room!". Isso indica que o ponto de extremidade WebSocket foi criado corretamente e está funcionando adequadamente. O parâmetro WebSocket no ponto de extremidade representa uma conexão WebSocket individual. Quando o método websocket.accept() é aguardado, o servidor estabelece a conexão com o cliente (um processo tecnicamente conhecido como "handshake" HTTP). Em seguida, o comando websocket.send_text() envia uma mensagem para o cliente, e finalmente, websocket.close() encerra a conexão.
Essa configuração, embora mínima e com pouca utilidade prática, representa o básico para uma conexão WebSocket. Em seguida, veremos como trocar mensagens entre o cliente e o servidor por meio de um ponto de extremidade WebSocket.
É importante observar que, até o momento da escrita deste texto, a documentação do Swagger não oferece suporte para pontos de extremidade WebSocket. Isso significa que, se você iniciar o servidor e abrir o Swagger em http://localhost:8000/docs, não verá o ponto de extremidade que acabamos de criar. Existe uma discussão ativa sobre esse tópico no repositório oficial do FastAPI no GitHub, onde você pode acompanhar as atualizações.
As conexões WebSocket permitem comunicação bidirecional entre clientes e servidores, possibilitando a troca em tempo real de mensagens. Com essa funcionalidade, é possível criar aplicações como salas de bate-papo, onde as mensagens são enviadas e recebidas de maneira contínua e dinâmica. No exemplo que iremos seguir, configuraremos um ponto de extremidade WebSocket para que o servidor possa receber mensagens do cliente e imprimi-las no terminal.
Para implementar isso, começamos definindo o "logger" (registro de logs) usando o pacote uvicorn, que é o mesmo utilizado pelo FastAPI para gerenciamento de logs. No arquivo main.py, escrevemos o seguinte código:
Agora, vamos modificar a função ws_endpoint, responsável pelo ponto de extremidade WebSocket:
Perceba que removemos a chamada para websocket.close() e substituímos por um loop infinito. Isso permite que o servidor continue recebendo mensagens do cliente e imprimindo-as no terminal, sem fechar a conexão automaticamente. Nesse caso, a conexão só será encerrada quando o cliente a fechar. A cada mensagem recebida, o servidor imprime a mensagem no terminal e envia uma confirmação ao cliente.
Para testar, inicie o servidor com o comando uvicorn app.main:app no terminal e abra o Postman. Em seguida, crie uma nova requisição WebSocket e conecte-se ao endereço ws://localhost:8000/ws. Quando a conexão for estabelecida, você verá no terminal a mensagem: "INFO: connection open", e na resposta do Postman aparecerá: "Welcome to the chat room!". Agora, envie uma mensagem do Postman, como "Hello FastAPI application". Você verá no terminal: "INFO: Message received: Hello FastAPI application", e no painel de mensagens do cliente, a resposta será: "Message received!".
Agora que o servidor está configurado para receber mensagens do cliente, você tem uma comunicação bidirecional funcionando por meio de WebSockets. Esse tipo de comunicação em tempo real é a base para muitas aplicações modernas, como chats, jogos online, e outros sistemas que exigem troca instantânea de dados entre o cliente e o servidor.
Ao desenvolver aplicações com WebSockets, é essencial também gerenciar as desconexões corretamente. Isso garante que a comunicação entre cliente e servidor não seja interrompida de maneira abrupta, o que poderia causar falhas no sistema.
Quando o cliente se desconecta, é necessário capturar essa desconexão para que o servidor possa tratar o evento adequadamente, sem gerar erros inesperados. No caso de uma desconexão do lado do cliente, o FastAPI gera uma exceção WebSocketDisconnect. Vamos ajustar o código para lidar com essa situação:
Agora, se o cliente se desconectar, o servidor não gerará um erro, e em vez disso, registrará uma mensagem de aviso: "Connection closed by the client". Isso permite que o servidor continue funcionando sem falhas mesmo quando o cliente decide encerrar a conexão.
Além disso, o servidor também pode encerrar a conexão de forma controlada, por exemplo, ao receber uma mensagem específica, como a palavra "disconnect". Veja como implementar isso:
Nesse caso, quando o servidor receber a mensagem "disconnect", ele enviará uma resposta ao cliente e encerrará a conexão de forma limpa. Esse tipo de controle é essencial para aplicações que exigem um gerenciamento eficaz das conexões WebSocket, garantindo uma comunicação contínua e sem falhas.
Além disso, o uso de WebSockets permite maior flexibilidade e desempenho em comparação com requisições HTTP tradicionais, especialmente para aplicações em tempo real. No entanto, é importante considerar o gerenciamento adequado dos recursos do servidor, pois conexões WebSocket abertas podem consumir recursos significativos, especialmente em sistemas com alta carga de usuários simultâneos.
O protocolo WebSocket se destaca por sua eficiência na troca de dados em tempo real, oferecendo vantagens significativas em relação a outras abordagens de comunicação cliente-servidor, como long polling ou requisições periódicas via HTTP.
Como Integrar o FastAPI com o LangChain para Criar Assistentes de IA Dinâmicos
O LangChain é uma interface versátil para trabalhar com Modelos de Linguagem de Grande Escala (LLM), permitindo que desenvolvedores criem aplicativos de LLM e os integrem com fontes de dados externas e fluxos de trabalho de software. Desde seu lançamento em 2022, tornou-se rapidamente um dos projetos de código aberto mais populares no GitHub. Neste capítulo, exploraremos como integrar o FastAPI com o LangChain para criar um assistente de IA capaz de fornecer recomendações dinâmicas em uma loja de eletrônicos. A proposta é criar uma aplicação que utilize um modelo de Geração Aumentada por Recuperação (RAG), utilizando dados personalizados, como um documento de perguntas frequentes (FAQ), para treinar o modelo.
Para começar, o primeiro passo é garantir que você tenha uma chave da API do Cohere. Caso não a tenha, basta verificar a seção de preparação da receita anterior sobre como integrar o FastAPI com o Cohere. Em seguida, é necessário configurar um diretório de projeto, como ecotech_RAG, e armazenar a chave da API dentro de um arquivo .env, com a variável COHERE_API_KEY.
Além do fastapi e uvicorn, será preciso instalar pacotes adicionais para trabalhar com LangChain e com a integração do Cohere. Você pode instalar os pacotes usando o arquivo requirements.txt ou diretamente com o pip:
Com esses pacotes instalados, podemos começar a desenvolver nosso assistente de compras alimentado por IA. O LangChain fornece um conjunto de módulos interconectados que permitem construir uma cadeia de operações ligando a consulta do usuário ao modelo de saída. Abaixo, explicamos como construir a aplicação em etapas.
1. Definindo os Prompts
Para este caso específico, vamos usar um modelo de chat que recebe uma lista de mensagens como entrada. O LangChain oferece objetos de template para mensagens específicas, como a mensagem do sistema e a mensagem do usuário. A primeira parte do processo envolve definir os prompts a serem usados.
Crie um módulo chamado prompting.py no diretório raiz do projeto e defina um template para a mensagem do sistema. Este template servirá para guiar o modelo, garantindo que ele forneça respostas adequadas e dentro do contexto fornecido. O template pode ser algo como:
Com esse template, podemos definir a mensagem do sistema como segue:
A mensagem do usuário não precisa de contexto adicional e pode ser configurada assim:
Por fim, as duas mensagens podem ser agrupadas sob um objeto de template de chat, que unifica os prompts:
2. Processando e Vetorizando os Documentos
Para que nosso assistente de IA responda de forma inteligente às perguntas dos usuários, ele precisa analisar documentos específicos. Vamos criar uma pasta docs no diretório raiz do projeto para armazenar esses documentos. O arquivo de FAQ, por exemplo, pode ser baixado diretamente de um repositório GitHub ou criado manualmente. A única regra é garantir que cada pergunta e resposta estejam separadas por uma linha em branco.
No entanto, para otimizar as buscas por similaridade, precisaremos dividir os documentos em pedaços menores e armazená-los como vetores. O LangChain facilita isso com seu banco de dados vetorial em memória, o ChromaDB. Para dividir os documentos, utilizamos um "text splitter" baseado em caracteres, que divide o texto de acordo com um tamanho pré-definido.
3. Construindo a Cadeia do Modelo
Depois de definir os prompts e processar os documentos, o próximo passo é construir a cadeia de operações do modelo. Isso envolve integrar a lógica de recuperação de documentos com a geração de respostas baseadas em IA. A cadeia permite que a consulta do usuário seja mapeada para o modelo de IA, que pode gerar respostas personalizadas com base nos dados fornecidos.
4. Criando o Endpoint
A última etapa é criar o endpoint no FastAPI para que o assistente de IA possa interagir com os usuários. Para isso, utilizamos a funcionalidade de gerenciamento de contexto do FastAPI, que permite que o estado da aplicação seja mantido entre as requisições. Assim, a cada nova consulta, o assistente mantém o histórico de mensagens e pode gerar respostas mais contextuais.
Esse endpoint lida com requisições POST, onde a consulta do usuário é passada como parâmetro. A resposta do modelo é retornada ao usuário, criando uma interação dinâmica e contínua.
Além disso, uma vez que o assistente esteja em operação, seria interessante adicionar funcionalidades extras, como um endpoint GET /messages que retorna todas as mensagens trocadas ou um endpoint POST /restart-conversation que reinicia a conversa, apagando todas as mensagens anteriores.
Considerações Finais
Ao criar assistentes de IA com o LangChain e o FastAPI, é essencial entender a importância de como a interação com o modelo pode ser moldada através de prompts específicos e como os dados são processados e vetorizados para garantir respostas rápidas e relevantes. Para ambientes de produção, a escolha do modelo deve ser feita com base nas necessidades do projeto, considerando custo e desempenho.
Como Implementar Middleware de Segurança e Webhooks em FastAPI
FastAPI é uma poderosa ferramenta para criar APIs rápidas e eficientes, mas, como qualquer aplicação web, é importante garantir que apenas fontes confiáveis possam acessar a aplicação e que eventos em tempo real possam ser comunicados de forma eficiente entre sistemas. Dois conceitos essenciais nesse contexto são o middleware de segurança, como o TrustedHostMiddleware, e a implementação de webhooks para comunicação assíncrona. Neste capítulo, vamos explorar como implementar essas funcionalidades em FastAPI, garantindo a segurança da aplicação e a integração com sistemas externos.
O middleware de segurança TrustedHostMiddleware é uma ferramenta fundamental para garantir que a sua aplicação web só aceite requisições de hosts confiáveis. Ao ativar esse middleware, podemos definir explicitamente quais hosts podem interagir com a aplicação, evitando requisições maliciosas ou indesejadas. No exemplo abaixo, configuramos o middleware para permitir apenas requisições originadas de "localhost":
Essa configuração impede que a aplicação aceite requisições de qualquer outro host, protegendo-a de acessos indesejados. Se um host não autorizado tentar acessar a aplicação, a resposta será uma mensagem de erro, como "Invalid host header". Esse tipo de segurança é crucial para evitar ataques do tipo DNS rebinding ou outras formas de acesso indevido. Para testar essa configuração, basta executar o servidor com o comando:
Isso tornará a aplicação acessível pela rede local, mas com o middleware em funcionamento, qualquer tentativa de acesso de um host não permitido resultará em erro.
Além disso, a integração com webhooks tem se tornado cada vez mais comum nas arquiteturas modernas. Webhooks são mecanismos que permitem a comunicação assíncrona entre sistemas, ou seja, quando um evento ocorre em um sistema, ele pode notificar outros sistemas em tempo real. Por exemplo, imagine que você tenha uma API que deseja notificar outros serviços sempre que uma requisição for realizada. Abaixo, veremos como configurar esse sistema em FastAPI.
Primeiro, criamos um sistema simples de registro de URLs de webhooks. Quando um serviço deseja se inscrever para receber notificações de eventos, ele registra sua URL no sistema. Para isso, utilizamos um endpoint que armazena as URLs em memória, usando o state da aplicação:
Essa configuração permite que os sistemas se registrem para receber notificações, enviando a URL onde desejam ser notificados. O próximo passo é definir o comportamento dos callbacks de webhook. Sempre que um evento ocorrer, a aplicação precisa enviar uma notificação para todos os URLs registrados. Isso é feito com o seguinte código, que usa a biblioteca httpx para fazer requisições assíncronas aos URLs registrados:
Agora, vamos configurar o middleware para interceptar todas as requisições HTTP e, sempre que uma requisição ocorrer, notificar todos os URLs registrados. O middleware é responsável por criar o evento e disparar as notificações para os sistemas interessados:
Este middleware intercepta todas as requisições HTTP, cria um evento com as informações da requisição e dispara esse evento para os sistemas que se registraram. Cada sistema registrado receberá uma notificação sempre que um evento ocorrer na aplicação, permitindo uma comunicação em tempo real.
É importante notar que, embora o exemplo fornecido use a memória da aplicação para armazenar os URLs dos webhooks, em aplicações reais, é recomendável usar um banco de dados ou outra solução persistente para armazenar essas URLs. Além disso, deve-se implementar uma lógica de tratamento de falhas, como re-tentativas em caso de falha de rede ou de resposta do sistema receptor, para garantir que os eventos não sejam perdidos.
A implementação de middleware de segurança, como o TrustedHostMiddleware, e a configuração de webhooks permitem que sua aplicação FastAPI seja mais segura e interativa. Esses componentes são cruciais para garantir que apenas fontes confiáveis possam acessar a aplicação e para permitir a comunicação em tempo real com sistemas externos. Essas práticas são fundamentais para sistemas modernos e escaláveis, que exigem integração com outros serviços e notificações em tempo real.

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский