вторник, 18 октября 2016 г.

Как развернуть Node.js приложение на рабочем сервере с ngnix? PM2 спешит на помощь!

Итак, предположим что вы уже умеете запускать node.js приложение =), т.е. выполнить команду примерно такого вида, для вас не составит особого труда:

$: NODE_ENV=develop node --harmony api.js -p 8080

тут, кто не в курсе:

  • NODE_ENV: передаем переменную окружения в процесс node.js где он будет
    доступен таким образом, process.env.NODE_ENV
  • api.js собственно ваш файл приложения
  • -p 8080, а это уже параметр непосредственно передаваемый в ваше приложение

Запустить получилось, все хорошо, работаем потихоньку, разрабатываем. Впринципе, устраивает. Но вот пришла пора выпускать свой зверь-сервис в жизнь, так сказать, поселить его надобно на сервере добротном. Да так чтобы работал не покладая сил :). Т.е. без демонизатора не обойтись, а где его взять удобный и понятный, мало кто знает. Я тоже не знал, пока не наткнулся на замечательный менеджер процессов, написанный на нашем любимом JavaScript и node.js, речь пойдет о PM2.

Pm2 это хороший менеджер процессов. До недавнего времени работающий только
с нодой, но теперь он поддерживает скрипты написанные на разных языках и технологиях.
Из основого что он может:

  • Автоматический перезапуск упавшего приложения, из-за криво написанного обработчика ошибок :) или фатальной ошибке.
  • Использование кластерного мода, т.е. будут использованы все ядра CPU для работы приложения, чего не делает сама нода.
  • Удобный деплой
  • Логирование происходящего
  • Умение перезапустить приложение в случае изменения файлов, это штука удобна при разработке,
    но я использую Idea и подружить её дебаггер с pm2 вотчером не вышло. Пока... не вышло.

Для установки pm2 выполните эту "сложную" команду :)

npm i -g pm2

Все теперь можно попробовать запустить ваше приложение

pm2 start app.js
    // или со слежкой изменения файлов, т.е. будет атворестар в случае изменения файлов
    pm2 start --watch

pm2 выведет информацию о запущенном приложении

┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬──────────┐
│ App name │ id │ mode │ pid  │ status │ restart │ uptime │ cpu │ mem       │ watching │
├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼──────────┤
│ app      │ 0  │ fork │ 7263 │ online │ 0       │ 0s     │ 0%  │ 15.6 MB   │ disabled │
└──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴──────────┘

По "App name" можно обращаться к приложению или по его "id", давайте попробуем перезагрузить его

pm2 restart app
  // или
  pm2 restart 0

Для остановки необходимо выполнить эту команду:

pm2 stop app

Можно вообще удалить прилоджения из менеджера для этого выполните

pm2 delete app

Логи (логи будут показывать в режим "онлайн")

pm2 log app

просмотреть состояние приложений можно командой

pm2 monit
  а для конкретного приложения
  pm2 monit app

Небольшая шпаргалка с командами Pm2

Так, управлять процессом это уже хорошо, но как управлять процессом автоматически? Для этого все что необходимо, можно сконфигурировать в файле, и "скормить" этот файл pm2

Создадим в корне проекта, рядом с package.json новый файл конфигурации для pm2 process.json с таким содержимым.

{
  "apps": [
    {
      "name"       : "api-app", // Название приложения, по этому названию будет проще искать и работать с ним через Pm2
      "script"     : "./api.js", // Исполняемый файл приложения
      "args"       : "-p 8080", // Аргументы для вашего приложение (не путать с параметрами интерпритатора node.js) можно указывать массивом, например так: ['-p', '8080']
      "node_args"  : "--harmony" // Параметры интерпритатора node.js
      "exec_mode"  : "cluster", // Режим работы Pm2
      "instances"  : 4 // Количество процессов для запуска, проще: сколько ядер, столько и ставим.
    },
    {
      "name"        : "worker", // Это второе приложение в одном конфиге
      "script"      : "./worker.js",
      "watch"       : true, // это тот самый параметр, который заставит следить за изменениями файлов, и в случае изменения перезапустит приложение
      "env": {
        "NODE_ENV": "development", // Переменные окружения, которые будут добавлены по-умолчанию если при запуске не указан параметр env
        "DEBUG": "*"
      },
      "env_production" : {
         "NODE_ENV": "production" // Переменные для prodcution окружения
      }
    }
  ],
  "deploy": { // Интсрукции для деплоя ваших приложений, которые описаны выше
    "production": {
      "user": "node", // Имя под которым вы будут авторизоваться по SSH на серверах для деплоя
      "host": ["212.83.163.1", "212.83.163.2", "212.83.163.3"], // сервера для деплоя
      "ref": "origin/master", // Ветка для выгрузки
      "repo": "git@github.com:repo.git", // Ссылка (или путь) к репозиторию
      "path": "/home/project/api", // Путь на сервере к расположению проекта
      "post-deploy": "npm install; pm2 startOrRestart process.json --env production", // После отработки деплоя выполнит эти команды в корне выгруженного проекта
      "env": {
        "NODE_ENV": "production"
      }
    },
    "test": {
      "user": "node",
      "host": "212.83.163.3",
      "ref": "origin/develop",
      "repo": "git@github.com:repo.git",
      "path": "/home/project/api",
      "post-deploy": "npm install; pm2 startOrRestart process.json --env production"
    }
  }
}

Запуск через кофигурационный файл можно осуществить так:

pm2 start process.json

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

ssh-keygen -t rsa -f /path/to/file

Затем копируем публичный ключ из /path/to/file.pub на сервер в файл ~/.ssh/authorized_keys

Проверяем авторизацию. Далее, наверняка есть желание проверить этот самый деплой. Запускаем

pm2 deploy process.json production

Тут deploy это команда для запуска процесса деплоя с использованием параметров, указанных в файле process.json а атрибуте "deploy", process.json это наш файл конфигурации, и production это параметр окружения, в нашем случае это production окружение.

Перед деплоем нашего свежего кода, не забываем его пушить в репозиторий, а незабыть нам поможет package.json с его параметром "scripts", добавляем команду:

...
  "scripts": {
    ...
    "release": "git push origin master; pm2 deploy process.json production",
    "testup": "git push origin develop; pm2 deploy process.json test",
  }
  ...

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

npm run release

А для обновления тестового сервера

npm run testup

И небольшая настройка, если вы используете nginx и вы хотите, например, чтобы ваше приложение было доступно по адресу http://zver-project.com/api, то добавте следующее в конфиг nginx к вашему хосту

...
  location /api/ {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-Nginx-Proxy true;
      proxy_pass http://127.0.0.1:8080/;
      proxy_redirect off;
   }
  ...

Я описал совершенно мизирную часть возможностей pm2, он еще много чего умеет. Больше информации можно найти тут на официальном сайте PM2

пятница, 7 октября 2016 г.

Как искать landing страницы?

Да, и вправду как? Вот и я столкнулся с этим вопросом. Недолгий поиск привел к тому, что я нашел 100500 генераторов этих самых лендинг страниц, на втором месте по результативности оказались "помощники" в раскрутке и замыкал троицу "победителей" в номинации "поиск по landing страницам" индивидуальные, позиционирующие себя как элитные, разработчики.

Мда, забавно, а что мне теперь делать, обычному человеку, потенциальному клиенту, который хочет банально найти уже готовые страницы, выбрать по какому-то критерию, услугу или товар, который будет предложен на этих самых целевых страницах. Так, кто не в курсе, называют лендинг страницы, еще их именуют посадочными. Почему мне вообще пришла идея такая? Искать по лендингам? Как показала практика, на лендинг страницах предлагают очень много действительно интересных, новых товаров, какие-то уникальные акции проводя на них же, представляют новинки с возможностью предзаказа и пр. в общем удобно и красиво.

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

"Во дела!" подумал я :), надо делать. Надо делать площадку для поиска таких страниц, и дать возможность регистрироваться владельцам таких страниц. Я смогу комфортно искать нужные мне товары, а владелец landing страницы получит дополнительный источник потенциальных клиентов. Неплохо, неправда ли?

Вот сел я темным вечером одним, и сделал сервис, и назвал я его Landingbook. Сел и стал ждать, потом дал немного рекламки, опять как-то вяленько, мало активности, видимо не суждено мне комфортно искать авторские работы, уникальные предложения и акции, новинки и все то, что очень часто предлагается через лендинг страницы.

Так а в чем же суть сервиса Landingbook? Суть проста: человек регистрирует свою лендинг страницу в каталоге, она модерируется и попадает в поисковую выдачу, все!

Но прошло немного времени, а каталог был пуст, мало кто хочет регистрировать свою страницу, почем? Я не понимаю если честно. Видимо пугает форма добавления, буду добавлять парсер данных по ссылке, чтобы минимизировать ввод данных при регистрации страницы. Так же вынашиваю планы, все же, написать поискового бота, но с этим есть технические сложности связанные именно со технической спецификой лендинг страниц.

В итоге простенький каталог, похоже, перерастет в очень непростенький поисковичек.

Т.к. блог имеет техническю направленность, думаю не лишним будет упомянуть о то что все это чудо работает на Yii2, а вот поисковый бот и парсер будут уже сделаны на node.js. Постараюсь, описывать интересные моменты в разработке, посмотрим что из этого выйдет.

вторник, 12 мая 2015 г.

Как установить io.js на Ubuntu

Всем привет! Для того чтобы установить io.js на лучшую в мире ОС :) надо обладать особым складом ума, иметь навыки и ловкость ниндзя. Итак по-порядку:
  • Создадим или перейдем в папку с вашими любимыми бинарниками, например /home/tobi/soft
  • Скачаем архив с оф. сайта, в моем случае это x64 (все доступные для текущей версии 2.0.1)
  • Распакуем и удалим архив
  • Создадим ссылки на бинарники что бы можно было вызвать iojs в любом месте
Ну а теперь понятным языком :)
$: mkdir /home/tobi/soft
$: cd /home/tobi/soft
$: curl -O https://iojs.org/dist/v2.0.1/iojs-v2.0.1-linux-x64.tar.gz
$: tar -xvf iojs-v2.0.1-linux-x64.tar.gz
$: unlink iojs-v2.0.1-linux-x64.tar.gz
$: ln -s /home/tobi/soft/iojs-v2.0.1-linux-x64/bin/iojs /usr/bin/iojs
проверяем установку для пущей правды перейдем в домашний каталог
$: cd ~
$: iojs -v
v2.0.1
Это пожалуй все

понедельник, 24 ноября 2014 г.

Magento. Подчищаем атрибуты, без шума и пыли.

Вот вы создали какой-то "кривой" атрибут и решили быстренько его убрать от глумящихся над вашей некомпетентностью коллег, или же поставили очередной вело-криво-собранный модуль написанный индусами, не беда пару строк в красивом файле восстановят вашу репутацию:

<?php
include 'app/Mage.php';
Mage::app();
$setup = Mage::getResourceModel('catalog/setup','catalog_setup');
$setup->removeAttribute('catalog_product','your_attribute_name');

Вместо your_attribute_name вписываете имя удаляемого атрибута. Все! честь спасена.

вторник, 11 февраля 2014 г.

CORS. Как посылать запросы GET, POST, DELETE etc.

В наши дни не знать что такое CORS (wiki), сродни прогулки тёмной ночью по последнему этажу, недостроенной высотки где-нибудь за городом, в неблагополучном районе... Т.е. смертеподнобно, а особенно если вы фронт-енд разработчик.

Более подробно вы можете ознакомиться перейдя по ссылкам в конце текста, я же скажу вкратце, CORS это набор HTTP заголовков, которые позволяют объяснить браузеру и серверу, что они хоть и из разных доменов, но одной крови работать могут вместе. Т.е. кросс-доменные зпросы поддерживаються. Кто не знает, то запрос из браузера посредством старого, доброго JavaSctipt недавно, так на чистоту, без костылей сделать было невозможно. Ура, эти мрачные времена канули в лету. Пришёл на момощь CORS.

Речь пойдет именно о процессе использования и базовой настройки заголовков сервера, и отсыла запросов клиента с использованием jQuery.

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

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers

Это тот минимум, который необходим для успешной работы наших кросс-доменных запросов. немного рапишем что, к чему:

Access-Control-Allow-Origin — (обязательный) собственно список разделенный пробелами допустимых доменнов (источников), которые будут ломиться делать запросы к нам на сервер. Из особенностей: регистрозависим, поддерживает маски, например, http://api.superservice.com/, http://*.superservice.com/ или, как вы могли догадаться просто "звездочка" *. Этот заголовок будет сравниваться с заголовком Origin клиентского запроса.
Access-Control-Allow-Method — (обязательный) это список доступных HTTP методов, разделенных запятыми. Особое внимание стоит уделить тому, что "звездочка" аля * работать не будет!
Access-Control-Allow-Headers — (обязательный вместе с Access-Control-Request-Headers) список через запятую заголовков разрешенных в запросе.

Пример настройки для Apache:

# CORS заголовки (добавте это, например, в .htaccess)
<ifmodule mod_headers.c>
  Header always set Access-Control-Allow-Origin: "*"
  Header always set Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
  Header always set Access-Control-Allow-Headers "X-Requested-With, content-type"
</ifModule>

Давайте разберемся, что тут происходит? А происходит следующее, запросы будут обработанны с любого источника, если они будут одного из перечисленных в Header always set Access-Control-Allow-Methods типов, так же будут подерживаться заголовки X-Requested-With и content-type

Тот же пример для PHP

<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: X-Requested-With, content-type');
?>

Теперь, предположим, что сервис у нас крутится по адресу http://api.superservice.com/ , а GET запрос нам надо сделать со странички http://pure.client.net/. Это не сложно сделать, к примеру с jQuery:

jQuery.ajax({
  url: 'http://api.superservice.com/credit-card-ids',
  type: 'GET',
  contentType: 'application/json', // тот тип контента, который вы будете получать от своего сервиса
  headers: { }, // Разные заголовки, нестандартные заголовки, не забудте их указать в Access-Control-Allow-Headers
  success: function (res) { 
  console.log(res);
},
error: function () { ... }
});

Думаю самое время упомянуть о неприятном моменте с AngularJs если вам понадобиться сделать кросс-доменый запрос с помощью $http или $resource, то надо будет удалить один заголовок, делается это так:

myApp.config(['$httpProvider', function($httpProvider) {
 $httpProvider.defaults.useXDomain = true;
 delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
]);

Такое поведение странно, для меня, по крайней мере, несмотря на то что заголовок X-Requested-With вы укажите Access-Control-Allow-Headers не исключены проблемы с запросами. Ходят слухи что это бага AngularJs, кому интересно, если найдете что, отпишитесь в коментах.

Переходим к интересным запросам типа POST/PUT/DELETE, всех их объединяет то что они пытаются что-то изменить на сервере. Но как сделать такой запрос кросс-доменным? Сначала надо получить список заголовков типа Access-Control-*, делается это предварительным OPTIONS запросом. Который может вернуть тот же набор Access-Control-*, что и обычный метод GET.

На сервере если вы больше нигде и не для чего более не будете использовать OPTIONS запросы, то можете смело на все запросы типа OPTIONS отвечать необходимыми заголовками в том числе и такими заветными для нас как CORS заголовки. Если кто не понял то имееться ввиду добавить правило для всех запросов типа OPTIONS в роутах вашего любимого фреймворка. Или же сделать обработку руками.

Этот предварительный запрос (prefligth-запрос), делается автоматически jQuery или тем же Angular, по-тому же URL что и ваш основной запрос POST/PUT/DELETE поэтому будте внимательны если вы не сделаете правильной обработку запросов типа OPTIONS то и запрос POST/PUT/DELETE у вас сделать не получиться, из-за политики безопасности, который следуют браузеры.

Пример запроса POST с использованием jQuery.

jQuery.ajax({
  ulr: 'http://api.superservice.com/credit-card-ids',
  type: 'POST',
  data: '{"content": "' + content + '"}', // внимание! если вы передатите параметр как объект то это будет Query String, в данном случае это Request Body, т.к. тут передана строка.
  contentType: 'application/json', // тот тип контента, который вы будете получать от своего сервиса
  headers: { }, // Разные заголовки, нестандартные заголовки, не забудте их указать в Access-Control-Allow-Headers
  success: function (res) { 
  console.log(res);
},
error: function () { ... }
});

Я старался изложить кратко, в справочном виде, не очень получилось :), но думаю многим кто прочитал это сэкономит время. Ниже список источников, который позволят вам более глумого изучить вопрос CORS

пятница, 20 декабря 2013 г.

Node.js. Легкий стресс-тест через Phanos.

На волне набирающей популярности ноды. Решил взяться за написание маленького паучка, который бы имитировал реального пользователя. И гулял по сайту не аки бот, а как нормальный человек. Благо в инфраструктуре ноды добра для написания такого чудного существа достаточно. Основа паука стал phantom.js, это "обезгалвленный" движек Webkit, другими словами - это браузер лишенный UI.

Итак, Phanos, в первую очередь мне он был нужен для одной важной задачи, - сымитировать набег пользователей на сайт в течении 2х недель! Самое интересное что для сайт этот был single page application. Т.е. это полноценное web-приложение с кучей асинхроных запросов и всякой джэесины :). Которую как мы понимаем не в состоянии отработать jMeter ни тем более Apache Banchmark. А так как интерес к node.js у меня сейчас в обостренной форме, хотя как мы можем заметить, в рунете заметен спад заинтересованности. То собственно и был рожден сей паучек. Еще одна особенность сайта - это то что он также реализован на node.js и эта штука вся крутится на heroku, то вся статитка также отдается через аппликуху, то так же важно грузить и все остальное, т.е. картинки, стили и собственно Js.

Phanos, его особенности это:

  • имитация посещения пользователями длительное время, т.е. вы можете оставить тулзу работать в течении дня, недели, месяца, да хоть года, указать количество пользователей и вперед.
  • полностью отрабатывает страницы с ajax js
  • легко устанавливается и работает под Linux и Windows, теоретически должен работать и на Mac.

Для начала работы с Phanos необходимо установить node.js. А затем выполнить команду, Ubuntu:

$ sudo npm install -g phanos

для Windows:

$ npm install -g phanos

В процесе установки будет загружен phantomjs под вашу платформу. Ниже приведу пару примеров как пользоваться phanos.

$ phanos -u = http://www.google.com -w 10 -t 3600

Команда выше запустить 10 волкеров (пользователей) "гулять" по гугл в течении 1 часа (3600 сек.). Как лего догадаться то -u это адрес страницы, -w это количество пользователей, -t время работы скрипта, здесь час.

$ phanos -u = http://www.google.com -w 10

Тут просто 10 волкеров в течении 30 сек. будут шнурять по сайту.

На подходе режим работы "наплыв", заключаться будет в том чтобы сэмитировать "сарафанное" радио, "хабраэффект", в общем плавное нелинейное увеличение пользователей. Так же будет возможность указать несколько адресов сразу. Небольшой отчетик о сделанных запросах и их времени. Автоматическое "гуляние" по сайту, без необходимости перечисления всех страниц, которые надо грузить.

Тулза открыта. Доп. информация по установке, параметрам и использованию на github все найденные баги/фичи если не сложно оставляем там же.


P.S.: Phanos - это симбиоз от слов Phantom и stress, а не то что могут подумать некоторые изощреные умы =)

понедельник, 28 октября 2013 г.

Magento. Операторы сравнения.

Magento выделяется особой самобытностью, в этом движке любят делать все не так как все, хорошо это или плохо... не мне судить, но вот чтобы добавить какой-дибо атрибут для фильтрации колекции, необходимо знать операторы сравнения. Ниже они преведены собственной персоной.
Операторы сравнения Magento:

array("eq"=>'n2610')
WHERE (e.sku = 'n2610')

array("neq"=>'n2610')
WHERE (e.sku != 'n2610')

array("like"=>'n2610')
WHERE (e.sku like 'n2610')

array("nlike"=>'n2610')
WHERE (e.sku not like 'n2610')

array("is"=>'n2610')
WHERE (e.sku is 'n2610')

array("in"=>array('n2610'))
WHERE (e.sku in ('n2610'))

array("nin"=>array('n2610'))
WHERE (e.sku not in ('n2610'))

array("notnull"=>true)
WHERE (e.sku is NOT NULL)

array("null"=>true)
WHERE (e.sku is NULL)

array("gt"=>'n2610')
WHERE (e.sku > 'n2610')

array("lt"=>'n2610')
WHERE (e.sku < 'n2610')

array("gteq"=>'n2610')
WHERE (e.sku >= 'n2610')

array("moreq"=>'n2610') //a weird, second way to do greater than equal
WHERE (e.sku >= 'n2610')

array("lteq"=>'n2610')
WHERE (e.sku <= 'n2610')

array("finset"=>array('n2610'))
WHERE (find_in_set('n2610',e.sku))

array('from'=>'10','to'=>'20')
WHERE e.sku >= '10' and e.sku <= '20'