Лекция
Привет, Вы узнаете о том , что такое приложения реального времени, Разберем основные их виды и особенности использования. Еще будет много подробных примеров и описаний. Для того чтобы лучше понимать что такое приложения реального времени, polling, long polling, websockets server-sent events, sse, webrtc , настоятельно рекомендую прочитать все из категории Выполнение скриптов на стороне сервера PHP (LAMP) NodeJS (Backend) .
Веб-приложения изначально были разработаны как простая модель клиент-сервер, в которой веб-клиент инициирует HTTP-запрос, запрашивая некоторые данные с сервера. Например, базовое веб-приложение с потоком модели клиент-сервер будет выглядеть следующим образом.
Поскольку разработчики начали изучать способы реализации большего количества приложений « реального времени ». Протокол HTTP сделал такие варианты использования очень сложными, в результате начали появляться творческие способы преобразования модели HTTP-запрос-ответ в модель, работающую в режиме реального времени.
Самый простой, но самый не эффективный, метод: клиент раз в несколько секунд опрашивает сервер на наличие событий. Даже если ничего нет, то клиент всеравно делает запрос — а мало ли что придет.
Плюсы:
— Просто
— Данные могут быть пожаты
Минусы:
— Очень много лишних запросов
— События всегда приходят с опозданием
— Серверу приходится хранить события пока клиент не заберет их или пока они не устареют
Улучшенный вариант предыдущего метода. Клиент отправляет запрос на сервер, сервер держит открытым соединение пока не придут какие-нибудь данные или клиент не отключится самостоятельно. Как только данные пришли — отправляется ответ и соединение закрывается и открывается следующее и так далее.
Плюсы по сравнению с Polling:
— Минимальное количество запросов
— Высокая временная точность событий
— Сервер хранит события только на время реконнекта
Минусы по сравнению с Polling:
— Более сложная схема
Следующий вариант для рассмотрения HTTP streaming, где несколько ответов могут быть отправлены на один запрос, как показано на рисунке ниже. Это эффективный механизм, но, к сожалению, не приемлем повсеместно во всех конфигурациях прокси / брандмауэре, что делает его непригодным для повсеместного развертывания .
Это бинарный дуплексный протокол, позволяющий клиенту и серверу общаться на равных. Этот протокол можно применять для игр, чатов и всех тех приложений где вам нужны предельно точные события близкие к реальному времени.WebSocket - это протокол связи с компьютером который обеспечивает полнодуплексный каналы связи через одиночное TCP-соединение.
Особенности протокола
Плюсы по сравнению с Long Polling:
Основное преимущество серверной части WebSockets заключается в том, что это не HTTP-запрос (после рукопожатия), а правильный протокол связи на основе сообщений. Это позволяет достичь огромных преимуществ в производительности и архитектуре . Например, в node.js вы можете использовать одну и ту же память для разных соединений сокетов, чтобы каждый из них мог получить доступ к общим переменным. Следовательно, вам не нужно использовать базу данных в качестве точки обмена посередине (например, с AJAX или длинным опросом с таким языком, как PHP). Вы можете хранить данные в ОЗУ или даже сразу же повторно публиковать их между сокетами.
Минусы по сравнению с Long Polling:
— HTTP не совместимый протокол, нужен свой сервер, усложняется отладка
Так почему же стоит применять SSE, раз у нас есть такой прекрасный протокол WebSockets?! Во-первых не каждому веб-приложению необходима двусторонняя связь — подойдет и SSE. Во-вторых SSE — HTTP совместимый протокол и вы можете реализовать рассылку событий на любом веб-сервере.
Люди часто обеспокоены безопасностью WebSockets. Реальность такова, что это не имеет большого значения или даже делает WebSockets лучшим вариантом. Во-первых, с AJAX вероятность MITM выше , поскольку каждый запрос представляет собой новое TCP-соединение, проходящее через интернет-инфраструктуру. С WebSockets, когда он подключен, гораздо сложнее перехватить между ними, с дополнительной принудительной маскировкой кадров при потоковой передаче данных от клиента к серверу, а также дополнительным сжатием, которое требует больше усилий для проверки данных. Все современные протоколы поддерживают как HTTP, так и HTTPS (с шифрованием). Помните, что WebSockets обычно имеют совсем другой подход к логике работы в сети , больше похожий на игры в реальном времени, чем у http.
SSE предложил Ian Hickson более 7 лет назад, но только год назад она стала появляться в браузерах. У нас же есть WebSockets зачем нам еще один какой-то протокол?! Но во всем есть свои плюсы и минусы, давайте посмотрим чем же SSE может быть полезен.
Идея SSE проста — клиент подписывается на события сервера и как только происходит событие — клиент сразу же получает уведомление и некоторые данные, связанные с этим событием. Чтобы понять полезность протокола SSE необходимо сравнить его с привычными методами получения событий, вкратце объясню их суть:
Клиент отправляет запрос на сервер, сервер в ответ отправляет следующий заголовок:
Content-Type: text/event-stream
И не закрывает соединение (на php можно создать бесконечный цикл, как сделать на node.js будет объеснено в примере статьи). Вот и все — SSE работает! Чтобы отправить клиенту какие-то данные сервер просто пишет в сокет строку следующего формата:
data: My message\n\n
Если необходимо отправить несколько строк данных, то формат будет следующим:
data: {\n data: "msg": "hello world",\n data: "id": 12345\n data: }\n\n
Вот, впринципе, и вся база протокола. Кроме этого сервер может отправлять id сообщения это нужно на случай если соединение было разорвано. Если соединение было сброшено, то клиент при попытке подключения отправит специальный заголовок (Last-Event-ID), чтобы восстановить утраченные события:
id: 12345\n data: GOOG\n data: 556\n\n
Время переподключения (retry) в случае ошибок:
retry: 10000\n data: hello world\n\n
Поле id и retry не обязательны.
На клиенте все будет выглядеть следующим образом:
var source = new EventSource('http://localhost/stream.php'); source.addEventListener('message', function(e) { // Пришли какие-то данные console.log(e.data); }, false); source.addEventListener('open', function(e) { // Соединение было открыто }, false); source.addEventListener('error', function(e) { if (e.eventPhase == EventSource.CLOSED) { // Соединение закрыто } }, false);
Все предельно просто. Давайте построим приложение на основе протокола SSE. Как водится, это будет чат.
Multipart XMLHTTPRequest
Еще называют multipart streaming (Поддерживает только Firefox). Очень похожий на SSE протокол.
Его заголовок имеет фомат:
Content-type: multipart/x-mixed-replace;boundary=smthing
А части отсылаются в таком формате:
Content-type: text/html\r\n\r\n --smthing\n Message\n --smthing\n
В клиенте создается обычный XHR, но перед отправкой запроса необходимо поставить флаг req.multipart = true;
Правда похоже на SSE?
Есть еще один протокол, который можно привести к SSE:
XMLHTTPRequest: Interactive
Для использования его необходима поддержка браузером специального readyState с кодом 3 (interactive) — этот статус сообщает о том, что часть данных пришла, но соединение еще не закрыто. Для jQuery есть одноименный плагин, использующий readyState с кодом 3. И как всегда не все браузеры поддерживают readyState с кодом 3.
WebSocket и WebRTC - это оба проекта, которые обеспечивают коммуникационные возможности. Это два разных способа общения с сервером. WebSocket - это двусторонняя связь между сервером и клиентом, которая подразумевает, что обе стороны могут обмениваться данными и обмениваться данными одновременно. С помощью Web Real-Time Communication или WebRTC современные веб-приложения могут легко передавать аудио и видео контент миллионам зрителей.
Оба WebSockets против WebRTC являются популярным выбором на рынке; давайте обсудим некоторые основные различия между WebSockets и WebRTC
Ниже приведено 7 лучших сравнений между WebSockets и WebRTC
Основа сравнения между WebSockets и WebRTC | WebSockets | WebRTC |
Определение | WebSocket - это компьютерный коммуникационный протокол, который обеспечивает каналы связи по одному TCP-соединению. | WebRTC - это бесплатный открытый проект, который предоставляет браузерам и мобильным приложениям возможности связи в реальном времени с помощью простых API. |
обзор |
|
|
Пользы |
|
|
Архитектура | Архитектура для WebRTC состоит из слоев:
|
WebRTC имеет сложную архитектуру. Архитектура WebRTC имеет три уровня.
|
Окружающая обстановка | Java, JMS, C ++ | Перед созданием приложений WebRTC важно установить среду кодирования. Во-первых, у нас должен быть текстовый редактор, в котором мы можем редактировать HTML и JavaScript. Еще одно требование - сервер для размещения файлов HTML и JavaScript. |
Характеристики |
|
|
Безопасность | Проблемы безопасности для WebSockets включают в себя:
|
Чтобы связать одного пользователя с другим, мы должны найти четкий путь вокруг вашей сети и сети другого пользователя. В этом случае может быть несколько уровней безопасности. Чтобы повысить безопасность и разрешить нескольким пользователям использовать один и тот же IP-адрес, маршрутизатор скрывает ваш собственный сетевой адрес и заменяет его другим. |
Изучив сравнение и различия между WebSockets и WebRTC, мы узнали, что оба WebSockets против WebRTC являются протоколами, обеспечивающими связь. WebSockets обеспечивают двустороннюю связь через единую связь TCP, тогда как WebRTC обеспечивает одноранговую связь в режиме реального времени в браузере и мобильных приложениях. Оба WebSockets против WebRTC имеют Java в качестве основной среды. Хотя API-интерфейсы WebSockets включены с возможностью передачи видеосвязи, они не так надежны, как WebRTC
В простейшем виде это одноранговые узлы (клиенты WebRTC) и сигнальный сервер. Вначале одноранговые узлы не знают друг друга и не знают необходимой сетевой информации, чтобы сделать возможным прямое соединение. Перед установкой прямого соединения одноранговые узлы должны обмениваться всеми необходимыми данными с помощью сервера сигнализации. Это средняя точка, известная каждому партнеру. Таким образом, каждый одноранговый узел может подключиться к серверу сигнализации, а затем один одноранговый узел может позвонить другому, попросив сервер сигнализации обмениваться конкретными данными с другим одноранговым узлом и знакомить одноранговые узлы друг с другом. Оба клиента обмениваются необходимыми данными (включая сведения о сети), а затем устанавливают прямое одноранговое соединение. После установления соединения одноранговые узлы больше не используют сервер.
Служба сообщений для сигнализации должна быть двунаправленной: клиент-сервер и сервер-клиент. В этом случае мы можем использовать WebSocket, который предназначен для полнодуплексного взаимодействия клиент-сервер. Для поддержки пользователей с высокой степенью одновременного использования нашему приложению WebRTC требуются сигнальные серверы, способные выдерживать значительную нагрузку.
В этом случае мы можем выполнить вертикальное масштабирование, добавив несколько серверов сигнализации за балансировщиком нагрузки. Проблема возникает, когда два одноранговых узла собираются позвонить, но подключаются к разным серверам. Итак, как эти два сервера общаются друг с другом? Решение - использовать очередь сообщений, такую как ZeroMQ, Redis и т. Д.
Каждому серверу сигнализации необходимо создать двух клиентов, которые подключены к очереди сообщений: один для публикации сообщения в очереди, а другой для подписки на очередь. Когда сервер получает запрос на маршрутизацию сообщения клиенту, которого нет в словаре локальных подключений, он опубликует сообщение на сервер очереди, который сообщит всем серверам-подписчикам, что нужно искать этого клиента и выдавать сообщение, если оно подключен к этому серверу.
В некоторых случаях, когда клиенты используют NAT и брандмауэры, невозможно установить одноранговое соединение с помощью STUN. Такая ситуация часто возникает, когда клиент работает в корпоративной сети со строгой политикой подключения. В таком случае единственный способ установить соединение - использовать сервер TURN. Сервер TURN работает как прокси - все данные между одноранговыми узлами (включая аудио, видео и служебные данные) проходят через сервер TURN.
Сервер TURN должен поддерживать аутентификацию и запрещать анонимный доступ. При использовании службы TURN весь трафик от одного узла к другому проходит через сервер TURN.
Нам также может потребоваться вертикальное масштабирование для серверов TURN, добавив балансировщик нагрузки.
Мы можем настроить сервер TURN как резервный вариант, когда одноранговое соединение не может быть установлено с помощью STUN. Таким образом мы сможем объединить лучшее из обоих миров.
AJAX - request
→ response
. Создает соединение с сервером, отправляет заголовки запроса с дополнительными данными, получает ответ от сервера и закрывает соединение. Поддерживается во всех основных браузерах.
Long poll - request
→ wait
→ response
. Создает соединение с сервером, как это делает AJAX, но поддерживает соединение keep-alive в течение некоторого времени (хотя и ненадолго). Во время подключения открытый клиент может получать данные с сервера. Клиент должен периодически переподключаться после закрытия соединения из-за тайм-аутов или eof данных. На стороне сервера он по-прежнему обрабатывается как HTTP-запрос, такой же, как AJAX, за исключением того, что ответ на запрос произойдет сейчас или когда-нибудь в будущем, что определяется логикой приложения. Long poll - это грязный обходной путь для предотвращения создания соединений для каждого запроса, как это делает AJAX, но длинный опрос был создан, когда WebSockets не существовало. Теперь из-за WebSockets длинные опросы ушли.
WebSockets - client
↔ server
. Создайте TCP-соединение с сервером и держите его открытым столько, сколько необходимо. Сервер или клиент могут легко закрыть соединение. Клиент проходит через процесс установления связи, совместимый с HTTP. В случае успеха сервер и клиент могут обмениваться данными в обоих направлениях в любое время. Это эффективно, если приложение требует частого обмена данными обоими способами. В WebSockets есть кадрирование данных, которое включает маскировку для каждого сообщения, отправляемого от клиента к серверу, поэтому данные просто зашифровываются. график поддержки (очень хорошо) | википедия
WebRTC - peer
↔ peer
. Транспорт для установления связи между клиентами и не зависит от транспорта, поэтому он может использовать UDP, TCP или даже более абстрактные уровни. Обычно это используется для передачи больших объемов данных, таких как потоковое видео / аудио, где надежность является второстепенной, и можно пожертвовать несколькими кадрами или снижением качества в пользу времени отклика и, по крайней мере, некоторой передачи данных. Обе стороны (одноранговые узлы) могут передавать данные друг другу независимо. Хотя его можно использовать полностью независимо от каких-либо централизованных серверов, он по-прежнему требует определенного способа обмена данными о конечных точках, когда в большинстве случаев разработчики по-прежнему используют централизованные серверы для «связывания» одноранговых узлов. Это требуется только для обмена важными данными для установления соединения, после чего централизованный сервер не требуется. WebRTC позволяет осуществлять одноранговую связь.
События, отправленные сервером - client
← server
. Клиент устанавливает постоянное и долгосрочное соединение с сервером. Только сервер может отправлять данные клиенту. Если клиент хочет отправить данные на сервер, для этого потребуется использование другой технологии / протокола. Этот протокол совместим с HTTP и прост в реализации на большинстве серверных платформ. Это предпочтительный протокол для использования вместо длительного опроса. график поддержки (хорошо, кроме IE) | википедия
Рис Сравнение различных видов соединенией реального времени и процессорного времени
Мы будем принимать поток событий по SSE: уход в оффлайн, приход в онлайн, сообщение. Т.к. по SSE нельзя отправлять сообщение, то мы будем отправлять их по HTTP.
Схема работы такая:
— При входе в чат запрашивается имя
— Клиент подключается к серверу чата. Создается поток событий.
— При подключении клиента чат рассылает всем событие: %username% online
— При отключении клиента чат рассылает всем событие: %username% offline
— Клиент может отправлять сообщение по HTTP «POST /message» Cервер принимает это сообщение и рассылает всем клиентам принятое сообщение по SSE
Разберем код клиента. Для того, чтобы у некоторых браузеров не было бесконечной загрузки мы вместо $.ready выполняем setTimeout:
setTimeout(function () { // Ставлю именно таймаут, а не $.ready иначе у вебкитов будет бесконечная загрузка }, 50);
Запрашиваем имя пользователя:
// Получаем имя из localStorage и запрашиваем новое var name = (prompt('Name:', window.localStorage ? window.localStorage['name'] || '' : '') || 'anonymous').substr(0, 20); // Пытаемся сохранить имя if (window.localStorage) { window.localStorage['name'] = name; }
Создаем EventSource и передаем ему имя пользователя (теперь пользователь онлайн) и слушаем необходимые события:
var eventSrc = new EventSource("/event?name=" + name); // Слушаем событие EventSource - "message" eventSrc.addEventListener("message", function(event) { var data = JSON.parse(event.data); // Отрисовываем пришедшее с сервера сообщение renderMessage(data); }, false); // Слушаем событие EventSource - "error" eventSrc.addEventListener("error", function(event) { // Сообщаем о проблеме с подключением renderMessage({ isbot: true, message: 'connection error', name: '@Chat' }); }, false);
Не буду рассматривать метод renderMessage и разметку страницы. Весь код клиента можно посмотреть тут: index.html
На стороне сервера у нас будет Node.js. Тут все сложнее, но основная сложность в мультикасте сообщений от одного пользователя ко всем, а не в построении коммуникации по SSE.
Подключаем необходимые модули
var http = require('http'), fs = require('fs'), qs = require('querystring'), parse = require('url').parse; // Кэшируем статику (index.html мы будет отдавать с помошью Node.js) var indexFile = fs.readFileSync('index.html'); // Buffer
Роуты
Создаем список роутов Routes, включающий в себя следующие объекты:
1. Статика. Индексная страница, мы просто шлем статику:
'GET /': function (request, response) { // Шлем правильные заголовки response.writeHead(200, {'Content-Type': 'text/html; charset=UTF-8'}); response.write(indexFile); response.end(); }
2. Поднятие SSE соедиения:
'GET /event': function (request, response) { var url = parse(request.url, true); var name = (url.query.name || 'anonymous').substr(0, 20); var clientId = Clients.generateClientId(); // Шлем спец заголовок для EventSource response.writeHead(200, {'Content-Type': 'text/event-stream'}); // Выставляем больший таймаут на сокет, иначе сокет запроется через 2 минуты request.socket.setTimeout(1000 * 60 * 60); // 1 Час // Если соединение упало - удаляем этого клиента request.on('close', function () { Clients.remove(clientId); }); // Добавляем клиента в список Clients.add(clientId, response, name); }
3. Сообщение от клиента:
'POST /message': function (request, response) { var data = ''; // Пришел кусочек тела POST request.on('data', function (chunk) { data += chunk; }); // Все кусочки POST тела собраны request.on('end', function () { // Парсим тело data = qs.parse(data); // Рассылаем всем сообщение Clients.broadcast(data.message, data.name, false); response.writeHead(200); response.end(); }); }
4. Роут по умолчанию — Страница 404:
$: function (request, response) { response.writeHead(404); response.end(); }
Менеджер клиентов — Clients
При добавлении нового клиента (add) менеджер рассылает все сообщение о том, что клиент пришел:
add: function (clientId, response, name) { this._clients[clientId] = {response: response, name: name || 'anonymous'}; this.count++; // Рассылаем сообщения от имени бота this.unicast(clientId, 'Hello, ' + name + '! Online ' + this.count, '@ChatBot', true); this.broadcast(name + ' online', '@ChatBot', true); }
При удалении закрывает соединение и рассылает всем, что клиент оффлайн:
remove: function (clientId) { // Если клиента нет, то ничего не делаем var client = this._clients[clientId]; if (!client) { return; } // Закрываем соединение client.response.end(); // Удаляем клиента delete this._clients[clientId]; this.count--; // Сообщаем всем оставшимся, что он вышел // Рассылаем сообщения от имени бота this.broadcast(client.name + ' offline', '@ChatBot', true); }
Для отправки сообщений клиентам используется private метод _send:
_send: function (clients, message, name, isbot) { if (!message || !name) { return; } // Подготавливаем сообщение var data = JSON.stringify({ message: message.substr(0, 1000), name: (name || 'anonymous').substr(0, 20), isbot: isbot || false }); // Создаем новый буфер, чтобы при большом количестве клиентов // Отдача была более быстрой из-за особенностей архитектуры Node.js data = new Buffer("data: " + data + "\n\n", 'utf8'); // Формат сообщение SSE // Рассылаем всем clients.forEach(function (client) { client.response.write(data); // Отсылаем буфер }); }
Метод _send используют public методы broadcast и unicast для рассылки сообщения всем и одному клиенту соответственно.
Создаем и включаем сервер
// Создаем сервер var httpServer = http.createServer(function (request, response) { var key = request.method + ' ' + parse(request.url).pathname; // Если роута нет, то отдаем по умолчанию Routes.$ - 404 (Routes[key] || Routes.$)(request, response); }); // Включаем сервер httpServer.listen(80); console.log('Online');
Исходный код server.js https://github.com/azproduction/event-source-chat/blob/master/server.js
Наш чат на SSE готов. Запускаем сервер:
$ node server.js
Открываем один из браузеров: Firefox 6, Opera 10.6+, Chrome, WebKit 5+, iOS Safari 4+, Opera Mobile 10+. Переходим на http://localhost/ и чатим!
Заключение
SSE — хорошая технология, которая должна вытеснить Long Poling она проста и не менее эффективна, чем WebSockets. Сейчас SSE поддерживают Opera 10.6+ (Opera 9 поддерживает старый стандарт SSE), Chrome, Safari 5+. Firefox поддерживает Multipart XMLHTTPRequest, для которого можно написать обертку и использовать как интерфейс SSE.
Сравнение методов передачи данных
Ссылки
1. Онлайн пример SSE чата можно посмотреть вот тут: sse-chat.nodester.com
Это несколько урезанная версия чата из-за особенностей проксирования Nodester (нет сообщения о количестве пользователей онлайн и нет сообщений о выходе из чата, может быть частый реконнект)
2. Исходник примера: github.com/azproduction/event-source-chat
Представленные результаты и исследования подтверждают, что применение искусственного интеллекта в области приложения реального времени имеет потенциал для революции в различных связанных с данной темой сферах. Надеюсь, что теперь ты понял что такое приложения реального времени, polling, long polling, websockets server-sent events, sse, webrtc и для чего все это нужно, а если не понял, или есть замечания, то не стесняйся, пиши или спрашивай в комментариях, с удовольствием отвечу. Для того чтобы глубже понять настоятельно рекомендую изучить всю информацию из категории Выполнение скриптов на стороне сервера PHP (LAMP) NodeJS (Backend)
Комментарии
Оставить комментарий
Выполнение скриптов на стороне сервера PHP (LAMP) NodeJS (Backend)
Термины: Выполнение скриптов на стороне сервера PHP (LAMP) NodeJS (Backend)