Dieses Projekt nutzt Server-Sent Events (SSE) und Redis, um einen Online-Chatraum zu implementieren. Kenntnisse der Python- und JavaScript-Syntax sowie ein grundlegendes Verständnis der Verwendung von Flask und Redis sind erforderlich.
In diesem Abschnitt der Experimente werden wir die folgenden Konzepte lernen und üben:
Web-Real-Time-Kommunikation
Die Funktionsweise von SSE
Die Verwendung von Redis
👀 Vorschau
🎯 Aufgaben
In diesem Projekt wirst du lernen:
Wie man einen einfachen Online-Chatraum mit Flask und SSE erstellt
Wie man die Benutzeranmeldung implementiert
Wie man Redis für das Speichern und Abrufen von Nachrichten verwendet
🏆 Errungenschaften
Nach Abschluss dieses Projekts wirst du in der Lage sein:
SSE für die Echtzeitkommunikation in einer Webanwendung einzurichten
Redis zum Speichern und Abrufen von Nachrichten in einer Chatraum-Anwendung zu verwenden
Die Benutzeranmeldung in Flask zu implementieren
Web-Real-Time-Kommunikation
Web-Real-Time-Kommunikation (WebRTC) bezieht sich auf einen Mechanismus, der es uns ermöglicht, Benutzer sofort über ein Ereignis auf einer Webseite zu informieren, ohne dass sie die Seite aktualisieren müssen. Es gibt viele Anwendungen von WebRTC, wie z. B. Echtzeit-Chat und Instant-Messaging.
Es gibt mehrere Arten der Kommunikation zwischen Web-Clients und Servern.
Regelmäßiger HTTP-Fluss
Der Client fordert eine Webseite vom Server an.
Der Server antwortet entsprechend.
Der Server sendet die Antwort zurück an den Client.
Da HTTP-Anfragen zustandslos sind, was bedeutet, dass die HTTP-Verbindung nach jeder Anfrage beendet wird, sind der Server und der Browser völlig unbekannt voneinander, bis eine neue Anfrage gestellt wird, um die relevanten Informationen zu aktualisieren. An diesem Punkt fällt es nicht schwer, eine einfache Lösung zu finden, bei der der Browser periodische Anfragen stellen kann, um Echtzeit-Effekte zu simulieren. Dies wird als Polling bezeichnet.
Polling-Fluss
Der Client sendet eine Verbindungsanfrage an den Server über reguläres HTTP.
Der Client führt ein in der Webseite eingebettetes JavaScript-Polling-Skript aus, um periodisch (z. B. alle 5 Sekunden) Anfragen an den Server zu senden, um Informationen abzurufen.
Der Server antwortet auf jede Anfrage und sendet die entsprechenden Informationen zurück, genau wie bei einer normalen HTTP-Anfrage.
Polling ermöglicht es uns, Informationen in nahezu Echtzeit zu erhalten. Allerdings können häufige Anfragen des Browsers an den Server aufgrund von Polling zu Leistungseinbußen führen. Um diese Probleme zu mildern, wurde eine alternative Methode vorgeschlagen. Anstatt sofort auf Clientanfragen zu antworten, warten Server, bis sich ein Datenwechsel (oder ein Timeout) ereignet, bevor sie eine Antwort zurückgeben. Diese Methode maximiert die Verbindungsgültigkeit, um die Anzahl der Anfragen bei Polling zu reduzieren. Diese Methode wird als Long-Polling oder Long-Polling bezeichnet.
Long-Polling-Fluss
Der Client fordert eine Webseite vom Server an über reguläres HTTP.
Der Client führt ein in der Webseite eingebettetes JavaScript-Skript aus, um Daten zu senden und Informationen vom Server anzufragen.
Anstatt sofort auf die Anfrage des Clients zu antworten, wartet der Server auf eine gültige Aktualisierung.
Wenn die Informationen aktualisiert und gültig sind, schiebt der Server die Daten an den Client.
Nachdem der Client die Benachrichtigung des Servers erhalten hat, sendet er sofort eine neue Anfrage, um die nächste Runde des Pollings zu initiieren.
Die oben genannten Methoden werden üblicherweise verwendet, um Echtzeit-Web-Kommunikation zu implementieren. Nachdem HTML5 eingeführt wurde, haben wir jedoch bessere Optionen. In HTML5 können wir Server-Sent Events (SSE) oder WebSocket verwenden. SSE ist speziell für das Server-zu-Client-Daten-Pushing konzipiert, was für Szenarien wie die Übertragung von Wettkampfinformationen oder Aktienkursänderungen oft ausreicht.
Server-Sent Events-Fluss
Der Client fordert eine Webseite vom Server an über reguläres HTTP.
Der Client etabliert eine Verbindung mit dem Server mithilfe von in der Webseite eingebettetem JavaScript.
Wenn der Server Updates hat, sendet er ein Ereignis an den Client.
Wenn SSE unsere Anforderungen nicht erfüllt, können wir stattdessen WebSocket verwenden. Mit WebSocket wird ein vollständiger Duplex-Kommunikationskanal zwischen Browser und Server etabliert, was bidirektionale Echtzeit-Nachrichtenaustausch ermöglicht, ähnlich wie die Verwendung von TCP-Sockets.
Einfache Vergleich zwischen SSE und WebSocket
WebSocket ist ein vollständiger Duplex-Kommunikationskanal, der bidirektionale Kommunikation unterstützt und erweitertere Funktionen hat. SSE ist ein einseitiger Kanal, wobei der Server nur Daten an den Browser senden kann.
WebSocket ist ein neues Protokoll und erfordert Serverseitige Unterstützung, während SSE auf dem HTTP-Protokoll aufgesetzt ist und von bestehender Serversoftware unterstützt wird.
SSE ist ein leichtgewichtiges Protokoll und relativ einfacher, während WebSocket ein schwereres Protokoll und relativ komplexer ist.
Mit dem Verständnis dieser Mechanismen zur Implementierung von Echtzeit-Web-Kommunikation werden wir nun SSE verwenden, um einen einfachen Online-Chatraum zu implementieren.
Implementierung eines Online-Chatraums basierend auf SSE
Es gibt verschiedene Möglichkeiten, Nachrichten in einem Online-Chatraum zu pushen, und in diesem Kurs werden wir Server-Sent Events (SSE) verwenden, um dies zu erreichen. Um die Nachrichtenannahme zu erleichtern, werden wir die Publish/Subscribe-Funktionalität von Redis nutzen, um Nachrichten zu empfangen und zu senden. Auf der Webserver-Seite werden wir Flask verwenden, um die Implementierung durchzuführen.
Wie SSE funktioniert
In den vorherigen Lektionen haben wir gelernt, dass SSE auf HTTP basiert. Wie weiß der Browser dann, dass dies ein Server-sent-event-Strömung ist? Es ist eigentlich ganz einfach – man setzt einfach den Content-Type-Header der HTTP-Anfrage auf text/event-stream. SSE besteht im Wesentlichen darin, dass der Browser eine HTTP-Anfrage an den Server sendet und der Server dann kontinuierlich Informationen an den Browser auf einseitige Weise pusht. Das Format dieser Informationen ist ebenfalls sehr einfach, mit dem Präfix "data:" gefolgt von dem Inhalt der Nachricht und endet mit "\n\n".
Redis Publish/Subscribe-Funktionalität
Redis ist eine beliebte in-memory-Datenbank, die für Caching, Warteschlangen und andere Dienste verwendet werden kann. In diesem Kurs werden wir die Publish/Subscribe-Funktionalität von Redis verwenden. Kurz gesagt ermöglicht die Abonnementfunktion es uns, uns für verschiedene Kanäle abzumelden, und wann immer neue Nachrichten an diese Kanäle veröffentlicht werden, empfangen wir sie automatisch. Wenn der Server eine Nachricht erhält, die vom Browser über eine POST-Anfrage gesendet wurde, veröffentlicht er diese Nachrichten an bestimmte Kanäle. Danach werden die Clients, die sich für diese Kanäle abonniert haben, automatisch diese Nachrichten empfangen, die dann über SSE an die Clients gepusht werden.
Funktionsimplementierung
Nach der obigen Analyse ist der gesamte Prozess des Chatraums bereits klar. Lassen Sie uns nun mit der Implementierung der Funktionalität dieses Chatraums beginnen.
Erstellen Sie in das Verzeichnis ~/project eine Datei namens app.py und geben Sie den folgenden Quellcode ein:
import datetime
import flask
import redis
app = flask.Flask("labex-sse-chat")
app.secret_key = "labex"
app.config["DEBUG"] = True
r = redis.StrictRedis()
## Home-Route-Funktion
@app.route("/")
def home():
## Wenn der Benutzer nicht angemeldet ist, leite ihn zur Anmeldeseite um
if "user" not in flask.session:
return flask.redirect("/login")
user = flask.session["user"]
return flask.render_template("index.html", user=user)
## Nachrichten-Generator
def event_stream():
## Erstellen eines Publish-Subscribe-Systems
pubsub = r.pubsub()
## Verwenden der subscribe-Methode des Publish-Subscribe-Systems, um sich für einen Kanal abzumelden
pubsub.subscribe("chat")
for message in pubsub.listen():
data = message["data"]
if type(data) == bytes:
yield "data: {}\n\n".format(data.decode())
## Anmeldefunktion, Anmeldung erforderlich für den ersten Besuch
@app.route("/login", methods=["GET", "POST"])
def login():
if flask.request.method == "POST":
## Speichern des Benutzernamens im Sitzungswörterbuch und anschließendes Umleiten zur Startseite
flask.session["user"] = flask.request.form["user"]
return flask.redirect("/")
return flask.render_template("login.html")
## Empfangen von Daten, die von JavaScript mit der POST-Methode gesendet werden
@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)
## Event-Stream-Schnittstelle
@app.route("/stream")
def stream():
## Das zurückgegebene Objekt dieser Routenfunktion muss vom Typ text/event-stream sein
return flask.Response(event_stream(), mimetype="text/event-stream")
## Ausführen der Flask-Anwendung
app.run()
Im obigen Code verwenden wir die Sitzungsfunktion von Flask, um die Benutzeranmeldungsinformationen zu speichern, die Publish-Subscribe-Funktion von Redis, um Nachrichten zu empfangen und zu senden, und SSE, um die Nachrichten-Pushing-Implementierung durchzuführen.
Hierbei:
Die event_stream-Funktion ist ein Nachrichten-Generator, der kontinuierlich Nachrichten aus Redis abruft und an den Client weitergibt.
Die stream-Funktion ist eine Event-Stream-Schnittstelle, die ein Objekt vom Typ text/event-stream zurückgibt, was der SSE-Event-Stream ist.
Die post-Funktion ist eine Schnittstelle, die Daten empfängt, die von JavaScript mit der POST-Methode gesendet werden. Sie veröffentlicht die empfangenen Daten in den chat-Kanal in Redis.
Die login-Funktion ist eine Anmeldefunktion, die für den ersten Besuch erforderlich ist. Nach einer erfolgreichen Anmeldung wird der Benutzername im Sitzungswörterbuch gespeichert und dann zur Startseite umgeleitet.
Die home-Funktion ist die Startseite-Routenfunktion. Wenn der Benutzer nicht angemeldet ist, wird er zur Anmeldeseite umgeleitet. Wenn der Benutzer angemeldet ist, wird die index.html-Vorlage gerendert.
Erstellen Sie zunächst ein Verzeichnis templates unter ~/project, um die erforderlichen HTML-Dateien zu speichern. Erstellen Sie in diesem Verzeichnis eine Datei namens login.html und geben Sie den folgenden Code ein:
In der Datei login.html nutzen wir die Vorlagenfunktionalität von Flask und verwenden {{ user }}, um den Benutzernamen darzustellen, der aus flask.session abgerufen wird.
Als nächstes erstellen wir eine Datei index.html im Verzeichnis templates, um die Chatroom-Seite zu implementieren. Geben Sie den folgenden Code hinein:
<!doctype html>
<title>Online Chat</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/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() {
// Verbinde mit dem Event Stream des Servers
var source = new EventSource("/stream");
var out = document.getElementById("out");
source.onmessage = function (e) {
out.innerHTML = e.data + "\n" + out.innerHTML;
};
}
// Sende Nachricht an den Server
$("#in").keyup(function (e) {
if (e.keyCode == 13) {
$.post("/post", { message: $(this).val() });
$(this).val("");
}
});
sse();
</script>
In der Datei index.html verwenden wir die $.post-Methode von jQuery, um Nachrichten an den Server zu senden, und EventSource, um Server-Push-Nachrichten zu empfangen.
Da Redis verwendet wird, müssen Sie den Redis-Dienst in Ihrer Umgebung starten und das erforderliche Redis-Modul herunterladen, um Python mit dem Redis-Server zu verbinden:
pip install redis
sudo service redis-server start
Als nächstes können Sie unseren Chatraum ausführen:
cd ~/project
python app.py
Anschließend können Sie in Ihrem Browser auf http://localhost:5000 zugreifen, einen zufälligen Benutzernamen eingeben und den Chatraum betreten.
Sie können auch ein weiteres Browserfenster im Privatmodus öffnen, um den Chatraum zu betreten, und dann können Sie in beiden Fenstern chatten.
Zusammenfassung
Dieses Projekt nutzt Server-Sent Events (SSE), um Echtzeit-Web-Kommunikationsfunktionen zu implementieren und basiert auf Flask und Redis, um einen Online-Chatraum zu erstellen. Ziel ist es, dass jeder durch diesen Abschnitt der Experimente einen Überblick über den Kommunikationsprozess unter dem HTTP-Protokoll bekommt.