Sistemas Distribuídos - Lab Prático Server-Sent Events
Published:
1. Objetivo do Laboratório
Neste laboratório, você irá desenvolver uma aplicação web simples capaz de receber atualizações em tempo real enviadas pelo servidor.
Ao final da atividade, você deverá ser capaz de:
- Criar um servidor SSE usando Python e FastAPI.
- Enviar eventos contínuos do servidor para o navegador.
- Consumir eventos SSE no cliente usando HTML e JavaScript.
- Exibir dados recebidos em tempo real em uma página web.
- Compreender quando SSE pode ser utilizado em aplicações web reais.
2. Pré-requisitos
Antes de iniciar, você deve ter instalado:
- Python 3.10 ou superior.
- Editor de código, como VS Code.
- Navegador web atualizado.
- Conhecimento básico de HTML, JavaScript e rotas HTTP.
Também é recomendável criar um ambiente virtual:
python -m venv sse
source sse/bin/activate
3. Conceitos Envolvidos
Server-Sent Events
Server-Sent Events (SSE) permite ao servidor enviar dados continuamente para o cliente por meio de uma conexão HTTP persistente.
Diferentemente do modelo tradicional da Web, em que o cliente precisa solicitar novas informações repetidamente, no SSE o servidor pode enviar atualizações sempre que houver novos dados disponíveis.
Comunicação Unidirecional
No SSE, a comunicação ocorre principalmente em uma direção:
Servidor → Cliente
4. Estrutura do Projeto
Crie uma pasta chamada:
laboratorio-sse-fastapi
Dentro dela, crie os seguintes arquivos:
laboratorio-sse-fastapi/
│
├── main.py
└── index.html
5. Instalação das Dependências
Instale o FastAPI e o Uvicorn:
pip install fastapi uvicorn
FastAPI é um framework web moderno, escrito em Python, projetado especificamente para construir APIs (Application Programming Interfaces) de maneira rápida e robusta. Ele não é apenas uma biblioteca; ele é um conjunto completo de ferramentas que simplificam drasticamente o desenvolvimento backend. O FastAPI cuida da estrutura e da segurança dos seus endpoints. Ele permite que você se concentre na lógica de negócio, sabendo que o framework cuidará da validação e do formato das respostas.
Uvicorn é um servidor web (Web Server Gateway Interface - WSGI/ASGI). Ele não é uma biblioteca de código; ele é a máquina que executa o seu código Python. Quando você escreve uma aplicação Web em Python, ele precisa ser executado por algum motor. O FastAPI define as regras, mas alguém precisa rodar essas regras no tempo real. É aí que entra o servidor.
ASGI (Asynchronous Server Gateway Interface): Este é o conceito mais importante. Uvicorn implementa o padrão ASGI. Enquanto servidores antigos (como os baseados em WSGI) eram síncronos (eles processavam uma requisição por vez, esperando ela terminar), o ASGI permite que o servidor seja assíncrono.
6. Passo a Passo
Passo 1 — Criar o Servidor FastAPI
No arquivo main.py, adicione o seguinte código:
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from fastapi.middleware.cors import CORSMiddleware
import time
import json
from datetime import datetime
app = FastAPI()
# Middleware de CORS (obrigatório para testes locais com HTML)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Em produção, restrinja a origem específica
allow_credentials=True,
allow_methods=["GET"],
allow_headers=["Content-Type"],
)
def gerar_eventos():
contador = 0
while True:
contador += 1
dados = {
"contador": contador,
"mensagem": "Evento recebido do servidor",
"horario": datetime.now().strftime("%H:%M:%S")
}
# Formato SSE obrigatório: "data: <conteúdo>\n\n"
evento = f"data: {json.dumps(dados)}\n\n"
yield evento
time.sleep(2)
@app.get("/eventos")
def eventos():
return StreamingResponse(
gerar_eventos(),
media_type="text/event-stream"
)
Explicação do Código
FastAPI()cria a aplicação web.StreamingResponsepermite enviar dados em fluxo contínuo.- A função
gerar_eventos()gera eventos repetidamente. - O comando
yieldenvia cada evento para o cliente sem encerrar a conexão. - O tipo
text/event-streaminforma ao navegador que a resposta será um fluxo SSE.
Passo 2 — Executar o Servidor
No terminal, dentro da pasta do projeto, execute:
uvicorn main:app --reload
Se tudo estiver correto, o servidor será iniciado em:
http://127.0.0.1:8000
Teste a rota SSE acessando:
http://127.0.0.1:8000/eventos
Você deverá observar eventos sendo enviados continuamente.
Importante: Houve reports de problemas em iniciar o servidor uvicorn com comando acima. No caso, o erro produzido foi
ERROR: Error loading ASGI app. Could not import module "main".Se isso ocorrer, você pode adicionar o código de inicalização do uvicorn diretamete no final arquivoapp.py:if __name__ == "__main__": uvicorn.run("__main__:app", host="0.0.0.0", port=8000, reload=True, workers=2)
Passo 3 — Criar o Cliente HTML
No arquivo index.html, adicione o seguinte código:
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Laboratório SSE com FastAPI</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 40px;
background-color: #f5f5f5;
}
h1 {
color: #333;
}
#status {
font-weight: bold;
margin-bottom: 20px;
}
.evento {
background-color: white;
border-left: 5px solid #2f80ed;
padding: 12px;
margin-bottom: 10px;
border-radius: 4px;
}
</style>
</head>
<body>
<h1>Cliente SSE com HTML e JavaScript</h1>
<p id="status">Conectando ao servidor...</p>
<h2>Eventos recebidos</h2>
<div id="eventos"></div>
<script>
const status = document.getElementById("status");
const eventos = document.getElementById("eventos");
const fonteEventos = new EventSource("http://127.0.0.1:8000/eventos");
fonteEventos.onopen = function() {
status.textContent = "Conectado ao servidor SSE.";
};
fonteEventos.onmessage = function(event) {
const dados = JSON.parse(event.data);
const novoEvento = document.createElement("div");
novoEvento.className = "evento";
novoEvento.innerHTML = `
<strong>Evento ${dados.contador}</strong><br>
Mensagem: ${dados.mensagem}<br>
Horário: ${dados.horario}
`;
eventos.prepend(novoEvento);
};
fonteEventos.onerror = function() {
status.textContent = "Erro na conexão com o servidor SSE.";
};
</script>
</body>
</html>
Passo 4 — Abrir o Cliente no Navegador
Abra o arquivo index.html diretamente no navegador.
Você deverá visualizar:
- Uma mensagem de conexão.
- Novos eventos sendo adicionados automaticamente à página.
- Um novo evento a cada 2 segundos.
7. Teste de Funcionamento
Verifique se:
- O servidor está rodando com
uvicorn. - O navegador consegue acessar a rota
/eventos. - A página HTML recebe novos eventos sem precisar ser recarregada.
- Os dados aparecem em ordem decrescente, com o evento mais recente no topo.
8. Exercícios
Exercício 1: Alterar o Intervalo dos Eventos
Altere o servidor para enviar eventos a cada 1 segundo em vez de 2 segundos.
Dica:
time.sleep(1)
Exercício 2: Enviar Temperatura Simulada
Modifique o servidor para enviar uma temperatura aleatória entre 20 e 40 graus.
Dica:
import random
temperatura = random.randint(20, 40)
O JSON enviado pelo servidor deve ter o seguinte formato:
{
"contador": 1,
"temperatura": 28,
"horario": "14:30:10"
}
Exercício 3: Criar Alerta de Temperatura
No cliente HTML, exiba uma mensagem de alerta quando a temperatura for maior que 35 graus.
Exemplo de saída esperada:
Alerta: temperatura elevada!
Exercício Extra
Você deverá simular um sistema de monitoramento enviando múltiplas variáveis:
- Temperatura
- Umidade
- Pressão
Exemplo de estrutura de dados:
{
"temperatura": 28,
"umidade": 60,
"pressao": 1012,
"horario": "14:35:10"
}
O servidor deve enviar esses dados a cada 2 segundos.
No cliente:
- Exibir cada variável em um painel separado.
- Atualizar os valores dinamicamente.
- Exibir o horário da última atualização.
- Horário da última atualização.
- Quantidade total de eventos recebidos.
- Alerta visual quando a temperatura estiver acima de 35 graus.
Resultado Esperado
Ao final do laboratório, a aplicação deve apresentar:
- Um servidor FastAPI com uma rota SSE.
- Um cliente HTML conectado usando
EventSource. - Atualizações automáticas na página.
- Dados recebidos em tempo real.
- Pelo menos uma melhoria implementada nos exercícios.
Questões para Reflexão
- Por que SSE é mais eficiente que polling em determinados cenários?
- Em quais situações SSE seria mais adequado que WebSockets?
- Qual é a função do cabeçalho
text/event-stream? - Por que o servidor precisa manter a conexão aberta?
- Quais limitações devem ser consideradas ao usar SSE?
