На этом уроке
- Узнаем, что такое веб-сокеты. Рассмотрим их применение в приложениях реального времени.
- Выясним, как использовать эту технологию в Node.js, используя библиотеку Socket.io.
- Познакомимся с модулем worker_threads в Node.js.
- Научимся выполнять ресурсоёмкие вычисления в отдельных потоках, не блокируя основной.
Оглавление
Теория урока
Введение
Веб-сокеты
Библиотека Socket.io
Серверная часть Socket.io
Клиентская часть Socket.io
Рассылка сообщений
Однопоточность
Модуль worker_threads
Модуль crypto
Заключение
Практическое задание
Глоссарий
Дополнительные материалы
Используемые источники
Теория урока
Введение
В предыдущем уроке мы познакомились с понятием http-сервер и написали собственный сервер, способный принимать различные запросы, обрабатывать их и отвечать на них. Однако есть большой класс приложений, для которых формат «клиент сделал запрос к серверу, сервер ответил, и соединение закрылось» не может обеспечить требуемую функциональность. Самые яркие представители этого класса — мессенджеры.
Чтобы мессенджер работал, а клиент получал сообщения от других клиентов, надо научить сервер передавать данные клиенту. Конечно, клиент может сам делать запросы к серверу с определённой частотой, но тогда останется некоторая задержка. А мессенджеры — это приложения реального времени.
Если частота запросов к серверу со стороны клиента будет слишком большой, то возникает вероятность наложения времени выполнения одних запросов на другие. То есть, когда клиент шлёт запрос, сервер его обрабатывает, и пока он его обрабатывает, клиент присылает новый запрос. Это приводит к тому, что до появления ответа от сервера состояние клиента уже поменяется, и данные будут не актуальны.
|
Однако сам сервер не инициирует соединение с клиентом. У большинства клиентов динамические IP-адреса, и до них нельзя достучаться снаружи.
В результате для реализации такой функциональности используется схема, когда клиент инициирует TCP-соединение с сервером, и оно поддерживается в течение длительного времени в отличие от обычных http-запросов.
Веб-сокеты
Веб-сокет — это стандарт двухсторонней связи клиента с сервером по TCP-соединению, который совместим с HTTP, предназначенный для обмена сообщениями в режиме реального времени.
Чтобы понять отличие веб-сокетов от http-запросов, рассмотрим две схемы применительно к задаче с мессенджером.
В случае с http-запросами схема работы будет примерно такой:
Рис. 1. Получение информации мессенджером посредством http-запросов
На этом рисунке мы видим, что Клиент 1 периодически спрашивает сервер о сообщениях для него. Когда на сервер приходит сообщение от Клиента 2 для Клиента 1 — оно передаётся ему в ответе сервера при следующем опросе. У такого подхода есть несколько недостатков:
- Бесполезные запросы. Клиент постоянно опрашивает сервер, то есть нагружает его своими запросами, хотя никакой полезной информации в ответах не передаётся.
- С появления на сервере сообщения для клиента и до момента, когда оно передалось клиенту, наблюдается задержка. Чем ниже частота опроса сервера, тем выше задержка.
Очевидно, что такой подход к построению подобных приложений совершенно нерационален. Взглянем на подход, который подразумевает использование веб-сокетов.
|
Рис. 2. Получение информации мессенджером посредством веб-сокетов
На рисунке 2 мы видим, что Клиент 1 только один раз устанавливает соединение с сервером. Дальше он просто ждёт, когда ему поступит информация от сервера. Когда на сервер приходит сообщение от Клиента 2, он сразу же отсылает его к Клиенту 1. Это происходит практически моментально, однако на рисунке всё равно изображается некоторая задержка. Её величина зависит от загрузки сервера, его вычислительных возможностей, от скорости соединения с Клиентом 1 и других технических факторов. Однако это совершенно не та задержка, когда сервер ждёт входящего запроса, чтобы передать в ответ данные, как это изображено на рисунке 1.
Таким образом, естественной сферой применения веб-сокетов становятся те приложения, которые работают в режиме реального времени:
- Чат-приложения (мессенджеры).
- Навигаторы.
- Приложения для интернета вещей (IoT-приложения).
- Многопользовательские игры.
- Прочее.
Сегодня веб-сокеты поддерживаются всеми современными браузерами: Google Chrome, Safari, Mozilla, Opera, Microsoft Edge и т. д.
Библиотека Socket.io
Библиотека Socket.io — это событийно-ориентированная JavaScript-библиотека для веб-приложений и обмена данными в реальном времени.
Она состоит из двух частей:
● клиентской, предназначенной для использования в браузере;
● серверной, предназначенной для использования на стороне сервера Node.js.
В основном Socket.io использует веб-сокеты для своей работы. Однако иногда она применяет и другие технологии — Flash Socket, AJAX Long Polling, AJAX Multipart Stream, не меняя при этом интерфейс. Это удобно. Если пользователь запустит наше приложение на каком-нибудь очень старом браузере, который не поддерживает веб-сокеты, оно всё равно будет работать, используя эти технологии.
|
Библиотека даёт возможности, которые не может предоставить нативная реализация веб-сокетов, например, одновременную рассылку сообщений нескольким подключённым клиентам.
Эта библиотека используется технологическими гигантами по всему миру для создания приложений реального времени.
В этом уроке мы рассмотрим работу с этой библиотекой с обеих сторон — клиентской и серверной. Создадим базу, показывающую принцип создания чатов, основанного на базе библиотеки Socket.io.
Серверная часть Socket.io
Начнём с серверной части и инициализируем проект:
npm init --yes |
Официальный сайт говорит, что для установки последней версии пакета, требуется запустить следующую команду:
npm install socket.io |
В качестве сервера возьмём сервер из предыдущего урока. Для создания же сокетного соединения достаточно дописать несколько строк.
Сначала инициализируем пакет сокета:
const io = require('socket.io'); |
Далее — созданный http-сервер сохраняем в константу:
const app = http.createServer((request, response) => { if (request.method === 'GET') { const filePath = path.join(__dirname, 'index.html'); readStream = fs.createReadStream(filePath); readStream.pipe(response); } else if (request.method === 'POST') { let data = ''; request.on('data', chunk => { data += chunk; }); request.on('end', () => { const parsedData = JSON.parse(data); console.log(parsedData); response.writeHead(200, { 'Content-Type': 'json'}); response.end(data); }); } else { response.statusCode = 405; response.end(); } }); |
Остаётся только проинициализировать точку доступа сокета, описать первые сообщения для подключившихся клиентов и запустить сервер.
const socket = io(app); socket.on('connection', function (socket) { console.log('New connection'); socket.on('CLIENT_MSG', (data) => { socket.emit('SERVER_MSG', { msg: data.msg.split('').reverse().join('')}); }); }); app.listen(3000, 'localhost'); |
Для инициализации точки доступа сокета передадим в модуль io наш созданный http-сервер. Точка доступа сокета может существовать и без этого, однако нам требуется связать её с уже имеющимся http-сервером.
Так как библиотека socket.io событийно-ориентированная, то и работа в библиотеке ведётся по большей части с событиями.
Например, событие ‘connection’ возникает, когда новый клиент подключается к серверу. В нашем случае мы сообщаем об этом соединении в терминал.
Далее создаём обработчик для события 'CLIENT_MSG'. Судя по названию, событие будет ждать сообщения от клиента. В ответ на него сервер станет посылать сообщение, в которое мы записываем принятую строку в обратном порядке.
Последним шагом запускаем http-сервер, чтобы он слушал порт 3000. Так как сервер сокета связан с http-сервером, он также будет слушать порт 3000.