В этом посте я расскажу о том, как я создавал веб-приложение на node.js с WebSocket через Socket.IO. Работу с node.js опишу подробно. Сама веб-приложение - это многопользовательская браузерная игра клон Трона. Я назвал её Tronode.js.
Описание процесса построения клиентской части можно прочитать здесь в соседнем посте, а процесс написания игровой логики в следующем.
Руководство по express рекомендует устанавливать модуль глобально:
Теперь переходим в директорию проекта и устанавливаем зависимости из созданного express'ом файла package.json:
Простой запуск нашего веб-приложения выполняется командой
При редактировании серверной части нужно перезапускать приложения. Для автоматизации этого процесса можно использовать модуль nodemon:
Немного о Stylus. С его помощью можно писать css правила в файлах с расширением .styl без фигурных скобок и точек с запятой в конце каждой строки, а так же использовать переменные и некоторые другие удобства. При каждом запуске stylus сам создаст классический .css-файл.
{
"name": "tronode.js",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "3.4.6",
"ejs": "*",
"stylus": "*",
"socket.io": "*"
}
}
Выполним npm install для установки модуля socket.io. Для того, чтобы веб-приложение начало принимать webscoket-соединения, нужно изменить app.js:
вместо
Теперь можно подключить пару обработчиков для тестирования (после var io и перед server.listen):
Здесь первым аргументов для функции .on идёт название события, например, 'connection' - стандартное событие при подключении клиента, а дальше 'my other event' - пример любого другого названия события, отправленного клиентом. Второй аргумент функции .on - функция обработчик, которая имеет доступ к данным (если это не стандартное событие) и к сокету (клиенту), который породил событие.
Для отправки события в другую сторону нужно вызвать метод emit, где первым аргументом идет название события, а вторым - объект с данными.
Теперь поработаем над клиентской частью.
Шаблон, который используется для стартовой страница находится в view/index.ejs. Добавим в секцию head подключение клиентской части sockets.io и скрипт для работы с сокетами. Для подключения sockets.io на стороне пользователя нам нужна js-библиотека. Для меня найти её оказалось не так просто и я скачал её с какого-то готового проекта, положил себе в public/javascripts/socket.io.min.js и подключил <script src="/javascripts/socket.io.min.js"></script>
Но оказывается, серверный socket.io при запущенном приложении генерирует на лету такую библиотеку. Для этого нужно подключать её так
Исходный код проекта я храню в bitbucket с использованием Mercurial. Директорию с зависимостями node_modules я предварительно исключил из hg. Bitbucket позволяет добавить deploy-ключи, поэтому deploy-script у меня состоит из вытягивания из репозитория, установке зависимостей и запуска/перезапуска приложения. Для перезапуска на рабочем окружении я использую модуль forever, который сам следит за тем, чтобы приложение было всегда запущено.
Веб-приложение у нас занимает отдельный порт. Если мы имеем другие веб-проекты на том же сервере, то там придется решать, какому приложению отдавать 80 порт. У меня его занял nginx, а 8080 занял apache. Я решил использовать тот же порт 3000, что и на тестовом окружении. Nginx у меня не поддерживает проксирование websocket, зато можно избавится от необходимости приписывать порт к адресу проекта. Для этого я использую nginx как прокси для поддомена. С этой целью я создал и подключил файл конфигурации:
Кроме того в переменные окружения стоит добавить NODE_ENV=production.
В руководстве описано как создавать и загружать приложения, но я продублирую информацию, чтобы картина была целостной. Прежде всего для работы с Heroku нам нужна их утилита из набора Heroku Toolbelt. Набор так же установит ssh и git. Heroku работает только с git, но это не проблема, так как мы можем использовать hg для обычно работы, а git только для деплоя. Главное, добавить служебные директории систем контроля версий в список исключения друг друга. Для этого нужно смотреть информацию о gitignore и hgignore.
После установки набора нужно выполнить
Если у вас не git-проект, то его нужно инициализировать
heroku apps:create my-name-of-app
Так как у нас приложение с websocket, то нужно включить его поддержку в heroku:
С первого раза моё приложение работало, но для транспорта socket.io вместо websocket выбирал xhr-polling, что гораздо медленнее. Я не смог выяснить почему, но позже я попробовал подключится с другого компьютера к моему приложению на Heroku и там уже был нормальный транспорт websocket.
Описание процесса построения клиентской части можно прочитать здесь в соседнем посте, а процесс написания игровой логики в следующем.
Введение в node.js
Node.js - платформа, которая позволяет запускать серверные приложения, написанные на языке javascript. Это означает, что на языке js вы можете открыть порт для прослушивания, например, 80 и отвечать на http запросы с применением различных обработок. В сравнении с php ему не нужен apache или nginx. Все обработки запросов могут происходить в рамках одного приложения. Node.js не ограничен созданием только веб-приложений, но в рамках этой статьи нас интересует только это.Пакетный менеджер npm
Платформа имеет большое количество модулей. Для их установки используется пакетный менеджер npm. Node.js и npm кроссплатформенные. Но на Windows есть удобный установщик, а, например, на Debian 7 мне пришлось собирать его самому, что впрочем заняло немного времени. Модули для node.js можно устанавливать глобально для системы - для этого используется флаг -g, либо только для проекта. При локальной установке в корне проекта создается директория node_modules, где хранятся все модули для проекта. При развертывание проекта в другом окружении удобно использовать специальный файл package.json, который располагается в корне проекта и содержит описание приложения, в том числе и список модулей, которые требуются для этого приложения, так называемые зависимости. Если такой существует, то командойnpm installбудут скачены и установлены все необходимые модули нужных версий.
Framework Express
Как у любой развитой платформы для веб-приложения, у node.js есть фреймворки для упрощения разработки. Один из таких фреймворков - express. Основное его назначение - это создание веб-приложений. С его помощью можно быстро создать приложение с использованием маршрутизации запросов, шаблонизаторов отображений, работы с сессиями, предобработки css и некоторые другие вещи.Руководство по express рекомендует устанавливать модуль глобально:
npm install -g expressВ этом случае станет доступна утилита express для работы с проектом. Создадим с её помощью веб-приложение с поддержкой сессий, шаблонизатором EJS и Stylus для написания css-правил:
express --sessions --css stylus --ejs tronode-js
Теперь переходим в директорию проекта и устанавливаем зависимости из созданного express'ом файла package.json:
cd tronode.js npm install
Простой запуск нашего веб-приложения выполняется командой
node app.jsПо умолчанию express слушает 3000 порт, поэтому для того, чтобы убедится, что базовое веб-приложение работает, нужно открыть в браузере http://localhost:3000.
При редактировании серверной части нужно перезапускать приложения. Для автоматизации этого процесса можно использовать модуль nodemon:
npm install -g nodemon nodemon app.jsОн будет отслеживать изменения файлов и каждый раз перезапускать приложение.
Немного о Stylus. С его помощью можно писать css правила в файлах с расширением .styl без фигурных скобок и точек с запятой в конце каждой строки, а так же использовать переменные и некоторые другие удобства. При каждом запуске stylus сам создаст классический .css-файл.
Работа с Socket.IO
Теперь добавим в package.json в раздел зависимостей строку "socket.io": "*". Мой файл выглядит так:{
"name": "tronode.js",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node app.js"
},
"dependencies": {
"express": "3.4.6",
"ejs": "*",
"stylus": "*",
"socket.io": "*"
}
}
Выполним npm install для установки модуля socket.io. Для того, чтобы веб-приложение начало принимать webscoket-соединения, нужно изменить app.js:
вместо
http.createServer(app).listen(app.get('port'), function(){ console.log('Express server listening on port ' + app.get('port')); });
нужно
var server = http.createServer(app); var io = require('socket.io').listen(server); server.listen(app.get('port'), function(){ console.log('Express server listening on port ' + app.get('port')); });
Теперь можно подключить пару обработчиков для тестирования (после var io и перед server.listen):
io.sockets.on('connection', function (socket) { socket.emit('news', { hello: 'world' }); socket.on('my other event', function (data) { console.log(data); }); });
Здесь первым аргументов для функции .on идёт название события, например, 'connection' - стандартное событие при подключении клиента, а дальше 'my other event' - пример любого другого названия события, отправленного клиентом. Второй аргумент функции .on - функция обработчик, которая имеет доступ к данным (если это не стандартное событие) и к сокету (клиенту), который породил событие.
Для отправки события в другую сторону нужно вызвать метод emit, где первым аргументом идет название события, а вторым - объект с данными.
Теперь поработаем над клиентской частью.
Шаблон, который используется для стартовой страница находится в view/index.ejs. Добавим в секцию head подключение клиентской части sockets.io и скрипт для работы с сокетами. Для подключения sockets.io на стороне пользователя нам нужна js-библиотека. Для меня найти её оказалось не так просто и я скачал её с какого-то готового проекта, положил себе в public/javascripts/socket.io.min.js и подключил <script src="/javascripts/socket.io.min.js"></script>
Но оказывается, серверный socket.io при запущенном приложении генерирует на лету такую библиотеку. Для этого нужно подключать её так
<script src="/socket.io/socket.io.js"></script>И сам код для тестирования:
<script> var socket = io.connect('http://localhost:3000'); socket.on('news', function (data) { console.log(data); socket.emit('my other event', { my: 'data' }); }); </script>Обратите внимание на адрес подключения. Порт должен совпадать с тем, на каком запущен express. Можно даже через шаблон передать значение серверной переменной port, чтобы удостовериться в их равенстве. Запустите серверное приложение, откройте в браузере консоль и перейдите на http://localhost:3000. Вы должны увидеть обмен сообщениями.
Развертывание на рабочее окружение
Развертывание node.js приложения мне показалось самым простым по сравнению с php и python, но оно имеет свои особенности. Для начала определим, где будет располагаться директория проекта. Я её поместил в /var/www/tronode.js. Теперь установим node.js и npm. Как я уже писал, для Debian 7 я делал сборку по этой инструкции.Исходный код проекта я храню в bitbucket с использованием Mercurial. Директорию с зависимостями node_modules я предварительно исключил из hg. Bitbucket позволяет добавить deploy-ключи, поэтому deploy-script у меня состоит из вытягивания из репозитория, установке зависимостей и запуска/перезапуска приложения. Для перезапуска на рабочем окружении я использую модуль forever, который сам следит за тем, чтобы приложение было всегда запущено.
Веб-приложение у нас занимает отдельный порт. Если мы имеем другие веб-проекты на том же сервере, то там придется решать, какому приложению отдавать 80 порт. У меня его занял nginx, а 8080 занял apache. Я решил использовать тот же порт 3000, что и на тестовом окружении. Nginx у меня не поддерживает проксирование websocket, зато можно избавится от необходимости приписывать порт к адресу проекта. Для этого я использую nginx как прокси для поддомена. С этой целью я создал и подключил файл конфигурации:
server { listen 80; server_name tronode.livelevel.net; location / { proxy_pass http://127.0.0.1:3000/; proxy_redirect off;
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #proxy_http_version 1.1; #proxy_set_header Upgrade $http_upgrade; #proxy_set_header Connection $connection_upgrade; } # Static files location location ~* ^.+.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe$ root /var/www/tronode.js/public; expires 10d; } }
Кроме того в переменные окружения стоит добавить NODE_ENV=production.
Бесплатный хостинг Heroku
Heroku позволяет бесплатно хостить на одной виртуальной единице приложение на node.jsВ руководстве описано как создавать и загружать приложения, но я продублирую информацию, чтобы картина была целостной. Прежде всего для работы с Heroku нам нужна их утилита из набора Heroku Toolbelt. Набор так же установит ssh и git. Heroku работает только с git, но это не проблема, так как мы можем использовать hg для обычно работы, а git только для деплоя. Главное, добавить служебные директории систем контроля версий в список исключения друг друга. Для этого нужно смотреть информацию о gitignore и hgignore.
После установки набора нужно выполнить
heroku loginВводим адрес электронной почты и пароль, указанные при регистрации. Будут вопросы о ssh-ключах, соглашаемся. Если всё успешно, то займемся приложением.
Если у вас не git-проект, то его нужно инициализировать
git initа затем создать приложение (при этом в git будет добавлен алиас адреса для heroku):
heroku apps:create my-name-of-app
Так как у нас приложение с websocket, то нужно включить его поддержку в heroku:
heroku labs:enable websockets -a my-name-of-appЗатем добавим все файлы в git, зафиксируем и вытолкнем в heroku:
git add . git commit -m "first commit" git push heroku master heroku openЕсли всё прошло успешно, то в браузере будет открыто ваше приложение. Но следует помнить, что heroku переопределяет переменную окружения PORT, а значит express будет запущен не на порту 3000 (иначе он вообще запущен не будет). Для адреса подключения к websocket следует использовать тот же адрес, что и приложения, но вместо протокола http// использовать ws://
var host = location.origin.replace(/^http/, 'ws');
С первого раза моё приложение работало, но для транспорта socket.io вместо websocket выбирал xhr-polling, что гораздо медленнее. Я не смог выяснить почему, но позже я попробовал подключится с другого компьютера к моему приложению на Heroku и там уже был нормальный транспорт websocket.
Комментариев нет:
Отправить комментарий