Стритрейсинг на Nginx + Passenger

Опубликовано 07.10.2008

Что роднит веб-девелоперов и стрит-рейсеров? Стремление выжать из своей “лошадки” максимум возможного. И для тех, и для других лишняя миллисекунда - это потенциальное поражение в конкурентной борьбе за славу и уважение миллионов фанатов. Возможно, с миллионами я слегка загнул, по крайней мере для гонщиков :)

Даже когда счет идет на тысячи постоянных посетителей, лишняя миллисекунда может вылиться в многие и многие бесцельно потраченные серверо-часы. Но что самое страшное - лишнее время загрузки страницы увеличивает шансы на то, что особо торопливый пользователь просто не станет дожидаться пока ваш сервер соизволит выдать ему требуемую информацию, плюнет и уйдет в Google искать ваших конкурентов. Менеджер проекта в ярости, инвесторы спрашивают где бабки, у вас стресс. Занавес.

Статика

Если вы занимаетесь веб-разработкой не первый день (а хотя бы второй), то вы наверняка знаете, что в процессе загрузки получение непосредственно самой страницы (HTML-кода) может занимать порядка 10-15%, редко когда больше. Львиную же долю времени отнимает загрузка статичных файлов - картинок, javascript’ов, CSS-файлов, флэшек и прочих прелестей.

Для быстрой отгрузки статичных файлов очень часто используется сервер Nginx. Если ничего не поменялось за последнее время, то он до сих пор считается самым быстрым веб-сервером для раздачи статики. Старикашка Apache за ним угнаться не может точно, другие тоже отстают. Поправьте если ошибаюсь.

Динамика

Однако, старикашка Apache тоже не лыком шит, у него есть свои плюсы. В контексте разработки Rails-проектов - это Passenger (он же mod_rails), который есть только под Apache.

Развертывая проекты под Nginx, для обработки динамики приходится стартовать отдельные rails-процессы (например, с использованием Thin, Mongrel и собратьев), причем чем активнее используется приложение, тем больше надо процессов. До поры до времени такой подход имеет право на жизнь, но однажды наступает один из возможных вариантов:

  • Вы хотите сократить время загрузки на секунду-другую
  • Вам надо запустить полсотни мелких рельсовых проектов с мизерной посещаемостью
  • Вам надо запустить толстый проект на сервере с малым объемом памяти (например, на VPS-хостинге от Agava, Linode и других)
  • У ваших инвесторов кончилось бабло на закупку железа под ваш мега-стартап
  • Вас парит что в памяти сидят и курят бестолковые процессы по сотне метров

Меня настигли одновременно 4 пункта из 5, угадайте какие. Поэтому единственный вариант для меня - это переход на Passenger.

Лед и пламень

Однако, решил я, если уж браться за оптимизацию и минимизацию, то нужно свести воедино преимущества двух серверов - быструю отдачу статики Nginx’ом и экономию памяти за счет Passenger под Apache. Задача не очень сложная и просто требует понимания того, кто за что отвечает. В моем случае получается вот так:

  • Apache - обрабатывает динамические запросы к Rails
  • Nginx - отдает статичные файлы (картинки, css, js, flash), закэшированные страницы и осуществляет поддержку фрагментарного кэширования через SSI+memcached

Входящий запрос попадает на Nginx, который определяет, можно ли отдать статику. Если результат запроса нельзя отдать из статичного файла - Nginx проксирует запрос на Apache. Nginx вешается на порт 80, а Apache например на 3000.

Конфигурация Nginx (размещаем ее в /my_project_folder/nginx.conf):

upstream apache {
  server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
}
server {
  listen 80;

  server_name myproject.com www.myproject.com somealias.com;

  proxy_next_upstream error;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_redirect false;
  proxy_max_temp_file_size 0;

  # Задаем корневую папку из которой будет браться статика
  root /my_project_folder/public;

  location / {
    proxy_set_header Host myproject.com;

    if (-f /my_project_folder/_cache/$uri.html) {
      root /my_project_folder/_cache;
      rewrite (.*) $1.html break;

    }

    if (-f /my_project_folder/_cache/$uri/index.html) {
      root /my_project_folder/_cache;
      rewrite (.*) $1/index.html break;
    }

    if (!-f $request_filename) {
      proxy_pass http://apache;
      break;
    }
  }
}

Обратите внимание, в инструкции server_name мы прописываем все возможные доменные алиасы, по которым мы хотим видеть наш проект, однако в location мы через proxy_set_header задаем вполне конкретный Host. Это нужно для того, чтобы в виртуальных хостах Apache можно было прописать только главный домен. Если вам требуется обрабатывать запросы в зависимости от домена - пропишите так:

proxy_set_header Host $http_host;

Далее настраиваем Apache. Прежде всего нужно установить mod_rails следуя официальной инструкции. Затем прописываем виртуальные хосты (размещаем в /my_project_folder/apache.conf):

# Это лучше вынести в основной конфиг
Listen 3000
NameVirtualHost *
ServerName localhost

# Виртуальный хост, на который проксируются запросы
<VirtualHost *>
  ServerName myproject.com
  DocumentRoot /my_project_folder/public
</VirtualHost>
Собственно, теперь конфиг для виртуального хоста **Nginx** нужно подключить в основной конфиг **Nginx**:
http {
  ...

  include /my_project_folder/nginx.conf;
}

А в основном конфиге Apache подключить виртуальный хост Apache:

Include /my_project_folder/apache.conf

Перезапускаем оба сервера и наслаждаемся :) Статику отдает Nginx, динамику Apache+Passenger.

Хостинг Rails на VPS Linode

Опубликовано 07.10.2008

Данной заметкой в Записках открывается новая рубрика - гостевые посты. Сегодня у меня в гостях Анатолий Михайлов, ruby-разработчик из Омска, один из членов нашей команды на предстоящем турнире Rails Rumble 2008.


Как правильно выбрать и настроить хостинг с нуля!

  1. Цели и задачи, для выполнения которых мне необходим хостинг
  2. Выбор хостинг-провайдера, а также типа сервера (shared, vds, ds)
  3. Пошаговая инструкция со скриншотами
  4. Заключение

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

Итак, начнем. Хостинг мне нужен для того, чтобы:

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

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

Выбор был очень трудоемким и сложным процессом. В итоге выбран в качестве хостера был

Ниже перечислю источники, из которых я черпал информацию, а также имена программистов, которые помогли принять решение

  • http://groups.google.com/group/ror2ru/browse_thread/thread/62e8386d117a115a?hl=en
  • Иван Немытченко: “Перепробовал разных хостеров. В итоге переехал с slicehost.com на linode.com. Чем slicehost не угодил? - админка слабее гораздо, памяти меньше за ту же цену - гемы нормально не ставятся на 256 ”
  • Ryan Bates: “Although I haven’t tried it, I’m pretty sure Linode will work great. It’s similar to SliceHost which is what I have used in the past and have been very happy with it. Either one should be able to run the app fine on a 256 MB VPS. You may need to go higher if you have a lot of traffic.”
  • и др.

Почему я не рассматриваю российские хостинг-компании?

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

Идем на сайт linode.com и проходим формальную регистрацию. Советую приобрести карту Visa Virtuon (банк-эммитент – Банк Москвы, например) для оплаты товаров и услуг в сети интернет.

Далее - минимум комментариев, скриншоты скажут сами за себя. Пошагово процесс регистрации и первоначальной настройки сервера:

Выбираем тарифный план

Вводим контактные данные

Получаем пиьсмо и подтверждаем регистрацию

Начинается самое интересное. Пройдя аутентификацию перед нами сногшибательный dashboard

Нажимаем Deploy a Linux Distribution

Перейдя в раздел Console задаем рутовый пароль для созданного образа

Раздел Network расскажет все о вашем сетевом интерфейсе

Далее несколько полезных функций

Поздравляю со счастливым приобретением!

Настройка Nginx+Thin. Все очень просто, ребята из SliceHost уже написали хорошую инструкцию :)

Безопасность - хорошая тема для разговора. Я применяю методы, описанные в данной статье


Если у вас есть какие-либо вопросы к Анатолию - вы можете задать их в комментариях или связаться с Анатолием напрямую:

E-mail / google_talk: mikhailov.anatoly(собака)gmail.com
Skype: mikhailov.anatoly

Если вам есть что рассказать читателям Записок о веб-разработке на Ruby - свяжитесь со мной по skype: rene-dekart

Git и поддержка проектов

Опубликовано 01.10.2008

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

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

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

Наиболее приемлимым инструментом для обновления кода в нашем случае стала система контроля версий. В любой системе контроля версий уже присутствуют инструменты для наложения изменений (патчей), сделанных в коде одного проекта, на код другого проекта.

Когда мы работали с Subversion, с обновлением кода была масса проблем. Постоянно возникали какие-то косяки с применением патчей. Чтобы решить эту проблему, нам приходилось держать все проекты тупо в виде веток одного репозитория. Это крайне неудобно в плане администрирования и повседневного использования. Работа по такой схеме вызывала натуральную зубную боль - настолько было неудобно.

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

Практика

Давайте по шагам рассмотрим, каким образом можно организовать обновление проектов, унаследованных от единой первоначальной версии. Допустим у нас есть исходное приложение mega-cms, размещенное в репозитории git@myhost.ru:mega-cms.git, на базе которого мы создаем проекты.

Создаем новый проект и репозиторий:

mkdir ~/mega-project
cd ~/mega-project
git init

Создан девственно чистый репозиторий. Следующий шаг - заливка кода из базового приложения:

git remote add mega-cms git@myhost.ru:mega-cms.git
git pull mega-cms master

Первой командой мы создаем ссылку на удаленный репозиторий, а второй сливаем из него все изменения начиная с самых первых. Фактически, мы теперь имеем в проекте mega-project точную копию проекта mega-cms.

Теперь давайте зальем эту копию в удаленный репозиторий, чтобы использовать ее для развертывания на сервере:

git remote add origin git@myhost.ru:mega-project.git
git push origin master

Отлично. Теперь давайте внесем какие-нибудь изменения в исходный код mega-project. Например, добавим файл с описанием проекта:

echo "This is readme file for mega-project" > README
git add .
git commit -a -m "Adding README file"

А теперь внесем изменения в проект mega-cms - добавим список задач:

cd ~/mega-cms
echo "Create tasks to automate project update" > TODO
git add .
git commit -a -m "Adding TODO list"
git push

Последняя команда выгружает изменения в проекте в удаленный репозиторий. Теперь самое интересное - нам нужно обновить код в mega-project, чтобы созданный список появился и там:

cd ~/mega-project
git pull mega-cms master

Вуаля! Git автоматически определяет, какие изменения в mega-cms были внесены после копирования и автоматически применяет их к mega-project. Если возникают какие-либо конфликты (например, мы исправляем один и тот же абзац README в mega-project и в mega-cms), то Git попросит нас ручками разрешить спорную ситуацию.

И последний шаг - заливаем примененные изменения в удаленный репозиторий:

git push

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

Rails Rumble 2008: Началась регистрация

Опубликовано 27.09.2008

Подать заявку на участие можно здесь: http://app.railsrumble.com/

Спешите, осталось меньше половины мест.

А у нас между тем определился состав команды:

  • Евгений Хлызов
  • Иван Немытченко
  • Анатолий Михайлов
  • Алексей Дмитриев, т.е. я :)

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

Времени до начала конкурса еще достаточно, поэтому пока будем продумывать техническую реализацию (есть одно концептуальное разногласие в выборе JS-фреймворка) и изучать сопутствующие инструменты, которые потребуются при работе.

RSpec Stories: пара ссылок

Опубликовано 25.09.2008

Rails Rumble 2008: Опубликованы правила

Опубликовано 24.09.2008

На сайте Rails Rumble 2008 опубликованы правила участия. Собственно, ничего особо не изменилось, за исключением использования Git в качестве репозитория для кода. Оно и к лучшему.

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

Вакансия Rails-программиста в Новосибирске

Опубликовано 22.09.2008

Попросили вот помочь:

Необходим разработчик Ruby on Rails для написания плагинов для Redmine (redmine.org). Желательно из Новосибирска, возможно удаленная работа. График полностью выстраиваете как хотите. Оплата по договоренности на задание. (Договариваемся о задании и сумме. Делаем. Оплачиваем)

Артур Нек
ICQ: 314489403
Skype: fyntom
E-mail: yoland@yandex.ru

Фактически, фриланс. И тем не менее, кому-нибудь пригодится.

P.S. Эта вакансия была размещена по доброте душевной :)

Rails Rumble 2008

Опубликовано 14.09.2008

18-19 октября состоится очередной Rails Rumble - 48-часовое состязание команд рельсовиков. В прошлом году мы с Иваном Немытченко участвовали в этом состязании. Из призов, правда, был получен уникальный опыт - вполне достойный приз. В этом году Rails Rumble будет гораздо более масштабным - организаторы заявляют о 200 местах для участников, что весьма впечатляет. Спонсоры тоже нормальные:

Список призов пока еще не озвучен.

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

Ruby on Rails на хостинге Agava VPS (через mod_rails)

Опубликовано 26.08.2008

Один из старых клиентов, для которых я разрабатывал движок, решили переехать к новому хостеру. Путем недолгих изысканий был выбран VPS хостинг от Agava, план VPS Basic. В этой заметке я вкратце опишу процесс развертывания Rails-приложения на сервере Агавы с использованием Passenger (он же mod_rails). Для развертывания мы выбрали сервер под управлением операционной системы CentOS 5.2, контрольная панель ISPManager Lite.

Установка GCC

Сразу после регистрации сервера ва потребуется установить GNU C++ Compiler. Когда я первый раз настраивал аккаунт на Агаве он был уже установлен, однако на новом аккаунте он почему-то отсутствовал. Поэтому его потребовалось установить:

yum install gcc-c++

При установке может возникнуть ошибка Error: Missing Dependency: kernel-headers >= 2.2.1 is needed by package. В этом случае необходимо обновить пакет kernel-headers. Я его ставил отсюда:

wget ftp://ftp.pbone.net/mirror/ftp.centos.org/5.2/os/i386/CentOS/kernel-headers-2.6.18-92.el5.i386.rpm
rpm -i kernel-headers-2.6.18-92.el5.i386.rpm

После этого все должно ставиться нормально.

Установка Ruby

Изначально на сервер не установлен интерпретатор Ruby, поэтому нам необходимо его установить. Чтобы сделать эксперимент максимально интересным, будем ставить не стандартный MRI, а Ruby Enterprise Edition оптимизированный для работы с Passenger.

Качаем архив с официального сайта, распаковываем и запускаем инсталлятор:

./installer

Следуем инструкциям в инсталляторе, там все прозрачно.

Установка MySQL

Следующий этап - установка gem’а для работы с MySQL. Сам сервер MySQL уже установлен, нужно только поставить дополнительные библиотеки:

yum install mysql-devel

Затем ставим сам gem:

gem install mysql

Идем дальше

Установка RMagick

Следующий этап - установка ImageMagick и RMagick. Я при установке следовал инструкциям, предложенным в этой статье, копипастить не буду :)

Установка Passenger (mod_rails)

Затем приступаем к установке Passenger. Для начала нам надо поставить дополнительные библиотеки для Apache:

yum install httpd-devel

Затем, чтобы Passenger нашел все исполняемые файлы, которые идут в составе Ruby, нам надо добавить путь до бинарников в наш .bash_profile:

PATH=$PATH:$HOME/bin:/opt/ruby-enterprise-1.8.6-20080810/bin

Вам необходимо поставить тот путь, куда вы установили Ruby.

Затем ставим gem и модуль для Apache:

gem install passenger
passenger-install-apache2-module

Подключаем модуль в нашем файле httpd.conf (у меня он находится по адресу /etc/httpd/conf/httpd.conf):

LoadModule passenger_module /opt/ruby-enterprise-1.8.6-20080810/lib/ruby/gems/1.8/gems/passenger-2.0.3/ext/apache2/mod_passenger.so
PassengerRoot /opt/ruby-enterprise-1.8.6-20080810/lib/ruby/gems/1.8/gems/passenger-2.0.3
PassengerRuby /opt/ruby-enterprise-1.8.6-20080810/bin/ruby

Через панель управления создаем нового пользователя и под его именем создаем новый WWW-домен. В httpd.conf автоматически добавится VirtualHost. Чтобы по на этом хосте заработали рельсы, нам необходимо поменять DocumentRoot таким образом, чтобы он указывал на папку public в нашем проекте:

<VirtualHost 123.45.67.89:80>
  ServerName mysite.ru
  DocumentRoot /var/www/mysite/data/www/mysite.ru/public
  SuexecUserGroup mysite mysite
  CustomLog /var/www/httpd-logs/mysite.ru.access.log combined
  ErrorLog /var/www/httpd-logs/mysite.ru.error.log
  ServerAlias www.mysite.ru mysite.ru
  ServerAdmin webmaster@mysite.ru
</VirtualHost>

Установка git

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

yum install gettext-devel expat-devel curl-devel zlib-devel openssl-devel

Поставив библиотеки, качаем и ставим сам git:

wget http://kernel.org/pub/software/scm/git/git-1.6.0.tar.gz
tar zxvf git-1.6.0.tar.gz
cd git-1.6.0
make all
make install

Как видите, все просто, с настройкой справится даже человек, не искушенный в настройке серверов (типа меня).

Single Sign-On: Расшаривание сессии

Опубликовано 20.08.2008

При разработке крупного портала или сети сайтов, объединенных общей базой пользователей, возникает вопрос о том, как реализовать авторизацию на всех сайтах через одно окно. Грубо говоря, если Вася авторизовался на сайте A, то при входе на сайт B он должен видеть сообещение “Привет, Вася!”.

В чем состоит основная сложность? В том, что встроенными средствами браузера мы не можем передавать cookie от одного сайта к другому. Если мы авторизовались на сайте A, то на сайте B мы не можем об этом узнать, не применив бубна и парочки заклинаний. Для обхода этой проблемы были придуманы такие технологии, как CAS. Возможно, это именно то, что вам нужно для решения вашей проблемы.

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

Если вы разрабатываете сайты с использованием Ruby on Rails, то знаете, что рельсы могут хранить сессии в различных типах хранилищ - напрямую в cookie, в базе данных, в файловой системе и в memcached. Во всех вариантах, исключая cookie-хранилище, рельсы передают браузеру только ключ сессии, а сами данные хранит на сервере.

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

Давайте посмотрим поближе на техническую реализацию. Предположим, у нас есть два сервиса - Фотоальбом и Блоги, на которых мы хотим авторизоваться. Единая авторизация делается через Паспорт, который отвечает за регистрацию пользователей и хранение персональной информации. Условия задачи такие: пользователь заходит на Фотоальбом, авторизуется через Паспорт, а затем переходит на Блоги, где он должен авторизоваться автоматически.

Единая сессия стартуется на точке пересечения всех сервисов - Паспорте. Сессия стартуется, создается идентификатор, а затем по требованию копируется на другие сервисы. Выглядит это следующим образом:

  1. Пользователь в первый раз входит на Фотоальбомы
  2. Проверяется наличие ключа общей сессии, в связи с ее отсутствием пользователь перенаправляется на Паспорт
  3. Паспорт стартует общую сессию, пользователь перенаправляется обратно на Фотоальбомы
  4. Проверяется наличие общей сессии, для Фотоальбомов устанавливается общий ключ сессии

Для проверки наличия ключа и его установки на всех страницах сервиса Фотоальбомов в самом верху устраницы подключен Javascript, который генерируется Паспортом для каждого конкретного запроса. Если на Паспорте не запущена сессия, то Javascript перенаправляет пользователя на страницу, на которой сессия создается. Если сессия запущена, то Javascript говорит браузеру установить общий ключ сессии. Код примерно такой:

fucntion setSharedSession(key, value){
  if(getCookie(key) != value) {
    setCookie(key, value); // Устанавливаем cookie сессии
    window.location = window.location; // Обновляем страницу
  }
}
setSharedSession('_my_session_key', '83498234abc4586def90586abc');

Код Javascript-а выполняется уже в пространстве cookie Фотоальбомов, устанавливается сессионная кука и происходит обновление страницы. При обновлении запрос отправляется уже с новой сессией. Так как Javascript мы ставим в самом верху страницы, для пользователя все этим махинации практически незаметны - перенаправление на Паспорт, переход обратно на Фотоальбом и обновление страницы происходит практически незаметно для глаз.

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

Если пользователь выходит из системы на Паспорте (кликает по ссылки “Выйти”, например), то мы удаляем текущую общую сессию. При загрузке любой страницы на любом из сервисов, подключенных к общей сессии, ключ сессии сохраняется, однако приложение не сможет получить информацию из несуществующей сессии и пользователь автоматически будет считаться неавторизованным.

Есть несколько тонких моментов, которые надо учитывать при использовании данной методики.

  1. Выбор хранилища сессий. Из тех вариантов, которые есть в рельсах, для такого подхода удобнее всего использовать memcached - он очень легко масштабируется. Менее удобен, но все же приемлем метод хранения сессий в базе данных. Все остальные я исползовать не рекомендую. Само собой, у всех сервисов должны быть абсолютно идентичные настройки хранилища.
  2. Сохранение объектов в сессию. Если вы в одном сервисе сохраните в сессию экземпляр класса, которого нет в другом сервисе, ваша психика может оказаться под угрозой :) Поэтому в сессию рекомендую сохранять только объекты базовых типов - строки, массивы, хэши.
  3. Производительность. Так как волшебный Javascript подключается на всех страницах сервисов, количество его загрузок будет равно количеству загрузок всех остальных страниц. Поэтому такой скрипт я бы рекомендовал реализовать на чем-то более быстром, нежели рельсы или ruby как таковой. Как вариант можно использовать Perl или PHP, благо серверный функционал довольно прост - в зависимости от наличия куки возвращать редирект или инструкцию на установку куки.
  4. Безопасность. Так как на сайте явно присутствует готовая функция для установки cookie, вам нужно быть предельно внимательными к вопросам защиты от XSS-атак. Фактически, любая дырка, в которую можно вставить инструкцию <script>, является для вас смертельно опасной. Чтобы устранить эту опасность можно использовать либо специальные плагины (например, xss_terminate), либо обработчики шаблонов со встроенной защитой (HAML, erubis).
  5. Сфера применимости. Такая методика может быть применена при довольно жестких условиях, описанных в начале статьи. Поэтому стоит заранее задуматься, какова вероятность того, что ваше приложение выйдет за обозначенные рамки, и насколько быстро это произойдет. Кроме того, стоит задуматься, будет ли участвовать в процессе работы с сессиями стороннее ПО - десктопные программы, приложения на других языках, что-то еще. Если да, то я рекомендую смотреть в сторону более универсальных решений.

Ваше мнение?

Rails 2.2 will be thread-safe

Опубликовано 18.08.2008

Josh Peek officially joins the Rails core, пишет DHH в официальном блоге Rails. А это значит, что Rails 2.2 будут thread-safe. Что ж, это отличные новости.

Тем кто в танке, читать почему это важно

Кофейные ссылки #8

Опубликовано 13.08.2008

Новые поступления:

Порылся тут в архивах и вот еще что откопал:

Кофейные ссылки #7

Опубликовано 12.08.2008

Что-то я с этой работой совсем замотался и позабыл про Записки. Пора возвращать все на круги своя. Итак, сегодняшние кофейные ссылки:

Самое интересное из того, что накопилось за последнее время.

Видео с RuPyRu 2008

Опубликовано 05.08.2008

Наконец-то в прокат вышло видео с конференции RuPyRu 2008, в которой я принимал непосредственное участие. Спешите видеть :)

Мой доклад можно посмотреть здесь. Слайды к докладу - здесь. В ответах на вопросы информации оказалось ничуть не меньше, чем в самом докладе.

От себя могу порекомендовать доклады Ивана Немытченко (очень практично), Олега Дашевского (хорошо быть в курсе новых тенденций) и Александра Зубарева (о практике разработок в крупных компаниях).

Порталы: От приложений к сервисам

Опубликовано 22.07.2008

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

В этой заметке я расскажу, какие ответы я нашел на заданные вопросы. В связи с тем, что мы ведем разработку на Ruby on Rails, многие методики ориентируются в первую очередь на эту платформу. Однако, я уверен, что на других платформах тоже возможно решить задачу тем же путем. Итак, давайте смотреть:

Вопрос №1: Из каких частей состоит проект?

Так как я занимаюсь разработкой системы управления, не привязанной к какому-то конкретному проекту, то в моем случае разумно делить проект на функциональные составляющие. Четко можно выделить следующие ключевые модули:

  • Паспорт - управление регистрацией и авторизацией пользователей, редактирование персональной информации, используемой во всех частях системы, просмотр профилей, управление правами доступа;
  • Публикации - размещение текстовых материалов, видео- и графических материалов администраторами и штатными журналистами сайта
  • Комментарии - обсуждение тех или иных материалов, размещенных в различных модулях системы
  • Рейтинги - выставление оценок материалам, размещенным в различных модулях
  • Изображения - управление загрузкой графических материалов и их привязка к материалам из других модулей, упорядочивание и редактирование изображений

Я не буду утомлять вас перечислением всех модулей, ключевых достаточно чтобы уловить суть. Если вам интересен полный список (и интерес не праздный) - милости просим в контакты. :)

Вопрос №2: Каким образом разделены части проекта?

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

Варианта в конечном итоге образовалось два - делать сервисы в виде “приложения в приложении” (с помощью Engines или Desert) либо каждый сервис выносить в виде отдельного приложения. Первый вариант меня не устроил тем, что и Engines, и Desert не являются официально поддерживаемым решением для разделения приложения на части (как, например, с этим обстоит в Django). На практике это означает что с выходом изменений в рельсах придется какое-то время ждать пока разработчики плагина адаптируют его к внесенным изменениям. Например, для Engines время ожидания доходило до нескольких недель после официального выхода новой версии.

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

Вопрос №3: Какой функционал является общим и как он используется?

Наверное, самый сложный вопрос. Путем проб и ошибок я пришел к выводу, что общим для всех сервисов является следующее:

  • Конфигурация - позволяет сервисам знать друг о друге (адреса, порты, пароли), хранение общих настроек
  • Методика авторизации - схема получения информации о текущем авторизованном пользователе, ограничение доступа к закрытым разделам
  • Общие классы - например, ресурсы, которые используются сервисами для работы друг с другом
  • Расширения - модули acts_as_*, подключаемые в модели и контроллеры
  • Хелперы - общий набор, используемый во всех сервисах; например, хелперы для вывода информации о пользователе, блока комментариев и прочие.

В первоначальном варианте общий функционал был оформлен в виде плагина, который подключался как submodule встроенными средствами git. Однако, такая практика дает побочный эффект: при внесении изменений в плагин приходится руками обновлять его во всех сервисах. Это нудно и периодически возникает расхождение версий плагина и сервиса. с которым идет работа.

Чтобы избавиться от этого бестолкового геморроя, я решил оформить функционал в формате gem’а, который подключается на ранних стадиях загрузки rails-приложения (до вызова boot.rb). Сразу посли подключения гема становится доступна конфигурация - набор констант, разбитых по модулям. Эти константы используются при инициализации приложения, например, при задании ключа для хранения сессий и прочих рельсовых настроек. А после того, как приложение инициализированно, вызывается метод, который подключает хелперы, методы для контроллеров и моделей.

Gem хорош тем, что он ставится на сервер и становится доступен всем сервисам, работающим на этом сервере. Плюс появляется возможность задавать требования к версии.

Задайте себе эти вопросы, и очень может быть, что вы найдете способ более правильно организовать ваши приложения.