Этот проект использует SSE (Server-Sent Events) и Redis для реализации онлайн-чат-комнаты. Требуются предварительные знания синтаксиса Python и JavaScript, а также базовое понимание использования Flask и Redis.
В этом разделе эксперимента мы научимся и потренируемся в следующих концепциях:
Веб-реального времени коммуникации
Работы SSE
Использовании Redis
👀 Предварительный просмотр
🎯 Задачи
В этом проекте вы научитесь:
Как создавать простую онлайн-чат-комнату с использованием Flask и SSE
Как реализовать функциональность входа пользователя
Как использовать Redis для хранения и извлечения сообщений
🏆 Достижения
После завершения этого проекта вы сможете:
Настроить SSE для реального времени коммуникации в веб-приложении
Использовать Redis для хранения и извлечения сообщений в приложении-чат-комнате
Реализовать функциональность входа пользователя в Flask
Веб-реальное время коммуникация
Веб-реальное время коммуникация (WebRTC) относится к механизму, который позволяет мгновенно информировать пользователей о событии на веб-странице без необходимости их обновления страницы. WebRTC имеет множество применений, таких как реальное время чат и мгновенные сообщения.
Существует несколько способов коммуникации между веб-клиентами и серверами.
Обычный HTTP поток
Клиент запрашивает веб-страницу у сервера.
Сервер отвечает соответственно.
Сервер отправляет ответ обратно клиенту.
Поскольку HTTP-запросы являются безсостоятельными, то есть HTTP-соединение завершается после каждого запроса, сервер и браузер совершенно не знают друг о друге, пока не будет сделан следующий запрос для обновления соответствующей информации. В этом случае не сложно придумать простое решение, где браузер может делать периодические запросы для имитации реального времени эффектов. Это называется опросом (polling).
Поток опроса
Клиент отправляет запрос на соединение с сервером с использованием обычного HTTP.
Клиент выполняет встроенный в веб-страницу JavaScript-скрипт опроса, чтобы периодически отправлять запросы на сервер (например, каждые 5 секунд) для получения информации.
Сервер отвечает на каждый запрос и отправляет обратно соответствующую информацию, точно так же, как и при обычном HTTP-запросе.
Опрос позволяет получать информацию в почти реальном времени. Однако частые запросы от браузера к серверу в результате опроса могут привести к неэффективности в производительности. Чтобы минимизировать эти проблемы, была предложена альтернативная методика. Вместо немедленного ответа на запросы клиента, сервера ждут, пока не произойдет изменение данных (или истечение времени ожидания), прежде чем вернуть ответ. Этот подход максимизирует валидность соединения, чтобы уменьшить количество запросов при опросе. Этот метод называется длинным опросом (long polling).
Поток длинного опроса
Клиент запрашивает веб-страницу у сервера с использованием обычного HTTP.
Клиент выполняет встроенный в веб-страницу JavaScript-скрипт для отправки данных и запроса информации на сервер.
Вместо немедленного ответа на запрос клиента, сервер ждет валидного обновления.
Когда информация обновляется и становится валидной, сервер отправляет данные клиенту.
При получении уведомления от сервера клиент немедленно отправляет новый запрос, чтобы начать следующий круг опроса.
Перечисленные выше методы обычно используются для реализации реального времени веб-коммуникации. Однако после появления HTML5 у нас появились более подходящие варианты. В HTML5 мы можем использовать Server-Sent Events (SSE) или WebSocket. SSE специально предназначен для отдачи данных от сервера к клиенту, что часто достаточно для таких сценариев, как трансляция информации о матче или изменения цен на акции.
Поток Server-Sent Events
Клиент запрашивает веб-страницу у сервера с использованием обычного HTTP.
Клиент устанавливает соединение с сервером с использованием встроенного в веб-страницу JavaScript.
Когда сервер имеет обновления, он отправляет событие клиенту.
Если SSE не отвечает на наши потребности, мы можем использовать WebSocket вместо этого. С использованием WebSocket между браузером и сервером устанавливается полно-дуплексный канал связи, что позволяет осуществлять двухсторонний обмен сообщениями в реальном времени, аналогично использованию TCP-сокетов.
Простой сравнительный анализ между SSE и WebSocket
WebSocket - это полно-дуплексный канал связи, поддерживающий двухстороннюю коммуникацию и обладающий более продвинутыми функциями. SSE - это односторонний канал, где сервер может только отправлять данные в браузер.
WebSocket - это новый протокол и требует поддержки сервера, в то время как SSE развертывается поверх протокола HTTP и поддерживается существующим серверным программным обеспечением.
SSE - это легковесный протокол и относительно простой, в то время как WebSocket - более тяжелый протокол и относительно сложнее.
После ознакомления с этими механизмами реализации реального времени веб-коммуникации мы теперь будем использовать SSE для реализации простой онлайн-чат-комнаты.
Реализация онлайн-чат-комнаты на основе SSE
В онлайн-чат-комнате существуют различные способы отправки сообщений, и в этом курсе мы будем использовать Server-Sent Events (SSE) для этого. Чтобы облегчить получение сообщений, мы воспользуемся функцией публикации/подписки (pub/sub) Redis для приема и отправки сообщений. С веб-сторона сервера мы будем использовать Flask для реализации.
Как работает SSE
В предыдущих уроках мы узнали, что SSE основан на HTTP. Так как же браузер знает, что это поток событий, отправляемых сервером? Действительно, это довольно просто - достаточно установить заголовок Content-Type HTTP-запроса в text/event-stream. SSE по существу заключается в том, что браузер отправляет HTTP-запрос на сервер, а затем сервер непрерывно отправляет информацию в браузер в单向ном режиме. Формат этой информации также очень простой, с префиксом "data:", за которым следует содержание сообщения, и заканчивающийся "\n\n".
Функциональность публикации/подписки Redis
Redis - это популярная в-memory база данных, которая может использоваться для кэширования, очередей и других служб. В этом курсе мы будем использовать функцию публикации/подписки Redis. Простыми словами, функция подписки позволяет нам подписываться на различные каналы, и всякий раз, когда на эти каналы публикуются новые сообщения, мы автоматически получаем их. Когда сервер получает сообщение, отправленное браузером с помощью POST-запроса, он публикует эти сообщения в конкретные каналы. Затем клиенты, подписавшиеся на эти каналы, автоматически получат эти сообщения, которые затем будут отправлены клиентам с помощью SSE.
Реализация функциональности
После анализа выше вся процедура работы чат-комнаты уже ясна. Теперь приступим к реализации функциональности этой чат-комнаты.
Создайте файл с именем app.py в директории ~/project и вставьте в него следующий исходный код:
import datetime
import flask
import redis
app = flask.Flask("labex-sse-chat")
app.secret_key = "labex"
app.config["DEBUG"] = True
r = redis.StrictRedis()
## Функция маршрута домашней страницы
@app.route("/")
def home():
## Если пользователь не авторизован, перенаправляем его на страницу входа
if "user" not in flask.session:
return flask.redirect("/login")
user = flask.session["user"]
return flask.render_template("index.html", user=user)
## Генератор сообщений
def event_stream():
## Создаем систему публикации/подписки
pubsub = r.pubsub()
## Используем метод подписки системы публикации/подписки для подписки на канал
pubsub.subscribe("chat")
for message in pubsub.listen():
data = message["data"]
if type(data) == bytes:
yield "data: {}\n\n".format(data.decode())
## Функция входа, вход требуется при первом посещении
@app.route("/login", methods=["GET", "POST"])
def login():
if flask.request.method == "POST":
## Сохраняем имя пользователя в словаре сессии и затем перенаправляем на домашнюю страницу
flask.session["user"] = flask.request.form["user"]
return flask.redirect("/")
return flask.render_template("login.html")
## Получаем данные, отправленные JavaScript с использованием метода 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)
## Интерфейс потока событий
@app.route("/stream")
def stream():
## Объект, возвращаемый функцией этого маршрута, должен быть типа text/event-stream
return flask.Response(event_stream(), mimetype="text/event-stream")
## Запускаем приложение Flask
app.run()
В вышеприведенном коде мы используем функцию сессии Flask для хранения информации о входе пользователя, функцию публикации/подписки Redis для приема и отправки сообщений и SSE для реализации отправки сообщений.
Здесь:
Функция event_stream - это генератор сообщений, который непрерывно получает сообщения из Redis и отправляет их клиенту.
Функция stream - это интерфейс потока событий, которая возвращает объект типа text/event-stream, который представляет собой поток событий SSE.
Функция post - это интерфейс, который получает данные, отправленные JavaScript с использованием метода POST. Она публикует полученные данные в канал chat в Redis.
Функция login - это функция входа, которая требуется при первом посещении. После успешного входа имя пользователя сохраняется в словаре сессии, а затем происходит перенаправление на домашнюю страницу.
Функция home - это функция маршрута домашней страницы. Если пользователь не авторизован, она перенаправляет его на страницу входа. Если пользователь авторизован, она отображает шаблон index.html.
Сначала создайте директорию templates в ~/project, чтобы хранить необходимые HTML-файлы. В директории templates создайте файл login.html и напишите следующий код:
В файле login.html мы используем функциональность шаблонов Flask и используем {{ user }} для представления имени пользователя, которое извлекается из flask.session.
Далее, создадим файл index.html в директории templates, чтобы реализовать страницу чат-комнаты. Вставьте в него следующий код:
<!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() {
// 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>
В файле index.html мы используем метод $.post библиотеки jQuery для отправки сообщений на сервер и EventSource для приема сообщений, отправляемых сервером.
Поскольку используется Redis, вам необходимо запустить службу Redis в своей среде и скачать модуль redis, необходимый для подключения Python к серверу Redis:
pip install redis
sudo service redis-server start
Далее, вы можете запустить нашу чат-комнату:
cd ~/project
python app.py
Затем вы можете открыть http://localhost:5000 в своем браузере, ввести произвольное имя пользователя и войти в чат-комнату.
Вы также можете открыть другую вкладку браузера в приватном режиме, чтобы войти в чат-комнату, и затем можно будет общаться в обоих окнах.
Обзор
В этом проекте используется SSE для реализации функций реального времени веб-коммуникации и для создания онлайн-чат-комнаты используются Flask и Redis. Цель состоит в том, чтобы каждый, на основе этого раздела эксперимента, понял процесс коммуникации в рамках протокола HTTP.
We use cookies for a number of reasons, such as keeping the website reliable and secure, to improve your experience on our website and to see how you interact with it. By accepting, you agree to our use of such cookies. Privacy Policy