Introdução
Este projeto utiliza SSE (Server-Sent Events) e Redis para implementar uma sala de chat online. É necessário conhecimento prévio da sintaxe de Python e JavaScript, bem como uma compreensão básica do uso de Flask e Redis.
Nesta seção do experimento, aprenderemos e praticaremos os seguintes conceitos:
- Comunicação web em tempo real
- O funcionamento do SSE
- Utilização do Redis
👀 Pré-visualização

🎯 Tarefas
Neste projeto, você aprenderá:
- Como criar uma sala de chat online simples usando Flask e SSE
- Como implementar a funcionalidade de login do usuário
- Como usar o Redis para armazenamento e recuperação de mensagens
🏆 Conquistas
Após concluir este projeto, você será capaz de:
- Configurar SSE para comunicação em tempo real em uma aplicação web
- Usar o Redis para armazenar e recuperar mensagens em uma aplicação de sala de chat
- Implementar a funcionalidade de login do usuário em Flask
Comunicação Web em Tempo Real
A Comunicação Web em Tempo Real (WebRTC) refere-se a um mecanismo que nos permite notificar instantaneamente os usuários sobre um evento em uma página web sem a necessidade de que eles atualizem a página. Existem muitos usos para WebRTC, como bate-papo em tempo real e mensagens instantâneas.
Existem várias maneiras de comunicação entre clientes web e servidores.
Fluxo HTTP Regular
- O cliente solicita uma página web do servidor.
- O servidor responde de acordo.
- O servidor envia a resposta de volta para o cliente.

Como as requisições HTTP são stateless (sem estado), o que significa que a conexão HTTP é encerrada após cada requisição, o servidor e o navegador não têm conhecimento um do outro até que a próxima requisição seja feita para atualizar as informações relevantes. Neste ponto, não é difícil pensar em uma solução simples onde o navegador pode fazer requisições periódicas para simular efeitos em tempo real. Isso é chamado de polling.
Fluxo de Polling
- O cliente envia uma requisição de conexão para o servidor usando HTTP regular.
- O cliente executa um script JavaScript de polling embutido na página web para enviar periodicamente requisições ao servidor (por exemplo, a cada 5 segundos) para recuperar informações.
- O servidor responde a cada requisição e envia de volta as informações correspondentes, assim como uma requisição HTTP normal.

O polling nos permite obter informações quase em tempo real. No entanto, requisições frequentes do navegador para o servidor como resultado do polling podem levar a ineficiências de desempenho. Para mitigar esses problemas, foi proposto um método alternativo. Em vez de responder imediatamente às requisições do cliente, os servidores esperam até que haja uma mudança de dados (ou timeout) antes de retornar uma resposta. Essa abordagem maximiza a validade da conexão para reduzir o número de requisições no polling. Este método é chamado de long polling, ou Long-Polling.
Fluxo de Long-Polling
- O cliente solicita uma página web do servidor usando HTTP regular.
- O cliente executa um script JavaScript embutido na página web para enviar dados e solicitar informações ao servidor.
- Em vez de responder imediatamente à requisição do cliente, o servidor espera por uma atualização válida.
- Quando a informação é atualizada e válida, o servidor envia os dados para o cliente.
- Ao receber a notificação do servidor, o cliente envia imediatamente uma nova requisição para iniciar a próxima rodada de polling.

Os métodos mencionados acima são comumente usados para implementar comunicação web em tempo real. No entanto, após a introdução do HTML5, temos melhores opções disponíveis. No HTML5, podemos usar Server-Sent Events (SSE) ou WebSocket. SSE é especificamente projetado para envio de dados do servidor para o cliente, o que é frequentemente suficiente para cenários como transmissão de informações de partidas ou alterações de preços de ações.
Fluxo de Server-Sent Events
- O cliente solicita uma página web do servidor usando HTTP regular.
- O cliente estabelece uma conexão com o servidor usando JavaScript embutido na página web.
- Quando o servidor tem atualizações, ele envia um evento para o cliente.

Se SSE não atender às nossas necessidades, podemos usar WebSocket em vez disso. Com WebSocket, um canal de comunicação full-duplex é estabelecido entre o navegador e o servidor, permitindo a troca de mensagens bidirecional em tempo real, semelhante ao uso de sockets TCP.
Comparação Simples entre SSE e WebSocket
- WebSocket é um canal de comunicação full-duplex que suporta comunicação bidirecional e possui recursos mais avançados. SSE é um canal unidirecional, onde o servidor só pode enviar dados para o navegador.
- WebSocket é um novo protocolo e requer suporte do lado do servidor, enquanto SSE é implantado em cima do protocolo HTTP e é suportado pelo software de servidor existente.
- SSE é um protocolo leve e relativamente mais simples, enquanto WebSocket é um protocolo mais pesado e relativamente mais complexo.
Com a compreensão desses mecanismos para implementar comunicação web em tempo real, agora usaremos SSE para implementar uma sala de chat online simples.
Implementando uma Sala de Chat Online Baseada em SSE
Existem várias maneiras de enviar mensagens em uma sala de chat online, e neste curso, usaremos Server-Sent Events (SSE) para conseguir isso. Para facilitar a recepção de mensagens, aproveitaremos a funcionalidade pub/sub (publicar/inscrever-se) do Redis para receber e enviar mensagens. No lado do servidor web, usaremos Flask para a implementação.
Como o SSE Funciona
Nas lições anteriores, aprendemos que SSE é baseado em HTTP. Então, como o navegador sabe que este é um fluxo de eventos enviados pelo servidor? É realmente muito simples – basta definir o cabeçalho Content-Type da requisição HTTP para text/event-stream. SSE essencialmente envolve o navegador enviando uma requisição HTTP para o servidor, e então o servidor continuamente enviando informações para o navegador de maneira unidirecional. O formato dessas informações também é muito direto, com o prefixo "data:" seguido pelo conteúdo da mensagem e terminando com "\n\n".
Funcionalidade Pub/Sub do Redis
Redis é um banco de dados in-memory (na memória) popular que pode ser usado para caching (armazenamento em cache), queuing (enfileiramento) e outros serviços. Neste curso, usaremos a funcionalidade de publicar/inscrever-se do Redis. Simplificando, o recurso de inscrição nos permite nos inscrever em vários canais e, sempre que novas mensagens são publicadas nesses canais, as recebemos automaticamente. Quando o servidor recebe uma mensagem enviada pelo navegador via uma requisição POST, ele publica essas mensagens em canais específicos. Posteriormente, os clientes que se inscreveram nesses canais receberão automaticamente essas mensagens, que serão então enviadas aos clientes através do SSE.
Implementação da Função
Após a análise acima, todo o processo da sala de chat já está claro. Agora, vamos começar a implementar a funcionalidade desta sala de chat.
Crie um arquivo chamado app.py no diretório ~/project e insira o seguinte código-fonte:
import datetime
import flask
import redis
app = flask.Flask("labex-sse-chat")
app.secret_key = "labex"
app.config["DEBUG"] = True
r = redis.StrictRedis()
## Função da rota inicial
@app.route("/")
def home():
## Se o usuário não estiver logado, redireciona para a página de login
if "user" not in flask.session:
return flask.redirect("/login")
user = flask.session["user"]
return flask.render_template("index.html", user=user)
## Gerador de mensagens
def event_stream():
## Cria um sistema de publicar-inscrever-se
pubsub = r.pubsub()
## Usa o método subscribe do sistema de publicar-inscrever-se para se inscrever em um canal
pubsub.subscribe("chat")
for message in pubsub.listen():
data = message["data"]
if type(data) == bytes:
yield "data: {}\n\n".format(data.decode())
## Função de login, login é necessário para a primeira visita
@app.route("/login", methods=["GET", "POST"])
def login():
if flask.request.method == "POST":
## Armazena o nome de usuário no dicionário de sessão e, em seguida, redireciona para a página inicial
flask.session["user"] = flask.request.form["user"]
return flask.redirect("/")
return flask.render_template("login.html")
## Recebe dados enviados por JavaScript usando o método POST
@app.route("/post", methods=["POST"])
def post():
message = flask.request.form["message"]
user = flask.session.get("user", "anonymous")
now = datetime.datetime.now().replace(microsecond=0).time()
r.publish("chat", "[{}] {}: {}\n".format(now.isoformat(), user, message))
return flask.Response(status=204)
## Interface de fluxo de eventos
@app.route("/stream")
def stream():
## O objeto de retorno desta função de rota deve ser do tipo text/event-stream
return flask.Response(event_stream(), mimetype="text/event-stream")
## Executa a aplicação Flask
app.run()
No código acima, usamos o recurso de sessão do Flask para armazenar informações de login do usuário, o recurso de publicar-inscrever-se do Redis para receber e enviar mensagens e SSE para implementar o envio de mensagens.
Aqui:
- A função
event_streamé um gerador de mensagens que recupera continuamente mensagens do Redis e as envia para o cliente. - A função
streamé uma interface de fluxo de eventos que retorna um objeto do tipotext/event-stream, que é o fluxo de eventos SSE. - A função
posté uma interface que recebe dados enviados por JavaScript usando o método POST. Ela publica os dados recebidos no canalchatno Redis. - A função
loginé uma função de login que é necessária para a primeira visita. Após um login bem-sucedido, o nome de usuário é armazenado no dicionário de sessão e, em seguida, redirecionado para a página inicial. - A função
homeé a função da rota da página inicial. Se o usuário não estiver logado, ele será redirecionado para a página de login. Se o usuário estiver logado, ele renderizará o templateindex.html.
Implementando o Template login.html
Primeiramente, crie um diretório templates em ~/project para armazenar os arquivos HTML necessários. No diretório templates, crie um arquivo login.html e escreva o seguinte código:
<!doctype html>
<title>Online Chat Login</title>
<style>
body {
max-width: 500px;
margin: auto;
padding: 1em;
background: black;
color: #fff;
font:
16px/1.6 menlo,
monospace;
}
</style>
<body>
<form action="" method="post">
User Name: <input name="user" />
<input type="submit" value="login" />
</form>
</body>
No arquivo login.html, utilizamos a funcionalidade de template do Flask e usamos {{ user }} para representar o nome do usuário, que é recuperado de flask.session.
Implementando o Template index.html
Em seguida, vamos criar um arquivo index.html no diretório templates para implementar a página da sala de chat. Escreva o seguinte código nele:
<!doctype html>
<title>Online Chat</title>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<style>
body {
max-width: 500px;
margin: auto;
padding: 1em;
background: black;
color: #fff;
font:
16px/1.6 menlo,
monospace;
}
</style>
<p><b>Hi, {{ user }}!</b></p>
<p>Message: <input id="in" /></p>
<pre id="out"></pre>
<script>
function sse() {
// Connect to server's event stream
var source = new EventSource("/stream");
var out = document.getElementById("out");
source.onmessage = function (e) {
out.innerHTML = e.data + "\n" + out.innerHTML;
};
}
// POST message to server
$("#in").keyup(function (e) {
if (e.keyCode == 13) {
$.post("/post", { message: $(this).val() });
$(this).val("");
}
});
sse();
</script>
No arquivo index.html, usamos o método $.post do jQuery para enviar mensagens para o servidor e usamos EventSource para receber mensagens do tipo server-push.
Executando e Testando
Como o Redis está sendo usado, você precisa iniciar o serviço Redis no seu ambiente e baixar o módulo redis necessário para conectar o Python ao servidor Redis:
pip install redis
sudo service redis-server start
Em seguida, você pode executar nossa sala de chat:
cd ~/project
python app.py
Então, você pode acessar http://localhost:5000 no seu navegador, inserir um nome de usuário aleatório e entrar na sala de chat.
Você também pode abrir outra janela do navegador no modo privado para entrar na sala de chat e, em seguida, pode conversar em ambas as janelas.

Resumo
Este projeto utiliza SSE para implementar recursos de comunicação web em tempo real e se baseia em Flask e Redis para criar uma sala de chat online. O objetivo é que todos obtenham uma compreensão do processo de comunicação sob o protocolo HTTP através desta seção do experimento.



