Подписочная модель обмена сообщениями

Содержание

Слайд 2

Вспомним схему взаимодействия HTTP

Вспомним схему взаимодействия HTTP

Слайд 3

Постановка задачи Представим что нам необходимо написать {чат/игру/систему оповещений} для нескольких

Постановка задачи

Представим что нам необходимо написать {чат/игру/систему оповещений} для нескольких пользователей.

Каким образом мы будет оповещать подключившихся пользователей об изменениях в системе?
Учитываем что сервер ничего не знает о подключениях, т.к. инициирующей стороной является клиент.
Открытый вопрос… что же мы можем сделать?
Слайд 4

Periodic polling (Частые опросы) Самый простой способ получать новую информацию от

Periodic polling (Частые опросы)

Самый простой способ получать новую информацию от сервера

– периодический опрос. То есть, регулярные запросы на сервер вида: «Привет, я здесь, у вас есть какая-нибудь информация для меня?». Например, раз в 10 секунд.
В ответ сервер, во-первых, помечает у себя, что клиент онлайн, а во-вторых посылает весь пакет сообщений, накопившихся к данному моменту.
Слайд 5

Periodic polling (Частые опросы) Это работает, но есть и недостатки: Сообщения

Periodic polling (Частые опросы)

Это работает, но есть и недостатки:
Сообщения передаются

с задержкой до 10 секунд (между запросами).
Даже если сообщений нет, сервер «атакуется» запросами каждые 10 секунд, даже если пользователь переключился куда-нибудь или спит. С точки зрения производительности, это довольно большая нагрузка.
Так что, если речь идёт об очень маленьком сервисе, подход может оказаться жизнеспособным, но в целом он нуждается в улучшении.
Слайд 6

Long polling (Длинные запросы) Длинные опросы – это самый простой способ

Long polling (Длинные запросы)

Длинные опросы – это самый простой способ поддерживать

постоянное соединение с сервером, не используя при этом никаких специфических протоколов.
Его очень легко реализовать, и он хорошо подходит для многих задач
Слайд 7

Long polling (Длинные запросы) «Длинные опросы» – гораздо лучший способ взаимодействия

Long polling (Длинные запросы)

«Длинные опросы» – гораздо лучший способ взаимодействия с

сервером.
Они также очень просты в реализации, и сообщения доставляются без задержек.
Как это происходит:
Запрос отправляется на сервер.
Сервер не закрывает соединение, пока у него не возникнет сообщение для отсылки.
Когда появляется сообщение – сервер отвечает на запрос, посылая его.
Браузер немедленно делает новый запрос.
Слайд 8

Long polling (Длинные запросы)

Long polling (Длинные запросы)

Слайд 9

Long polling (Длинные запросы) Для данного метода ситуация, когда браузер отправил

Long polling (Длинные запросы)

Для данного метода ситуация, когда браузер отправил запрос

и удерживает соединение с сервером в ожидании ответа, является стандартной. Соединение прерывается только доставкой сообщений.
Если соединение будет потеряно, скажем, из-за сетевой ошибки, браузер всё ровно будет продолжать посылать новые запросы.
Рассмотрим код клиентской функции subscribe, которая реализует длинные опросы.
Слайд 10

Слайд 11

Слайд 12

Почему Node.JS это OK для Long Pooling’a?

Почему Node.JS это OK для Long Pooling’a?

Слайд 13

Почему Node.JS это OK для Long Pooling’a?

Почему Node.JS это OK для Long Pooling’a?

Слайд 14

Long polling (Длинные запросы) Длинные опросы прекрасно работают, когда сообщения приходят

Long polling (Длинные запросы)

Длинные опросы прекрасно работают, когда сообщения приходят редко.
Если

сообщения приходят очень часто, то схема приёма-отправки сообщений, приведённая выше, становится похожей на «пилу».
Каждое сообщение – это отдельный запрос, с заголовками, авторизацией и так далее.
Поэтому в этом случае предпочтительнее использовать другой метод, такой как, например, WebSocket.
Слайд 15

WebSocket Протокол WebSocket, обеспечивает возможность обмена данными между браузером и сервером

WebSocket

Протокол WebSocket, обеспечивает возможность обмена данными между браузером и сервером через

постоянное соединение. Данные передаются по нему в обоих направлениях в виде «пакетов», без разрыва соединения и дополнительных HTTP-запросов.
WebSocket особенно хорош для сервисов, которые нуждаются в постоянном обмене данными, например онлайн игры, торговые площадки, работающие в реальном времени, и т.д.
Слайд 16

WebSocket Чтобы открыть веб-сокет-соединение, нам нужно создать объект new WebSocket, указав

WebSocket

Чтобы открыть веб-сокет-соединение, нам нужно создать объект new WebSocket, указав в

url-адресе специальный протокол ws:
let socket = new WebSocket("wss://app.herokuapp.com");
Протокол wss://, использующий шифрование. Это как HTTPS, но только для веб-сокетов.
WebSocket’ы без TLS/SSL = ws://
Слайд 17

WebSocket Как только объект WebSocket создан, мы должны слушать его события.

WebSocket

Как только объект WebSocket создан, мы должны слушать его события. Их

всего 4:
open – соединение установлено,
message – получены данные,
error – ошибка,
close – соединение закрыто.
А если мы хотим отправить что-нибудь, то вызов метода .send(…) сделает это.
Слайд 18

Слайд 19

Установка WebSocket соединения Когда new WebSocket(url) создан, он тут же сам

Установка WebSocket соединения

Когда new WebSocket(url) создан, он тут же сам начинает

устанавливать соединение.
Браузер, при помощи специальных заголовков, спрашивает сервер: «Ты поддерживаешь Websocket?» и если сервер отвечает «да», они начинают работать по протоколу WebSocket, который уже не является HTTP.
Слайд 20

Установка WebSocket соединения Пример заголовков запроса, который происходит при установке соединения

Установка WebSocket соединения

Пример заголовков запроса, который происходит при установке соединения

Слайд 21

Установка WebSocket соединения Origin – источник текущей страницы (например https://javascript.info). Объект

Установка WebSocket соединения

Origin – источник текущей страницы (например https://javascript.info). Объект WebSocket

по своей природе не завязан на текущий источник. Нет никаких специальных заголовков или других ограничений. Старые сервера все равно не могут работать с WebSocket, поэтому проблем с совместимостью нет. Но заголовок Origin важен, так как он позволяет серверу решать, использовать ли WebSocket с этим сайтом.
(Про особенности CORS позже)
Слайд 22

Установка WebSocket соединения Connection: Upgrade сигнализирует, что клиент хотел бы изменить

Установка WebSocket соединения

Connection: Upgrade сигнализирует, что клиент хотел бы изменить протокол.
Upgrade:

websocket запрошен протокол «websocket».
Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q== случайный ключ, созданный браузером для обеспечения безопасности.
Sec-WebSocket-Version: 13 версия протокола WebSocket, текущая версия 13.
Слайд 23

Установка WebSocket соединения Если сервер согласен переключиться на WebSocket, то он

Установка WebSocket соединения

Если сервер согласен переключиться на WebSocket, то он должен

отправить в ответ HTTPшный ответ с кодом 101:
Слайд 24

Установка WebSocket соединения Здесь Sec-WebSocket-Accept – это Sec-WebSocket-Key, перекодированный с помощью

Установка WebSocket соединения

Здесь Sec-WebSocket-Accept – это Sec-WebSocket-Key, перекодированный с помощью специального

алгоритма. Браузер использует его, чтобы убедиться, что ответ соответствует запросу (т.е. ответил тот же сервер, к которому мы обращались).
После этого данные передаются по протоколу WebSocket, и вскоре мы увидим его структуру («фреймы»). И это вовсе не HTTP.
Слайд 25

Слайд 26

Передача данных в WebSocket Поток данных в WebSocket состоит из «фреймов»,

Передача данных в WebSocket

Поток данных в WebSocket состоит из «фреймов», фрагментов

данных, которые могут быть отправлены любой стороной, и которые могут быть следующих видов:
«текстовые фреймы» – содержат текстовые данные, которые стороны отправляют друг другу.
«бинарные фреймы» – содержат бинарные данные, которые стороны отправляют друг другу.
«пинг-понг фреймы» используется для проверки соединения; отправляется с сервера, браузер реагирует на них автоматически.
также есть «фрейм закрытия соединения» и некоторые другие служебные фреймы.
В браузере мы напрямую работаем только с текстовыми и бинарными фреймами.
Слайд 27

Слайд 28

WebSocket Frameworks WebSocket сам по себе не содержит такие функции, как

WebSocket Frameworks

WebSocket сам по себе не содержит такие функции, как переподключение

при обрыве соединения, аутентификацию пользователей и другие механизмы высокого уровня. Для этого есть клиентские и серверные библиотеки, а также можно реализовать это вручную.
Пакет для нативной работы в сокетами доступен в репозитории в виде npm пакета “ws”
Слайд 29

Слайд 30

Слайд 31

Слайд 32

Socket.IO Socket.IO — библиотека JavaScript, основанная (написанная поверх) на веб-сокетах… и

Socket.IO

Socket.IO — библиотека JavaScript, основанная (написанная поверх) на веб-сокетах… и других

технологиях. Она использует веб-сокеты, когда они доступны, или такие технологии, как Flash Socket, AJAX Long Polling, AJAX Multipart Stream, когда веб-сокеты недоступны.
Слайд 33

Socket.IO В отличие от веб-сокетов, Socket.IO позволяет отправлять сообщения всем подключенным

Socket.IO

В отличие от веб-сокетов, Socket.IO позволяет отправлять сообщения всем подключенным клиентам.

Например, вы пишете чат и хотите уведомлять всех пользователей о подключении нового пользователя. Вы легко можете это реализовать с помощью одной операции. При использовании веб-сокетов, для реализации подобной задачи вам потребуется список подключенных клиентов и отправка сообщений по одному.
Слайд 34

Socket.IO В веб-сокетах сложно использовать проксирование и балансировщики нагрузки. Socket.IO поддерживает

Socket.IO

В веб-сокетах сложно использовать проксирование и балансировщики нагрузки. Socket.IO поддерживает эти

технологии из коробки.
Как отмечалось ранее, Socket.IO поддерживает постепенную (изящную) деградацию.
Socket.IO поддерживает автоматическое переподключение при разрыве соединения.
С Socket.IO легче работать.
Слайд 35

Socket.IO Как настроить socket.io на стороне NodeJS сервера (без фреймворков)

Socket.IO

Как настроить socket.io на стороне NodeJS сервера (без фреймворков)

Слайд 36

Socket.IO Здесь объект io предоставит нам доступ к библиотеке socket.io. Теперь

Socket.IO

Здесь объект io предоставит нам доступ к библиотеке socket.io. Теперь объект

io прослушивает каждое соединение с нашим приложением. Каждый раз, когда подключается новый пользователь, он выводит на экран «Новый пользователь подключен».
Если вы попытаетесь перезагрузить наш браузер на localhost, ничего не произойдет … Почему? Потому что наша клиентская сторона еще не готова.
Слайд 37

Socket.IO В реальном приложении, для того чтобы всё сработало, нам нужно

Socket.IO

В реальном приложении, для того чтобы всё сработало, нам нужно окно

с окном чата, входами для ввода имени пользователя / сообщения и кнопкой отправки. Для этого мы должны отдать html-файл с клиентской частью SocketIO. Давайте сделаем это с помощью Express’a
Слайд 38

Теперь наш localhost:3000 выглядит так:

Теперь наш localhost:3000 выглядит так:

Слайд 39

Socket.IO Когда у нас есть наш базовый шаблон, мы должны «установить»

Socket.IO

Когда у нас есть наш базовый шаблон, мы должны «установить» socket.io

на каждом клиенте, который попытается подключиться к нашему серверу. Для этого нам нужно импортировать библиотеку socket.io на стороне клиента:

Слайд 40

Socket.IO Далее, необходимо добавить IIFE для инициализации подключения: let socket =

Socket.IO

Далее, необходимо добавить IIFE для инициализации подключения:
let socket = io.connect(“https://localhost:3000”)
Как вы,

наверное, догадались, когда клиент загрузит страницу, он автоматически подключится и создаст новый сокет.
Поэтому, когда вы обновите страницу, мы увидим «Новый пользователь подключен» в вашем терминале.
Слайд 41

Socket.IO Когда пользователь подключается к нашему приложению, мы устанавливаем ему /

Socket.IO

Когда пользователь подключается к нашему приложению, мы устанавливаем ему / ей

имя пользователя по умолчанию, например «анонимный». Для этого нам нужно перейти на серверную часть (app.js) и добавить ключ в сокет. На самом деле, сокет представляет каждого клиента, подключенного к нашему серверу.
Слайд 42

Слайд 43

Socket.IO Мы также будем слушать вызовы/события, сделанные в «change_username». Если на

Socket.IO

Мы также будем слушать вызовы/события, сделанные в «change_username». Если на это

событие отправлено сообщение, имя пользователя будет изменено. На стороне клиента цель состоит в том, чтобы сделать наоборот. Каждый раз, когда нажимается кнопка смены имени пользователя, клиент отправляет событие с новым значением.
… для сообщений принцип тот же …
Слайд 44