<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">
  <id>http://railorz.ru/</id>
  <title>Railorz - Блог Алексея Дмитриева</title>
  <updated>2012-02-13T11:20:00Z</updated>
  <link rel="alternate" href="http://railorz.ru/" />
  
  <author>
    <name>Алексей Дмитриев</name>
    <uri>http://railorz.ru</uri>
  </author>
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/HtmlBlog" /><feedburner:info uri="htmlblog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
    <id>tag:railorz.ru,2012-02-13:/articles/157-customization/</id>
    <title type="html">Кастомизация кода под клиентов</title>
    <published>2012-02-13T11:20:00Z</published>
    <updated>2012-02-13T11:20:00Z</updated>
    <link rel="alternate" href="http://feedproxy.google.com/~r/HtmlBlog/~3/WfDoYnzlnCs/" />
    <content type="html">&lt;p&gt;Если вы продаете свой программный продукт, то рано или поздно один из клиентов попросит вас приделать к вашему продукту какую-нибудь эдакую штучку в вашем продукте, которая лично ему до зарезу нужна. И, весьма вероятно, вы согласитесь эту штучку для него сделать. А вполне возможно, что ваш продукт изначально будет ориентирован именно на таких клиентов, которые придут к вам благодаря базовому функционалу вашего продукта, но существенную часть прибыли принесут вам за счет кастомизации, то есть доработок вашего продукта под свои нужды.&lt;/p&gt;
&lt;p&gt;&lt;!-- More --&gt;&lt;/p&gt;
&lt;p&gt;Оставим пока в стороне вопросы договоров, платежей и прочие организационные тонкости и подумаем о том, каким образом организовать работу с кастомизированным кодом конкретных клиентов. В большинстве случаев клиент ведь захочет не только получить доработанную под него версию продукта, но и поддерживать ее в актуальном состоянии, получая исправления и дополнения, сделанные в вашем исходном продукте. Мало того, большую часть разработок, сделанных для конкретных клиентов, по различным причинам нельзя будет включить в основной код проекта.&lt;/p&gt;
&lt;p&gt;Есть несколько возможных подходов к организации кода при работе с клиентами, из которых я в своей практике использую два &amp;#8211; разбивка функционала по модулям и клонирование кода.&lt;/p&gt;
&lt;h2&gt;Модульная структура проекта&lt;/h2&gt;
&lt;p&gt;Если у вас есть хотя бы мало-мальский опыт программирования, то вам не надо объяснять что такое разбивка проекта на модули. Часть функционала системы выносится за пределы основного проекта &amp;#8211; вот собственно и все. В языке Ruby такими модулями могут быть gem-ы, в &lt;span class="caps"&gt;PHP&lt;/span&gt; это &lt;span class="caps"&gt;PEAR&lt;/span&gt;-пакеты, ну и так далее. Суть одна &amp;#8211; кусок функционала системы, который опционально устанавливается и настраивается для клиента.&lt;/p&gt;
&lt;p&gt;О процессе установки и настройки модуля стоит отдельно сказать, что вариантов может быть масса &amp;#8211; от компиляции отдельной версии с нужным модулем, добавленным руками, до разработки &lt;span class="caps"&gt;API&lt;/span&gt; для интеграции сторонних модулей.&lt;/p&gt;
&lt;p&gt;Первый вариант приемлем тогда, когда установка модуля для конкретного клиента случается редко, а количество доступных модулей невелико. Собственно, именно таким образом мы интегрируем модули для наших клиентов &amp;#8211; устанавливаем модуль используя стандартные средства языка (в среде ruby это ruby gems), а затем подключаем его, вставляя соответствующие инструкции в нужных местах в коде.&lt;/p&gt;
&lt;p&gt;Если же количество подключаемых модулей велико, модули разнообразны или даже пишутся сторонними разработчиками, то имеет смысл разработать &lt;span class="caps"&gt;API&lt;/span&gt; для подключения модулей. Эта задача специфична для каждой конкретной системы, поэтому могу дать лишь одну конкретную рекомендацию: исходите из конкретных потребностей. Не загадывайте наперед, не выдумывайте систему на пустом месте. Напишите десяток плагинов к вашей системе, используя топорный подход, описанный в предыдущем параграфе &amp;#8211; и &lt;span class="caps"&gt;API&lt;/span&gt; вырисуется сам собой.&lt;/p&gt;
&lt;h2&gt;Клонирование кода&lt;/h2&gt;
&lt;p&gt;Несмотря на кажущуюся простоту модульного подхода, он решает далеко не все задачи. Давайте рассмотрим пример: мы разработали новый веб-браузер для корпоративных клиентов и теперь продвигаем его на рынок. К нам приходит клиент, который хочет, чтобы в нашем браузере на всех страницах всех сайтов отображалось лого компании, плюс еще 5-10 изменений по мелочи. Клиент готов за это заплатить существенные деньги.&lt;/p&gt;
&lt;p&gt;Мы можем разработать набор модулей для решения задачи клиента, но по сути сделать поправки напрямую в коде было бы в десять раз быстрее, чем писать модули и интегрировать их. Делать поправки в основном коде, который мы поставляем всем остальным клиентам, нельзя по каким-либо причинам. В этом случае вполне логично сделать копию кода, однако исправленный код придется потом поддерживать, накатывать всевозможные обновления из последующих версий. В зависимости от того, какие инструменты мы используем в работе, сложность задачи с копированием кода и дальнейшей поддержкой может варьироваться от &amp;#8220;это невозможно&amp;#8221; до &amp;#8220;это элементарно&amp;#8221;.&lt;/p&gt;
&lt;p&gt;На мой взгляд, наиболее подходящим инструментом для решения этой задачи является система контроля версий &lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt;. Стандартный функционал Git позволяет создавать кастомные версии кода и обновлять их практически без усилий &amp;#8211; за счет копирования изменений из одного репозитория в другой, используя стандартные команды. Давайте посмотрим, как можно организовать работу с кодом для кастомизации кода под задачи вышеозначенного клиента.&lt;/p&gt;
&lt;p&gt;Допустим, наш основной код хранится в репозитории git@myserver:myrepo.git и мы хотим создать копию кода для клиента, которая будет лежать в отдельном репозитории git@myselver:client_1.git. Для начала, мы создаем локальную копию кода для клиента:&lt;/p&gt;
      &lt;div class="code"&gt;
        &lt;strong class="header"&gt;Bash Code&lt;/strong&gt;
        &lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight" style="background: #f8f8f8"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #B8860B"&gt;$ &lt;/span&gt;git clone git@myserver:myrepo client_1
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
      &lt;/div&gt;
    &lt;p&gt;Отлично, копия есть. Однако, эта копия привязана к нашему исходному репозиторию:&lt;/p&gt;
      &lt;div class="code"&gt;
        &lt;strong class="header"&gt;Bash Code&lt;/strong&gt;
        &lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1
2
3
4
5
6
7&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight" style="background: #f8f8f8"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #B8860B"&gt;$ &lt;/span&gt;&lt;span style="color: #AA22FF"&gt;cd &lt;/span&gt;client_1
&lt;span style="color: #B8860B"&gt;$ &lt;/span&gt;git remote show origin

  * remote origin
  Fetch URL: git@myserver:myrepo.git
  Push  URL: git@myserver:myrepo.git
  ...
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
      &lt;/div&gt;
    &lt;p&gt;Нам нужно отвязать нашу копию от исходного репозитория и привязать к новому:&lt;/p&gt;
      &lt;div class="code"&gt;
        &lt;strong class="header"&gt;Bash Code&lt;/strong&gt;
        &lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1
2
3&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight" style="background: #f8f8f8"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #B8860B"&gt;$ &lt;/span&gt;git remote rm origin
&lt;span style="color: #B8860B"&gt;$ &lt;/span&gt;git remote add origin git@myselver:client_1.git
&lt;span style="color: #B8860B"&gt;$ &lt;/span&gt;git push -u origin master
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
      &lt;/div&gt;
    &lt;p&gt;Теперь в новом репозитории у нас содержится полная копия исходного репозитория. В новом репозитории мы можем делать все необходимые нам изменения для клиента. Допустим, изменения сделаны, клиент счастлив, заплатил деньги и получил свою кастомизированную копию программы. Но через неделю в вашем продукте обнаружена уязвимость, которая затрагивает все версии браузера, включая кастомную версию для клиента. Вы сделали изменения в основном репозитории и теперь их нужно каким-то образом импортировать в копию, созданную для клиента. Давайте сделаем это:&lt;/p&gt;
      &lt;div class="code"&gt;
        &lt;strong class="header"&gt;Bash Code&lt;/strong&gt;
        &lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1
2&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight" style="background: #f8f8f8"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #B8860B"&gt;$ &lt;/span&gt;&lt;span style="color: #AA22FF"&gt;cd &lt;/span&gt;client_1
&lt;span style="color: #B8860B"&gt;$ &lt;/span&gt;git pull git@myserver:myrepo.git master
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
      &lt;/div&gt;
    &lt;p&gt;Так как большинство коммитов в клиентском репозитории идентичны коммитам в исходном репозитории, то Git импортирует исключительно те коммиты, которых нет в клиентском репозитории. Все конфликты между коммитами (если таковые возникнут) можно будет разрешить используя стандартные механизмы Git. Останется только залить изменения в репозиторий:&lt;/p&gt;
      &lt;div class="code"&gt;
        &lt;strong class="header"&gt;Bash Code&lt;/strong&gt;
        &lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight" style="background: #f8f8f8"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #B8860B"&gt;$ &lt;/span&gt;git push
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
      &lt;/div&gt;
    &lt;p&gt;Для упрощения импорта коммитов можно прописать основной репозиторий в remote-ссылки клиентского репозитория:&lt;/p&gt;
      &lt;div class="code"&gt;
        &lt;strong class="header"&gt;Bash Code&lt;/strong&gt;
        &lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight" style="background: #f8f8f8"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #B8860B"&gt;$ &lt;/span&gt;git remote add main_repo git@myserver:myrepo.git
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
      &lt;/div&gt;
    &lt;p&gt;И использовать ссылку вместо адреса для импорта:&lt;/p&gt;
      &lt;div class="code"&gt;
        &lt;strong class="header"&gt;Bash Code&lt;/strong&gt;
        &lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight" style="background: #f8f8f8"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #B8860B"&gt;$ &lt;/span&gt;git pull main_repo master
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
      &lt;/div&gt;
    &lt;p&gt;Кроме того, импортировать можно и из различных веток или тегов:&lt;/p&gt;
      &lt;div class="code"&gt;
        &lt;strong class="header"&gt;Bash Code&lt;/strong&gt;
        &lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1
2&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight" style="background: #f8f8f8"&gt;&lt;pre style="line-height: 125%"&gt;&lt;span style="color: #B8860B"&gt;$ &lt;/span&gt;git pull main_repo tags/stable
&lt;span style="color: #B8860B"&gt;$ &lt;/span&gt;git pull main_repo experimental_feature
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
      &lt;/div&gt;
    &lt;p&gt;Вполне вероятно, что этот же подход можно использовать и с другими распределенными системами контроля версий, такими как Mercurial. Если я не прав &amp;#8211; поправьте меня.&lt;/p&gt;
&lt;h2&gt;Итого&lt;/h2&gt;
&lt;p&gt;Вышеописанные подходы к кастомизации основного кода и поддержке клиентских копий мы используем в нашей компании уже несколько лет подряд и еще не было ни единого случая, когда они бы нас подвели. Вкратце, все вышесказанное можно резюмировать в двух советах:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Используйте модули тогда, когда функционал повторяется от клиента к клиенту, но его нельзя включить в основной код&lt;/li&gt;
	&lt;li&gt;Используйте клонирование кода тогда, когда изменения разумнее сделать напрямую в коде проекта, нежели писать для этого отдельный модуль&lt;/li&gt;
&lt;/ul&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=WfDoYnzlnCs:NRRFLyA8IUE:F5rjUU7H7Ys"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?i=WfDoYnzlnCs:NRRFLyA8IUE:F5rjUU7H7Ys" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=WfDoYnzlnCs:NRRFLyA8IUE:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=WfDoYnzlnCs:NRRFLyA8IUE:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/HtmlBlog/~4/WfDoYnzlnCs" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://railorz.ru/articles/157-customization/</feedburner:origLink></entry>
  <entry>
    <id>tag:railorz.ru,2012-01-13:/articles/156-evented-game-server/</id>
    <title type="html">Событийный игровой сервер</title>
    <published>2012-01-13T09:00:00Z</published>
    <updated>2012-01-13T09:00:00Z</updated>
    <link rel="alternate" href="http://feedproxy.google.com/~r/HtmlBlog/~3/P_Z8acyotrw/" />
    <content type="html">&lt;p&gt;Если вы читали некоторые из моих предыдущих постов, то наверняка знаете, что я уже довольно долгое время занимаюсь разработкой игр для соцсети Facebook. Сегодня я немного расскажу о подходе, который мы используем при разработке игрового сервера для нашего нового проекта.&lt;/p&gt;
&lt;p&gt;&lt;!-- More --&gt;&lt;/p&gt;
&lt;p&gt;Наш основной проект на момент написания этой заметки &amp;#8211; игра &lt;a href="http://apps.facebook.com/witchcraftgame/"&gt;Witchcraft&lt;/a&gt; &amp;#8211; написан на &lt;a href="http://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt;. Начать разработку онлайн-игры именно на Rails не было каким-то сознательным решением, скорее так исторически сложилось, что мне очень нравилось программировать на Rails и я готов был все что угодно делать с помощью этой технологии, не взирая на уместность. У текущей реализации проекта есть один несомненный плюс &amp;#8211; она работает. Но есть и ряд существенных минусов, от которых я сильно хочу избавиться и которые мы стараемся исключить при разработке нового продукта.&lt;/p&gt;
&lt;h3&gt;Минусы текущей реализации&lt;/h3&gt;
&lt;ul&gt;
	&lt;li&gt;Игровая механика жестко ограничена соглашениями Rails. Каждое действие игрока может генерировать только один результат, только один ответ от сервера. Например, при клике по кнопке миссии можно только вернуть результат выполнения миссии, но не событие о получении нового уровня или об открытии новой миссии. Конечно, все можно сделать, однако код для такой логике абсолютно противоречит структуре традиционного rails-приложения.&lt;/li&gt;
	&lt;li&gt;Возможности общения сервера с клиентом ограничены ответами на &lt;span class="caps"&gt;HTTP&lt;/span&gt;-запросы. Любая попытка реализовать отправку сообщения с сервера на клиент вне контекста конкретного запроса выглядит совершенно ортогонально концепции Rails.&lt;/li&gt;
	&lt;li&gt;Использование базы данных для хранения игрового контента и данных игрока жесточайше и главное совершенно бесполезно растрачивает серверные ресурсы на загрузку/сохранение данных при выполнении мелких действий, из которых собственно и строится интерактивный игровой процесс.&lt;/li&gt;
	&lt;li&gt;Завязанность визуализации игрового процесса на сервер. В Witchcraft подавляющая часть всего визуального представления игры генерируется сервером. Все страницы, результаты действий, сообщения пользователю, диалоги &amp;#8211; все это в виде &lt;span class="caps"&gt;HTML&lt;/span&gt; поступает с сервера. На клиенте происходит самый минимум &amp;#8211; навешивание событий на кнопки и ссылки, которые опять же вызывают сервер для генерации результатов событий. В отличие от бизнес-приложений, где один клиент генерирует 10-15 запросов за сессию, игрок выполняет 50-1000 действий за игровую сессию, потребляя ощутимое количество ресурсов.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Эти проблемы натолкнули меня на мысль разработать игровой сервер, более соответствующий природе игр, где весь процесс строится на базе событий.&lt;/p&gt;
&lt;h3&gt;Событийный игровой сервер&lt;/h3&gt;
&lt;p&gt;События могут быть сгенерированы игроком &amp;#8211; клики мышкой, нажатия на кнопки &amp;#8211; а могут и поступать от сервера &amp;#8211; построилось здание, на игрока напали, пришло сообщение. И те, и другие события могут происходить вне зависимости друг от друга, между ними нет взаимосвязи, то есть нельзя точно сказать, например, что в результате данного конкретного нажатия на кнопку построилось здание. События должны поступать от игрока на сервер и от сервера к игроку без какой-либо привязки к запросу.&lt;/p&gt;
&lt;p&gt;В процессе работы над прототипом игрового сервера у меня выкристаллизовалась система, состоящая из клиента, транспорта, очереди событий и игрового демона.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Игровой клиент&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Решив не отходить сильно далеко от Rails, я решил разрабатывать игровой клиент на JavaScript с использованием фреймворка &lt;a href="http://backbonejs.org/"&gt;Backbone.js&lt;/a&gt; &amp;#8211; это оказалось наболее простым решением. В качестве шаблонизатора используется &lt;a href="http://handlebarsjs.com"&gt;Handlebars.js&lt;/a&gt; &amp;#8211; шаблоны получаются весьма простыми и с минимумом логики. Клиент собирается и отдается в браузер Rails-приложением, которое помимо отдачи клиента будет отвечать за такие полезные в хозяйстве вещи как регистрация пользователей, настройки и прием платежей.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Транспорт&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Для доставки событий от клиента на сервер и обратно я решил испробовать WebSocket &amp;#8211; эта технология оказалась наиболее подходящей для асинхронного обмена данными. На стороне сервера я поставил Rack middleware прямо на Rails-приложение, обработку websocket-ов реализовал с помощью гема &lt;a href="http://rubygems.org/gems/faye-websocket"&gt;faye-websocket&lt;/a&gt;. Клиент подключается к серверу и отправляет сообщения о событиях, сервер сваливает полученные сообщения в очередь событий для обработки игровым демоном, выгребает из очереди события, сгенерированные игровым демоном для данного игрока, и отправляет их на клиент. Обмен данными происходит асинхронно.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Очередь событий&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Хранение очередей входящих и исходящих игровых событий я пока решил реализовать через &lt;a href="http://redis.io/"&gt;Redis&lt;/a&gt;, так как с ним я более-менее знаком и в нем хорошо реализована возможность создания списков объектов. События пишутся в ключи, соответствующие игровой сессии &amp;#8211; &amp;#8216;game_123_incoming&amp;#8217; для всех входящих от всех игроков, &amp;#8216;game_123_ougoing_player_*&amp;#8217; &amp;#8211; для исходящих, адресованных конкретным игрокам.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Игровой демон&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Непосредственная обработка игрового процесса происходит в отдельно запущенном демоне на базе &lt;a href="http://rubyeventmachine.com/"&gt;EventMachine&lt;/a&gt;. Демон загружает в себя игровые сессии по мере их добавления в хранилище, для хранения данных сессий опять же пока что использую Redis. Затем по таймеру (100 раз в секунду) демон опрашивает очередь на предмет наличия новых событий от игроков, а так же запускает так называемый game loop &amp;#8211; обработчик игрового процесса. Этот обработчик производит действия в соответствии с командами игроков и отсылает игрокам игровые события, произошедшие в результате их действий или в связи с внутренними алгоритмами. Все изменения параметров игровой сессии происходят в памяти игрового демона и периодически (раз в 10 секунд) записываются в хранилище.&lt;/p&gt;
&lt;h3&gt;Предолагаемые проблемы&lt;/h3&gt;
&lt;p&gt;На данный момент это экспериментальная схема, но она весьма неплохо работает, по крайней мере на девелоперской машине :) Однако я заранее предвижу несколько проблем, которые могут возникнуть:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;JS-клиент может достаточно сильно тормозить при активном рендере. Это частично можно скомпенсировать пре-рендером части шаблонов, а так же переходом на какие-либо более быстрые средства рендеринга &amp;#8211; Flash, &lt;a href="http://unity3d.com/"&gt;Unity&lt;/a&gt;, нативный клиент.&lt;/li&gt;
	&lt;li&gt;Websocket &amp;#8211; новая технология, поддерживается не всеми браузерами. Эту проблему можно обойти, используя Flash-сокеты, уже даже есть готовые решения. На крайний случай доставку событий от клиента можно реализовать обычными &lt;span class="caps"&gt;HTTP&lt;/span&gt;-запросами, а от сервера клиенту &amp;#8211; через &lt;a href="http://ru.wikipedia.org/wiki/Comet_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)"&gt;long-polling&lt;/a&gt;.&lt;/li&gt;
	&lt;li&gt;Redis &amp;#8211; не самый подходящий инструмент для хранения очередей. При необходимости его можно заменить на что-то более подходящее для организации очередей. Например, &lt;a href=""&gt;RabbitMQ&lt;/a&gt;.&lt;/li&gt;
	&lt;li&gt;Игровой демон на ruby может оказаться достаточно медленным при большом количестве игровых сессий. Это решается распределением сессий между несколькими демонами и/или переписыванием игровой логики на более быстрые языки &amp;#8211; Java, Erlang, C++.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Преимущества&lt;/h3&gt;
&lt;p&gt;У данной схемы помимо недостатков есть и определенные преимущества:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Слабая связность компонентов позволяет менять технологии и инструменты одного компонента без особого влияния на все остальные. При желании JS-клиента можно отдавать не рельсами, а голым nginx-ом, websocket-ы обрабатывать сервером на C++, игровой демон переписать на Java, а очередь хранить RabbitMQ.&lt;/li&gt;
	&lt;li&gt;Относительная универсальность системы позволяет разрабатывать игры совершенно разных жанров &amp;#8211; стратегии, rpg, тетрисы и кроссворды &amp;#8211; используя одну и ту же инфраструктуру. Все отличия заключаются в двух компонентах &amp;#8211; логика игровой сессии и клиент. Все остальное остается неизменным.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;В целом, эксперимент сам по себе оказался весьма интересен и позволил немного отстраниться от привычной схемы разработки приложений.&lt;/p&gt;









&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=P_Z8acyotrw:0EieMWrrUlI:F5rjUU7H7Ys"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?i=P_Z8acyotrw:0EieMWrrUlI:F5rjUU7H7Ys" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=P_Z8acyotrw:0EieMWrrUlI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=P_Z8acyotrw:0EieMWrrUlI:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/HtmlBlog/~4/P_Z8acyotrw" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://railorz.ru/articles/156-evented-game-server/</feedburner:origLink></entry>
  <entry>
    <id>tag:railorz.ru,2011-09-12:/articles/155-monetization/</id>
    <title type="html">Идея есть. Как на ней заработать?</title>
    <published>2011-09-12T08:00:00Z</published>
    <updated>2011-09-12T08:00:00Z</updated>
    <link rel="alternate" href="http://feedproxy.google.com/~r/HtmlBlog/~3/0EhSBsvmTrI/" />
    <content type="html">&lt;p&gt;Вопрос, вынесенный в заголовок, наверное самый интересный вопрос бизнеса как такового. А ответ на него &amp;#8211; самый важный для того, чтобы простое увлечение превратилось в источник прибыли.&lt;/p&gt;
&lt;p&gt;Вообще, практически каждую &lt;a href="/articles/153-getting-an-idea/"&gt;идею&lt;/a&gt; можно монетизировать несколькими способами. Мы сейчас рассматриваем разработку ПО, поэтому давайте подумаем над тем, каким образом можно монетизировать разработанный программный продукт.&lt;/p&gt;
&lt;p&gt;&lt;!-- More --&gt;&lt;/p&gt;
&lt;h3&gt;Продажа самого продукта&lt;/h3&gt;
&lt;p&gt;Самый простой и логичный подход &amp;#8211; вы делаете продукт, за использование которого люди платят вам деньги. Таким способом зарабатывают все без исключения разработчики софта, которые выставляют ценник на свой продукт.&lt;/p&gt;
&lt;h3&gt;Продажа расширений и дополнительных модулей&lt;/h3&gt;
&lt;p&gt;Не все из интересных и полезных возможностей стоит сразу же включать в стандартную комплектацию. Вполне может быть так, что новая возможность будет востребована лишь у небольшой части пользователей, но зато именно им она очень даже нужна. В этом случае имеет смысл такую возможность продавать отдельно. Например, в наш игровой движок не включены модули почтовой рассылки и приема платежей через Paypal &amp;#8211; их мы предлагаем отдельно.&lt;/p&gt;
&lt;h3&gt;Платная поддержка&lt;/h3&gt;
&lt;p&gt;Для сложных продуктов, особенно в корпоративном секторе, совершенно нормально брать с клиента дополнительные деньги за оказание технической поддержки и обновления продукта. Конечно, бесплатно помогать людям освоиться с вашим продуктом &amp;#8211; это здорово, но при росте количества клиентов затраты на поддержку вырастают. Чтобы как-то сократить эти издержки на разъяснение типичных непонятных моментов можно писать обучающие статьи и делать скринкасты, однако некоторым клиентам бывает необходимо иметь возможность при возникновении сложностей связаться с компетентным специалистом и быстро решить проблему. Именно такой сервис и необходимо предоставлять за деньги.&lt;/p&gt;
&lt;h3&gt;Доработки и дополнительные услуги&lt;/h3&gt;
&lt;p&gt;В некоторых случаях клиенту требуется какая-нибудь возможность, которую можно &amp;#8220;прикрутить&amp;#8221; к вашему продукту, и которую он хочет сделать эксклюзивно для себя. Из таких ситуаций тоже можно извлекать существенную долю прибыли, оказывая клиенту по сути услугу аутсорсинга разработки. Вы знаете свой продукт лучше всех, поэтому при прочих равных именно вы сможете наиболее качественно интегрировать необходимую возможность в ваш продукт. Мы, например, практически всем клиентам предоставляем услуги внесения дополнений, а так же разработку и интеграцию дизайна.&lt;/p&gt;
&lt;h3&gt;Реклама в продукте&lt;/h3&gt;
&lt;p&gt;Этот способ монетизации вряд ли можно назвать основным для ПО, разрабатываемого на продажу, однако его все равно не стоит сбрасывать со счетов. Он применим лишь в том случае, когда вашим продуктом пользуется реально ощутимое количество пользователей (тысячи), которым имеет смысл показывать какую-то рекламу. Реклама чаще всего используется как средство монетизации продуктов массового потребления, распространяемых бесплатно &amp;#8211; браузерах, мессенджерах, медиа-проигрывателях, играх и т.д. Можно рекламировать сторонние продукты и услуги, но наиболее выигрышный вариант &amp;#8211; реклама собственных продуктов и сервисов.&lt;/p&gt;
&lt;h3&gt;Пожертвования и поддержка спонсоров&lt;/h3&gt;
&lt;p&gt;Пожалуй, этот способ монетизации я бы назвал наименее перспективным в плане финансов. На мой взгляд, люди с гораздо большей вероятностью заплатят $100 за платный продукт, чем пожертвуют $100 на бесплатный. Единственный продукт, собиравший неплохие суммы пожертвований, который я знаю &amp;#8211; это &lt;a href="http://modrails.com/"&gt;Phusion Passenger&lt;/a&gt;. Но я абсолютно уверен, что и для команды Phusion этот способ монетизации их продукта далеко не самый приоритетный :)&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=0EhSBsvmTrI:tWmEBsXmYUU:F5rjUU7H7Ys"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?i=0EhSBsvmTrI:tWmEBsXmYUU:F5rjUU7H7Ys" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=0EhSBsvmTrI:tWmEBsXmYUU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=0EhSBsvmTrI:tWmEBsXmYUU:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/HtmlBlog/~4/0EhSBsvmTrI" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://railorz.ru/articles/155-monetization/</feedburner:origLink></entry>
  <entry>
    <id>tag:railorz.ru,2011-09-08:/articles/154-ruby-job/</id>
    <title type="html">Ищем Ruby-программиста в Челябинске</title>
    <published>2011-09-08T14:00:00Z</published>
    <updated>2011-09-08T14:00:00Z</updated>
    <link rel="alternate" href="http://feedproxy.google.com/~r/HtmlBlog/~3/kh9Nh3xWiPI/" />
    <content type="html">&lt;p&gt;Мы ищем новых Ruby-программистов в команду. Подробности вакансии можно посмотреть на сайте &lt;a href="http://rubyjobs.ru/vacancies/1582"&gt;RubyJobs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Если у вас есть знакомые веб-программисты в Челябинске &amp;#8211; не постесняйтесь скинуть им ссылку :)&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=kh9Nh3xWiPI:vXNCA7S8yMU:F5rjUU7H7Ys"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?i=kh9Nh3xWiPI:vXNCA7S8yMU:F5rjUU7H7Ys" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=kh9Nh3xWiPI:vXNCA7S8yMU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=kh9Nh3xWiPI:vXNCA7S8yMU:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/HtmlBlog/~4/kh9Nh3xWiPI" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://railorz.ru/articles/154-ruby-job/</feedburner:origLink></entry>
  <entry>
    <id>tag:railorz.ru,2011-08-29:/articles/153-getting-an-idea/</id>
    <title type="html">В поисках идеи</title>
    <published>2011-08-29T08:00:00Z</published>
    <updated>2011-08-29T08:00:00Z</updated>
    <link rel="alternate" href="http://feedproxy.google.com/~r/HtmlBlog/~3/b-sa9OFHKxY/" />
    <content type="html">&lt;p&gt;Если хорошенько подумать, то проблемы поиска идей просто не существует. Различные идеи нас окружают постоянно, порхают в голове как бабочки, не дают спокойно спать. Во всяком случае, со мной такое происходит постоянно. Так что проблема, на самом деле, не в поиске идей, а в их кристаллизации, систематизации и отборе.&lt;/p&gt;
&lt;p&gt;&lt;!-- More --&gt;&lt;/p&gt;
&lt;h3&gt;Кристаллизация идей&lt;/h3&gt;
&lt;p&gt;Современный образ жизни навязывает нам постоянный поток новой разнообразной информации, а профессия разработчика (особенно веб-разработчика) делает перенасыщение информацией нормой жизни. В таком диком мозговом шуме и толкотне любые зачатки интересных идей не могут как следует оформиться и глохнут под гнетом все новых и новых слоев информации.&lt;/p&gt;
&lt;p&gt;Вот например, есть у нас такой продукт &amp;#8211; игра &lt;a href="http://apps.facebook.com/witchcraftgame/?reference=blog"&gt;Witchcraft&lt;/a&gt;. Я уже с месяц примерно ходил и думал, чего бы такое туда прикрутить чтобы все стало лучше и интереснее &amp;#8211; и ничего толкового не приходило в голову. Вроде кучу всего надо сделать, но что именно &amp;#8211; непонятно. Однако, вчера по дороге из офиса домой я решил изменить своей привычке читать книжку с телефона, не стал слушать очередной подкаст, а вместо этого взял ручку и блокнот &amp;#8211; и выдал список поправок и доработок разного калибра длиной аж в 5 страниц.&lt;/p&gt;
&lt;p&gt;Чтобы вытащить на свет мало-мальски стоящую идею, нужно на время приостановить поток поступающей информации и дать зернам идеи прорасти. Я это делаю так:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Отключаю все входящие потоки информации &amp;#8211; отрываюсь от компьютера, вырубаю телефон, ухожу куда-нибудь, где я могу побыть один (тихая комната, кафе, парк) или просто прошу близких меня какое-то время не беспокоить.&lt;/li&gt;
	&lt;li&gt;Беру ручку и чистый блокнот. Я не единожды пробовал записывать мысли сразу в компьютер, но чаще всего это заканчивалось тем, что паузу в размышлениях сама собой заполнялась какой-нибудь активностью (типа чтения почты или твиттера) и все идеи куда-то выветривались. Записывать в разного рода гаджеты мне тоже не очень удобно, потому как сам по себе процесс набирания текста на телефоне или планшете требует немалых мозговых усилий. Так что ручка и бумага для меня &amp;#8211; идеальный вариант.&lt;/li&gt;
	&lt;li&gt;Выбираю какую-то из крупных идей, которые плавают на поверхности. У меня в голове идеи чаще всего выглядят так: &amp;#8220;Было бы прикольно сделать доску объявлений о продаже авто, в которой будет такая штучка, делающая то-то и вот так-то&amp;#8221;. Из этой мысли я беру идею &amp;#8220;сделать доску объявлений о продаже авто&amp;#8221; или даже просто &amp;#8220;сделать доску объявлений&amp;#8221;.&lt;/li&gt;
	&lt;li&gt;Выписываю на бумагу все возможные детали реализации идеи, которые могут прийти в голову. Не важно, относятся ли они к дизайну, разработке, развертыванию, маркетингу, организации работы или тому, как будет выглядеть мой офис если я займусь этой идеей. Все подряд, без всякого порядка, очередности, приоритетов, ограничений по длине предложений и качеству написания, без оглядки на возможность или невозможность воплощения. Короче, вываливаю все подряд. Этот список не увидит никто, кроме меня. Это, можно сказать, дамп, резервная копия идеи как таковой, в том виде, в котором ее выдает мозг. Просто пишу все подряд как приходит в голову.&lt;/li&gt;
	&lt;li&gt;Как только идеи закончились и ничего больше не пишется &amp;#8211; откладываю ручку и делаю паузу 5-10 минут. В это время важно сохранять всю ту же информационную тишину. Закрываю глаза, смотрю в окно, принимаю душ &amp;#8211; не важно. Главное &amp;#8211; минимум входящей информации.&lt;/li&gt;
	&lt;li&gt;Если в течение перерыва не пришло больше никаких мыслей &amp;#8211; перечитываю то, что написал. Если в мозгу еще остались какие-то остатки, не изложенные в списке &amp;#8211; они скорее всего всплывут при перечитывании или в течение следующих нескольких минут. Если ничего нового не всплыло &amp;#8211; можно считать идею готовой для систематизации.&lt;/li&gt;
	&lt;li&gt;Через несколько часов, максимум &amp;#8211; пара дней, я эту идею высказываю вслух кому-то из друзей или близких людей, подглядывая в конспект. При этом не так важно, разбирается ли человек в том, что я ему говорю (хоть это и желательно), важно чтобы он внимательно выслушал до конца. По ходу высказывания мозг заново переваривает идею, облекая ее в понятные другому человеку термины, и чаще всего при этом рождаются какие-то новые мысли, детали и мелочи, упущенные при первом изложении или на тот момент просто не существовавшие. Эти идеи тоже обязательно записываю в тот же список. Наиболее интересные свои идеи я рассказываю поочередно нескольким людям &amp;#8211; сначала тем, кто не разбирается в деталях реализации совсем (жена, теща), потом тем, кто более-менее в теме (друзья из смежных областей работы &amp;#8211; дизайнеры, верстальщики, маркетологи, сеошники), и в самом конце &amp;#8211; коллегам по цеху (программистам, админам).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Систематизация идеи&lt;/h3&gt;
&lt;p&gt;Для того, чтобы начать работу над идеей, нужно ее хотя бы немного систематизировать. Я использую всего два критерия систематизации &amp;#8211; категории задач и приоритеты.&lt;/p&gt;
&lt;p&gt;Все мысли, которые я изложил на бумаге, я заношу в компьютер (чаще всего документ Google Docs или обычный текстовый файл) и разбиваю подзаголовками на блоки &amp;#8211; разработка, развертывание, продвижение, организация и т.д. Внутри этих списков я выстраиваю все мысли в порядке их важности с точки зрения целостности идеи. Отдельно отмечаю те мысли, без которых нет смысла выпускать продукт, и те, без которых он может и совсем обойтись, то есть чисто декоративные навороты.&lt;/p&gt;
&lt;p&gt;В итоге получается план, более-менее пригодный для начала работы. Перед тем, как приступить к реализации, я его по пунктам переношу в систему управления проектом (например, Basecamp) или работаю с ним прямо в том же документе. На мой взгляд, важно сначала сделать план в простом текстовом виде, чтобы максимум внимания уделить продумыванию содержания, и лишь потом заботиться об оформлении и занесении в какую бы то ни было систему управления проектом. Тем более что идея может и не пройти окончательный отбор.&lt;/p&gt;
&lt;h3&gt;Отбор идей&lt;/h3&gt;
&lt;p&gt;Интересных идей, за которые хочется взяться, у меня в голове обычно сразу несколько. Разные сервисы, инструменты, сайты или бизнес-проекты (иногда даже не связанные с вебом) &amp;#8211; и все интересно, все достойно воплощения. Однако, если делать все сразу, то с таким же успехом можно вообще ничего не делать. Поэтому важно из всех идей выбрать наиболее интересную и перспективную.&lt;/p&gt;
&lt;p&gt;Для оценки идей я использую следующие критерии в порядке приоритета:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Финансовая привлекательность&lt;/strong&gt;, объем потенциальной прибыли, критерии и срок выхода на самоокупаемость. Тут важно оперировать конкретными цифрами. Если ориентируемся на продажу рекламы &amp;#8211; берем минимальную рыночную стоимость рекламы в нашем сегменте. Если планируем продавать продукт &amp;#8211; считаем объемы продаж исходя из минимального процента конверсии (не более 1-3% от общего числа пользователей). Если продаем услугу &amp;#8211; считаем прибыль для наиболее дешевого пакета. Лучше строить бизнес на минимальных цифрах, в этом случае все что сверх минимума &amp;#8211; чистая прибыль.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Полезность.&lt;/strong&gt; Нет никакого смысла делать вещи, которыми никто не будет пользоваться. Кто наши потенциальные покупатели? Чем наш продукт им поможет? Буду ли я сам пользоваться нашим продуктом? Идеальный вариант &amp;#8211; это делать продукт, который решает мою собственную проблему. В этом случае клиентами наверняка станут те, у кого есть та же самая проблема.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Временные и финансовые затраты.&lt;/strong&gt; Могу ли я себе это позволить? Смогу ли я воплотить идею в течение 2-3 месяцев при текущей загруженности? Где я возьму финансы на первоначальные затраты (домен, хостинг, покупка ПО, зарплата сотрудников, офис)? Любой бизнес требует затрат &amp;#8211; это норма. Нужно лишь понимать, где взять денег и сколько. Продавать квартиру и бросать работу ради мифической идеи очень романтично, но может и оказаться просто-напросто гибельно.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Интересность с точки зрения реализации.&lt;/strong&gt; Делать что-то полезное и выгодное, но банальное &amp;#8211; скучно. Мы программисты, на важно делать что-то новое и интересное. Какие новые технологии можно использовать в новом проекте? Чем он будет отличаться от того, что мы делали раньше? Важно понять, какой опыт можно получить при работе над этим проектом, даже если он не принесет денег напрямую.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Наличие готовых альтернатив.&lt;/strong&gt; Если уже есть готовые продукты, в которых ваша идея реализована как минимум на половину &amp;#8211; имеет ли смысл делать новый продукт? Я не имею в виду что это бессмысленно, я говорю о том, что стоит ответить на вопрос: чем ваш продукт будет отличаться от существующих? Какие проблемы новый продукт будет решать лучше? Можно ли эти проблемы решить комбинацией из двух уже существующих продуктов? Насколько легко существующие продукты смогут повторить инновацию?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Резюмируя, можно сказать следующее: не бойтесь идей и не бросайтесь ими. Переносите идеи на бумагу как только они приходят в голову, рассказывайте о них своим друзьям и знакомым, выбирайте наиболее интересные и перспективные идеи &amp;#8211; и &lt;strong&gt;действуйте&lt;/strong&gt;!&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=b-sa9OFHKxY:mTdZNyxi11w:F5rjUU7H7Ys"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?i=b-sa9OFHKxY:mTdZNyxi11w:F5rjUU7H7Ys" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=b-sa9OFHKxY:mTdZNyxi11w:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/HtmlBlog?a=b-sa9OFHKxY:mTdZNyxi11w:I9og5sOYxJI"&gt;&lt;img src="http://feeds.feedburner.com/~ff/HtmlBlog?d=I9og5sOYxJI" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/HtmlBlog/~4/b-sa9OFHKxY" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://railorz.ru/articles/153-getting-an-idea/</feedburner:origLink></entry>
</feed>

