Маленькая заметка о том, как подружить Heroku, Kraken.js и Sockjs

в 20:24, , рубрики: Без рубрики

Некоторое время назад я, в поисках новых инструментов для реализации очередного «домашнего» проекта, наткнулся на Kraken.js — Open Source проект от PayPal. Kraken.js представляет из себя очередной Node.js-фреймворк, основанный на express. Поискав на Хабре, я не обнаружил, ровным счетом, ничего. Встретил только одно упоминание в виде ссылки на главный сайт здесь.

Чем же он меня привлек, и чем он отличается от известных Derby.js, Meteor.js, Sails.js, и др.?

А понравился он мне прежде всего тем, что не накладывает на разработчика совсем уж жестких ограничений (прежде всего на источники данных, на менеджеры пакетов, ...), и при этом вносит некоторую структурированность в код, предлагая следовать MVC-модели. Не хочу здесь подробно останавливаться на всех его плюшках и особенностях, благо все отлично расписано на сайте проекта, а сразу перейду к «своим баранам».

Итак, задача залить Kraken.js-приложение на сервис Heroku, заставить его там работать, и, на сладкое, прикрутить Sockjs.

Оговорюсь, что все описанные далее действия я выполнял на Ubuntu 13.10, но думаю, что для других ОС сильных отличий быть не должно. Для начала идем на heroku.com, регистрируемся и скачиваем Heroku Toolbelt — консольный клиент для работы с Heroku. Вводим в коммандной строке:

$ heroku login

Теперь мы готовы к разворачиванию приложений на Heroku. Следующим шагом установим Kraken.js, для этого выполняем:

$ sudo npm install -g generator-kraken

Затем преходим в каталог с проектами и набираем комманду $ yo kraken и в интерактивном режиме отвечаем на вопросы генератора:

     ,'""`.
    / _  _ 
    |(@)(@)|   Release the Kraken!
    )  __  (
   /,'))((`.
  (( ((  )) ))
   ` `)(' /'

[?] Application name: Kraken-Sockets
[?] Description: A test kraken application with socks.js
[?] Author: <Ваше имя>
[?] Use RequireJS? (Y/n) n

Генератор создал каталог одноименный приложению и базовую структуру Kraken.js-приложения. Рассмотрим структуру подробнее:

/config
Тут лежат конфигурационные файлы приложения в формате json

/controllers
Тут весь роутинг и логика

/lib
Сюда можно положить свои и сторонние библиотеки

/locales
Файлы локализации

/models
Модели

/public
Web-ресурсы, статика

/public/templates
Тут лежат шаблоны

/tests
Функциональные и юнит-тесты

index.js
Точка входа в приложение (загрузчик)

Наберем команду npm start и по адресу localhost:8000 можем полюбоваться работающим Kraken.js-приложением. Хорошо, теперь пришло время залить его на Heroku. Но для начала нужно привести приложение в соответствие с требованиями. Файл package.json у нас уже есть, теперь нам нужно создать файл Procfile с таким содержанием:

web: node index.js

Этот файл является инструкцией для Heroku, как запускать наше приложение. Кроме этих файлов Heroku требует, чтобы приложение слушало порт, указанный в переменной окружения PORT. Через json-конфиги мы этого сделать не сможем. Специально для этих случаев в Kraken.js предусмотрен механизм программного конфигурирования. Метод отвечающий за программную конфигурацию находится в файле index.js. Мы должны придать ему следующий вид:

app.configure = function configure(nconf, next) {
    // Async method run on startup.
    nconf.set('port', Number(process.env.PORT || 5000));
    next(null);
};

Ну а теперь создадим git-репозиорий проекта:

$ git init
$ git add .
$ git commit -m "init"

И выполним:

$ heroku create kraken-test-socksjs

Heroku создаст нам приложение, доступное по адресу kraken-test-socksjs.herokuapp.com и сразу подключит нам удаленный репозиторий, push в который устанавливает приложение на сервер.
Пробуем:

$ git push heroku master

Заходим на kraken-test-socksjs.herokuapp.com и убеждаемся, что все работает. Но не спешим радоваться; самый первый коммит (например добавим что-нибудь в шаблон главной страницы) ломает все приложение. Дальше у меня ушло много часов просмотра логов (комманда $ heroku logs) и гугления. А все оказалось очень просто. Heroku, почему-то, теряет некоторые зависимости второго уровня (зависимости зависимостей приложения — звучит кошмарно, приведу пример: в нашем случае он теряет модуль formidable, от которого зависит модуль kraken-js). И еще он не выполняет grunt-таски по подготовке ресурсов (компиляция less, шаблонов, и т.д.).

Чтобы устранить эти проблеммы делаем следующее: в файле package.json переносим все devDependencies в dependencies и добавляем в dependencies модуль "grunt-cli": "~0.1.13". Это позволит нам вызывать grunt вручную при деплое приложения. После всех изменений файл package.json должен принять такой вид:

{
    "name": "kraken-sockets",
    "version": "0.1.0",
    "description": "",
    "author": "Your Name",
    "main": "index.js",
    "scripts": {
        "test": "grunt test",
        "start": "node index.js",
        "postinstall": "./node_modules/grunt-cli/bin/grunt build --force"
    },
    "engines": {
        "node": ">=0.10.0"
    },
    "dependencies": {
        "kraken-js": "~0.7.0",
        "express": "~3.4.4",
        "adaro": "~0.1.x",
        "nconf": "~0.6.8",
        "less": "~1.6.1",
        "dustjs-linkedin": "~2.0.3",
        "dustjs-helpers": "~1.1.1",
        "makara": "~0.3.0",
        "mocha": "~1.17.0",
        "supertest": "~0.8.2",
        "grunt": "~0.4.1",
        "grunt-cli": "~0.1.13",
        "grunt-contrib-less": "~0.9.0",
        "grunt-dustjs": "~1.2.0",
        "grunt-contrib-clean": "~0.5.0",
        "grunt-contrib-jshint": "~0.6.4",
        "grunt-mocha-cli": "~1.5.0",
        "grunt-copy-to": "0.0.10"
    },
    "devDependencies": {

    }
}

Обратите внимание на скрипт "postinstall", который как раз готовит ресурсы приложения. С проблеммой ресурсов справились, а что делать с пропавшими модулями? Это видимо баг новой системы кэширования модулей Heroku. Напишу им тикет на днях, а пока я справляюсь с помощью плагина heroku-repo. Просто перед каждым «пушем» релиза я чищу кэш модулей коммандой:

$ heroku repo:purge_cache -a kraken-test-socksjs

Ух, с дружбой Heroku и Kraken.js справились, теперь самое приятное: прикрутим к Кракену Sockjs.

Sockjs распространяется в виде npm-модуля и отлично прикручивается к express-приложению, но для того, чтобы ее прикрутить нужен доступ к серверу (тот, что представлен модулем http, на который вешается express.js). Так вот в Kraken.js он хорошо спрятан от разработчика. Но, покопавшись в исходниках Кракена, я нашел как его выцепить. Приведу сразу готовый код, который должен быть вставлен в index.js на место этого участка:

kraken.create(app).listen(function (err) {
        if (err) {
            console.error(err.stack);
        }
    });

Вот он:

var k = kraken.create(app);

    k.listen(function (err) {
        if (err) {
            console.error(err.stack);
        } else {
            var http = k.app.get('kraken:server');
            var sockjs_opts = {sockjs_url: 'http://cdn.sockjs.org/sockjs-0.3.min.js'};
            var sockjs_echo = sockjs.createServer(sockjs_opts);
            sockjs_echo.on('connection', function(conn) {
                hub.sockjs_pool.push(conn);
                conn.on('data', function(message) {
                    hub.sockjs_pool.forEach(function(con) {
                        con.write(message);
                    });
                });
            });
            sockjs_echo.installHandlers(http, {prefix:'/echo'});
        }
    });

На этом я закругляюсь. Весь код проекта можно посмотреть на github. Поиграть с развернутым приложением тут.

Очень надеюсь, что эта статься поможет кому-нибудь сэкономить время при отладке Kraken.js-приложений на Heroku и прикручивании к ним всяких вкусностей.
P.S. Это моя первая статья, поэтому конструктивная критика очень приветствуется.

Автор: rodenis

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js