Pular para o conteúdo
Gestão

Chat em tempo real e dashboards live com WebSockets

Admin5 min de leitura
Chat em tempo real e dashboards live com WebSockets

Chat em tempo real e dashboards live com WebSockets

Tecnologia e Inovação

Introdução

Nos últimos anos, a expectativa dos usuários por interatividade instantânea cresceu exponencialmente. Seja em um aplicativo de mensagens corporativas, em um sistema de alertas de estoque ou em painéis de monitoramento que exibem métricas em tempo real, a latência percebida pode ser o diferencial entre a adoção ou o abandono da solução.

Para atender a esse requisito, a tecnologia de WebSockets se consolidou como a escolha mais robusta quando a necessidade é comunicação full‑duplex (bidirecional) entre cliente e servidor. Diferente das requisições HTTP tradicionais, que seguem o modelo request/response, um canal WebSocket permanece aberto, permitindo que ambas as partes enviem mensagens a qualquer momento, sem overhead de novas conexões.

Neste artigo, vamos:

  • Revisar os conceitos fundamentais dos WebSockets e como eles se comparam a outras técnicas de push (SSE, long‑polling);
  • Desenhar uma arquitetura típica para chat, notificações e dashboards live;
  • Implementar, passo a passo, um chat simples usando FastAPI (Python) e um painel de métricas ao vivo usando Redis Pub/Sub como broker;
  • Apresentar boas práticas de segurança, escalabilidade e monitoramento.
  • Dica: Embora o foco seja o uso de WebSockets, as ideias aqui podem ser adaptadas a outras bibliotecas como Socket.IO ou protocolos como STOMP sem grandes mudanças na lógica de negócio.


    1. Conceitos fundamentais dos WebSockets

    1.1 Handshake e upgrade da conexão

    A negociação de um WebSocket inicia-se com um handshake HTTP padrão. O cliente envia um GET com o cabeçalho Upgrade: websocket e o servidor responde com 101 Switching Protocols se aceitar a conexão.

    GET /ws/chat HTTP/1.1
    

    Host: example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13

    Se tudo ocorrer bem, o canal passa a operar em modo binário ou texto, com mensagens delimitadas por frames. Essa troca ocorre uma única vez, reduzindo o custo de estabelecimento comparado a múltiplas requisições HTTP.

    1.2 Modelo de mensagem

    • Texto: ideal para JSON, XML ou simples strings.
    • Binário: usado quando se envia arquivos, imagens ou blobs compactados.
    A escolha depende do volume e da frequência de dados. Em dashboards, normalmente usamos JSON leve; em chats corporativos, texto simples costuma ser suficiente.

    1.3 Comparativo rápido

    TécnicaLatência médiaComplexidadeCompatibilidade
    WebSocket< 10 msMédiaNavegadores modernos
    Server‑Sent Events (SSE)30‑50 msBaixaNavegadores modernos (unidirecional)
    Long‑polling200‑500 msAltaTodos (fallback)

    WebSockets são a única solução bidirecional com latência mínima, tornando‑os ideais para casos onde o servidor precisa “empurrar” informações ao cliente em tempo real.


    2. Arquitetura recomendada para chat, notificações e dashboards

    2.1 Componentes principais

  • Servidor de aplicação – hospeda a lógica de negócios e abre endpoints WebSocket. Pode ser escrito em Python (FastAPI), Go, Java, etc.
  • Broker de mensagens – opcional, mas altamente recomendado quando há múltiplas instâncias do servidor. Ele garante que mensagens enviadas por um nó cheguem a todos os demais. Exemplos: Redis Pub/Sub, RabbitMQ, Kafka.
  • Cliente – aplicação web ou mobile que se conecta ao endpoint WebSocket e processa as mensagens recebidas.
  • Camada de autenticação – token JWT ou sessão segura, validada durante o handshake.
  • 2.2 Fluxo de mensagens

    [Cliente] -- (WebSocket) --> [Servidor A] -- (Redis Pub/Sub) --> [Servidor B] -- (WebSocket) --> [Cliente]
    • O cliente abre a conexão e envia seu token.
    • O servidor valida o token e associa a conexão a um channel interno (ex.: user:1234).
    • Quando o cliente envia uma mensagem, o servidor publica no broker (ex.: chat:room42).
    • Todos os servidores que têm assinantes naquele canal recebem a mensagem e a repassam via WebSocket.

    2.3 Por que usar um broker?

    • Escalabilidade horizontal: novas réplicas do servidor podem ser adicionadas sem perder a consistência das mensagens.
    • Persistência opcional: alguns brokers permitem armazenar mensagens até que o consumidor esteja online (útil para notificações offline).
    • Desacoplamento: a lógica de entrega fica separada da lógica de negócio, facilitando manutenção.


    3. Implementando um chat simples com FastAPI + WebSockets

    Pré‑requisitos: Python 3.9+, fastapi, uvicorn, python‑dotenv. Opcionalmente, redis para broker.

    3.1 Estrutura de pastas

    chat-app/
    

    ├─ app/ │ ├─ __init__.py │ ├─ main.py │ ├─ router.py │ └─ utils.py ├─ .env └─ requirements.txt

    3.2 Código do servidor

    requirements.txt

    fastapi
    

    uvicorn[standard] python-dotenv redis

    app/main.py

    ``python import os import json import uuid from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends from fastapi.middleware.cors import CORSMiddleware from dotenv import load_dotenv import redis.asyncio as aioredis

    load_dotenv() REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379")

    app = FastAPI(title="Chat em tempo real")

    Permitir chamadas do front-end (ajuste conforme seu domínio)

    app.add_middleware( CORSMiddleware, allow_origins=[""], allow_credentials=True, allow_methods=[""], allow_headers=["*"], )

    redis_client = aioredis.from_url(REDIS_URL, decode_responses=True)

    class ConnectionManager: """Gerencia conexões WebSocket por sala.""" def __init__(self): self.active_connections: dict[str, set[WebSocket]] = {}

    async def connect(self, websocket: WebSocket, room: str): await websocket.accept() self.active_connections.setdefault(room, set()).add(websocket)

    def disconnect(self, websocket: WebSocket, room: str): self.active_connections[room].remove(websocket) if not self.active_connections[room]: del self.active_connections[room]

    async def broadcast(self, room: str, message: dict): if room not in self.active_connections: return data = json.dumps(message) for connection in self.active_connections[room]: await connection.send_text(data)

    manager = ConnectionManager()

    @app.websocket("/ws/chat/{room_id}") async def chat_endpoint(websocket: WebSocket, room_id: str): """ Endpoint WebSocket para salas de chat. O cliente deve enviar um JSON com a chave username` na primeira mensagem. """ await manager.connect(websocket, room_id)

    try: # Primeiro payload: identificação do usuário init_msg = await websocket.receive_text() init_data = json.loads(init_msg) username = init_data.get("username", f"user_{uuid.uuid4().hex[:6]}")

    # Notifica a entrada na sala await manager.broadcast( room_id, {"type": "join", "user": username, "msg": f"{username} entrou na sala."} )

    # Loop principal de recebimento while True: raw = await websocket.receive_text() payload = json.loads(raw) await manager.broadcast( room_id, {"type": "msg", "user": username, "msg": payload.get("msg", "")} ) except WebSocketDisconnect: manager.disconnect(websocket, room_id) await manager.broadcast( room_id, {"type

    Artigos relacionados