<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2russianfull.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Insight IT</title>
	
	<link>http://www.insight-it.ru</link>
	<description>Информационные технологии</description>
	<lastBuildDate>Sun, 06 May 2012 11:53:48 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/insight-it/feed" /><feedburner:info uri="insight-it/feed" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>insight-it/feed</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/insight-it/feed" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare href="http://lenta.yandex.ru/settings.xml?name=feed&amp;url=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://lenta.yandex.ru/i/addfeed.gif">?????? ? ??????.?????</feedburner:feedFlare><feedburner:feedFlare href="http://www.plusmo.com/add?url=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare href="http://www.thefreedictionary.com/_/hp/AddRSS.aspx?http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://img.tfd.com/hp/addToTheFreeDictionary.gif">Subscribe with The Free Dictionary</feedburner:feedFlare><feedburner:feedFlare href="http://www.bitty.com/manual/?contenttype=rssfeed&amp;contentvalue=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://www.bitty.com/img/bittychicklet_91x17.gif">Subscribe with Bitty Browser</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsalloy.com/?rss=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://www.newsalloy.com/subrss3.gif">Subscribe with NewsAlloy</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:feedFlare href="http://mix.excite.eu/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://image.excite.co.uk/mix/addtomix.gif">Subscribe with Excite MIX</feedburner:feedFlare><feedburner:feedFlare href="http://download.attensa.com/app/get_attensa.html?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://www.attensa.com/blogs/attensa/WindowsLiveWriter/BadgeredintoBadges_10C02/attensa_feed_button5.gif">Subscribe with Attensa for Outlook</feedburner:feedFlare><feedburner:feedFlare href="http://www.webwag.com/wwgthis.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://www.webwag.com/images/wwgthis.gif">Subscribe with Webwag</feedburner:feedFlare><feedburner:feedFlare href="http://www.podcastready.com/oneclick_bookmark.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://www.podcastready.com/images/podcastready_button.gif">Subscribe with Podcast Ready</feedburner:feedFlare><feedburner:feedFlare href="http://www.flurry.com/pushRssFeed.do?r=fb&amp;url=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://www.flurry.com/images/flurry_rss_logo2.gif">Subscribe with Flurry</feedburner:feedFlare><feedburner:feedFlare href="http://www.wikio.com/subscribe?url=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://www.wikio.com/shared/img/add2wikio.gif">Subscribe with Wikio</feedburner:feedFlare><feedburner:feedFlare href="http://www.dailyrotation.com/index.php?feed=http%3A%2F%2Ffeeds.feedburner.com%2Finsight-it%2Ffeed" src="http://www.dailyrotation.com/rss-dr2.gif">Subscribe with Daily Rotation</feedburner:feedFlare><item>
		<title>Вакансии: команда IT-звезд</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/aqJ3YbN6wks/</link>
		<comments>http://www.insight-it.ru/life/vakansii/vakansii-komanda-it-zvezd/#comments</comments>
		<pubDate>Fri, 04 May 2012 14:20:08 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Вакансии]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[JQuery]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[RDM-soft]]></category>
		<category><![CDATA[SQA]]></category>
		<category><![CDATA[ZendFramework]]></category>
		<category><![CDATA[вакансии]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1921</guid>
		<description><![CDATA[Благодаря сайту Insight IT, компания RDM-Soft нашла ОТЛИЧНОГО тимлида! Теперь, тимлидер ищет в свою команду единомышленников и просто IT-звезд. О компании История компании началась в 2003 году. С этого момента выпущено много проектов. Некоторыми из них Вы, возможно, так или иначе пользовались. Сейчас запускается еще один проект: SEO-биржа (родственником будет sape.ru). У Вас есть прекрасная возможность оказаться у истоков [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/S8akriOC2_u7HVH0_idWP6qhtk8/0/da"><img src="http://feedads.g.doubleclick.net/~a/S8akriOC2_u7HVH0_idWP6qhtk8/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/S8akriOC2_u7HVH0_idWP6qhtk8/1/da"><img src="http://feedads.g.doubleclick.net/~a/S8akriOC2_u7HVH0_idWP6qhtk8/1/di" border="0" ismap="true"></img></a></p><p>Благодаря сайту <a href="http://www.insight-it.ru"  target="_blank">Insight IT</a>, компания <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://rdm-soft.com/"  target="_blank">RDM-Soft</a> нашла ОТЛИЧНОГО тимлида! Теперь, <a href="http://www.insight-it.ru/life/vakansii/vakansiya-php-polkovodec/"  target="_blank">тимлидер</a> ищет в свою команду единомышленников и просто IT-звезд.</p>
<p><span id="more-1921"></span></p>
<h2>О компании</h2>
<p>История компании началась в 2003 году. С этого момента выпущено много проектов. Некоторыми из них Вы, возможно, так или иначе пользовались. Сейчас запускается еще один проект: <strong>SEO-биржа</strong> (родственником будет <a href="http://www.insight-it.ru/goto/http://sape.ru/"  rel="nofollow" target="_blank">sape.ru</a>). У Вас есть прекрасная возможность оказаться у истоков будущего хита!</p>
<h2>Кто нужен?</h2>
<ul>
<li><strong>Mr. Backend.</strong> Он же программист.</li>
<li><strong>Мастер-ломастер.</strong> Он же инженер по контролю качества, проще говоря тестер.</li>
<li><strong>Dr. Frontend.</strong> Он же фронтендщик.</li>
</ul>
<div class="hr"><!-- --></div>
<h2>Mr. Backend</h2>
<h3>Требования</h3>
<ul>
<li>Отличные знания: <strong>PHP</strong>, <strong>ООП</strong>, <strong>SQL</strong>, <strong>MVC</strong>, <strong>ZendFramework</strong> (либо альтернатив), <strong>Linux</strong>.</li>
<li>Опыт работы по специальности: от 3 лет.</li>
<li>Опыт работы в команде.</li>
<li>Желание развиваться и изучать новое.</li>
<li>Отсутствие желания искать работу в ближайшие 3 года.</li>
</ul>
<h3>Задачи</h3>
<ul>
<li>Разработка серверной части проекта. Включает в себя :
<ul>
<li>бухгалтерия;</li>
<li>бизнес-логика;</li>
<li>статистика;</li>
<li>различные парсеры.</li>
</ul>
</li>
</ul>
<div class="hr"><!-- --></div>
<h2>Мастер-Ломастер (SQA)</h2>
<h3>Требования</h3>
<ul>
<li>Хорошие знания: <strong>PHP</strong>, <strong>SQL</strong>, <strong>Linux</strong>.</li>
<li>Отличные знания принципов whitebox и blackbox тестирования.</li>
<li>Опыт работы по специальности: от 2 лет.</li>
<li>Опыт работы в команде.</li>
<li>Желание развиваться и изучать новое.</li>
<li>Отсутствие желания искать работу в ближайшие 3 года.</li>
</ul>
<h3>Задачи</h3>
<ul>
<li>Написание юнит-тестов, автотестов.</li>
<li>Тестирование:</li>
<ul>
<li>бекенда;</li>
<li>фронтенда;</li>
<li>бизнес-логики;</li>
<li>производительности;</li>
<li>безопасности.</li>
</ul>
<li>В общем, тоже очень много интересной работы.</li>
</ul>
<div class="hr"><!-- --></div>
<h2>Dr. Frontend</h2>
<div>
<h3>Требования</h3>
<ul>
<li>Понимание, как сделать интерфейс удобным и приятным для пользователя</li>
<li>Отличные знания: <strong>HTML</strong>, <strong>CSS</strong>, <strong>JavaScript</strong> (jQuery, ExtJS или других распространенных библиотек)</li>
<li>Опыт проектирования и реализации пользовательского интерфейса</li>
</ul>
<h3>Задачи</h3>
<ul>
<li>Прототипирование UI сервиса</li>
<li>Реализация спроектированного UI</li>
<li>Разработка расширений для Firefox и Chrome.</li>
</ul>
</div>
<div class="hr"><!-- --></div>
<h2>Условия</h2>
<div class="pretty">
<ul>
<li><em>Удаленная работа.</em></li>
<li>Работа в профессиональной <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://rdm-soft.com/company.html"  target="_blank">команде</a>.</li>
<li>Полный рабочий день (5 дней в неделю по 8 часов).</li>
<li>Карьерный и профессиональный рост.</li>
<li>Прислушивание к Вашему мнению.</li>
<li>Зарплата по результатам собеседования:</li>
<ul>
<li><strong>Mr. Backend:</strong> от $1500 до $2000</li>
<li><strong>Мастер-Ломастер:</strong> от $700 до $1500</li>
<li><strong>Dr. Frontend:</strong> от $1000 до $2000</li>
</ul>
</ul>
</div>
<p>&nbsp;</p>
<p><strong>Чтобы заявить свои права на одну из вакансий, заполните форму</strong><br />
<div class="icon-list icon-star"></p>
<ul>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://docs.google.com/a/rdm-soft.com/spreadsheet/viewform?formkey=dE9ycUJJQ1NlQ1hGdERla0hWSHp1bVE6MQ#gid=0"  target="_blank"><strong>Mr. Backend</strong></a></li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://docs.google.com/a/rdm-soft.com/spreadsheet/viewform?formkey=dGg1cFVjX3JZR0Z1MS13cjFhOUZMQlE6MQ#gid=0"  target="_blank"><strong>Мастер-Ломастер</strong></a></li>
<li><strong><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://docs.google.com/a/xxl.cx/spreadsheet/viewform?formkey=dElZVjBlRGRMNndjWjh1U3BOM2U5dWc6MQ#gid=0"  target="_blank">Dr. Frontend</a></strong></li>
</ul>
<p></div><!-- .icon-list (end) --><br />
<div class="hr"><!-- --></div></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=aqJ3YbN6wks:acSS5c5UebE:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=aqJ3YbN6wks:acSS5c5UebE:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=aqJ3YbN6wks:acSS5c5UebE:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=aqJ3YbN6wks:acSS5c5UebE:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=aqJ3YbN6wks:acSS5c5UebE:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/aqJ3YbN6wks" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/life/vakansii/vakansii-komanda-it-zvezd/feed/</wfw:commentRss>
		<slash:comments>24</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/life/vakansii/vakansii-komanda-it-zvezd/</feedburner:origLink></item>
		<item>
		<title>Архитектура Instagram</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/LKr6puJoEz4/</link>
		<comments>http://www.insight-it.ru/masshtabiruemost/arkhitektura-instagram/#comments</comments>
		<pubDate>Fri, 13 Apr 2012 16:11:57 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Масштабируемость]]></category>
		<category><![CDATA[Amazon]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[CloudFront]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[ELB]]></category>
		<category><![CDATA[Fabric]]></category>
		<category><![CDATA[Facebook]]></category>
		<category><![CDATA[gearman]]></category>
		<category><![CDATA[gunicorn]]></category>
		<category><![CDATA[HAProxy]]></category>
		<category><![CDATA[Intagram]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Memcached]]></category>
		<category><![CDATA[Munin]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[ORM]]></category>
		<category><![CDATA[pgbouncer]]></category>
		<category><![CDATA[pgFouine]]></category>
		<category><![CDATA[Pingdom]]></category>
		<category><![CDATA[postgis]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Redis]]></category>
		<category><![CDATA[Route53]]></category>
		<category><![CDATA[S3]]></category>
		<category><![CDATA[Solr]]></category>
		<category><![CDATA[statsd]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[WSGI]]></category>
		<category><![CDATA[xfs]]></category>
		<category><![CDATA[Архитектура Instagram]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1875</guid>
		<description><![CDATA[Instagram&#160;&#8212; всего лишь iOS, а теперь и Android, приложение для обмена фотографиями с друзьями. Последнее время находится на слуху благодаря новости о покупке проекта Facebook&#39;ом за кругленькую сумму. Недавно один из основателей проекта, Mike Krieger, выступил на конференции с докладом о техническом аспекте проекта, который я и хотел бы вкратце пересказать. Статистика Начало: 1 сервер [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/rmfWXfOc3vg2bVubuxpG_o_NOTE/0/da"><img src="http://feedads.g.doubleclick.net/~a/rmfWXfOc3vg2bVubuxpG_o_NOTE/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/rmfWXfOc3vg2bVubuxpG_o_NOTE/1/da"><img src="http://feedads.g.doubleclick.net/~a/rmfWXfOc3vg2bVubuxpG_o_NOTE/1/di" border="0" ismap="true"></img></a></p><p><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://instagr.am/"  target="_blank">Instagram</a>&nbsp;&mdash; всего лишь <a href="/tag/ios/" target="_blank">iOS</a>, а теперь и <a href="/tag/android/" target="_blank">Android</a>, приложение для обмена фотографиями с друзьями. Последнее время находится на слуху благодаря новости о покупке проекта <a href="/tag/facebook/" target="_blank">Facebook</a>&#39;ом за кругленькую сумму. Недавно один из основателей проекта, Mike Krieger, выступил на конференции с докладом о техническом аспекте проекта, который я и хотел бы вкратце пересказать.</p>
<p><span id="more-1875"></span></p>
<h2>Статистика</h2>
<ul>
<li><strong>Начало:</strong></li>
<ul>
<li>1 сервер слабее Macbook Pro</li>
<li>25к регистраций в первый день</li>
<li>2 разработчика</li>
</ul>
<li><strong>Сегодня:</strong></li>
<ul>
<li>40+ миллионов пользователей</li>
<li>100+ виртуальных серверов в EC2, в том числе:</li>
<li>Проект куплен Facebook за <em>1 млрд. долл</em></li>
<li>1 миллион регистраций за 12 часов после запуска Android-версии</li>
<li>5 разработчиков</li>
</ul>
</ul>
<h2>Технологии</h2>
<ul>
<li><strong><a href="/tag/ubuntu/" target="_blank">Ubuntu</a> <a href="/tag/linux/" target="_blank">Linux</a> 11.04</strong>&nbsp;&mdash; основная операционная система</li>
<li><strong><a href="/tag/python/" target="_blank">Python</a></strong>&nbsp;&mdash; основной язык программирования серверной части</li>
<li><a href="/tag/django/" target="_blank"><strong>Django</strong></a>&nbsp;&mdash; фреймворк</li>
<li><strong><a href="/tag/amazon/" target="_blank">Amazon</a>:</strong></li>
<ul>
<li><strong><a href="/tag/ec2/" target="_blank">EC2</a></strong> - хостинг</li>
<li><strong><a href="/tag/elb/" target="_blank">ELB</a></strong> - балансировка входящих HTTP-запросов</li>
<li><strong><a href="/tag/route53/" target="_blank">Route53</a> </strong>&mdash; DNS</li>
<li><strong><a href="/tag/s3/" target="_blank">S3</a></strong>&nbsp;&mdash; хранение фотографий</li>
<li><strong><a href="/tag/cloudfront/" target="_blank">CloudFront</a></strong>&nbsp;&mdash; <a href="/tag/cdn/" target="_blank">CDN</a></li>
</ul>
<li><strong><a href="/tag/nginx" target="_blank">nginx</a></strong>&nbsp;&mdash; второй уровень балансировки входящих HTTP-запросов</li>
<li><a href="/tag/gunicorn/" target="_blank"><strong>gunicorn</strong></a>&nbsp;&mdash; WSGI-сервер</li>
<li><a href="/tag/haproxy/" target="_blank"><strong>HAProxy</strong></a> - балансировка нагрузки внутри системы</li>
<li><a href="/tag/postgresql/" target="_blank"><strong>PostgreSQL</strong></a>&nbsp;&mdash; основное хранилище данных</li>
<ul>
<li><a href="/tag/postgis/" target="_blank"><strong>postgis</strong></a>&nbsp;&mdash; поддержка гео-запросов</li>
<li><a href="/tag/pgfouine" target="_blank"><strong>pgfouine</strong></a>&nbsp;&mdash; отчеты на основе логов</li>
<li><strong><a href="/tag/pgbouncer/" target="_blank">pgbouncer</a></strong>&nbsp;&mdash; создание пула соединений</li>
</ul>
<li><a href="/tag/redis/" target="_blank"><strong>Redis</strong></a>&nbsp;&mdash; дополнительное хранилище данных</li>
<li><a href="/tag/memcached/" target="_blank"><strong>Memcached</strong></a>&nbsp;&mdash; кэширование</li>
<li><a href="/tag/gearman/" target="_blank"><strong>Gearman</strong></a>&nbsp;&mdash; очередь задач</li>
<li><a href="/tag/solr/" target="_blank"><strong>Solr</strong></a>&nbsp;&mdash; гео-поиск</li>
<li><strong><a href="/tag/munin/" target="_blank">munin</a></strong>, <a href="/tag/statsd/" target="_blank"><strong>statsd</strong></a>, <a href="/tag/pingdom/" target="_blank"><strong>pingdom</strong></a>&nbsp;&mdash; мониторинг</li>
<li><a href="/tag/fabric/" target="_blank"><strong>Fabric</strong></a>&nbsp;&mdash; управление кластером</li>
<li><a href="/tag/xfs/" target="_blank"><strong>xfs</strong></a>&nbsp;&mdash; файловая система</li>
</ul>
<h2>Философия</h2>
<div>
<ol>
<li>Простота</li>
<li>Минимизация операционных издержек</li>
<li>Использование подходящих инструментов</li>
</ol>
</div>
<h2>История</h2>
<ul>
<li>Забыли сделать <strong>favicon.ico</strong> до запуска&nbsp;&mdash; в первый же день логи пестрили ошибками 404</li>
<li>Для хранения данных использовали просто <strong>Django <a href="/tag/orm/" target="_blank">ORM</a></strong> и <strong>PostgreSQL</strong> (из-за postgis)</li>
<li>Начали с одного слабого сервера, после успешного запуска решили переехать на <strong>EC2</strong></li>
<li>Довольно быстро пришлось вынести <a href="/tag/subd/" target="_blank">СУБД</a> на отдельный сервер (виртуальный, естественно)</li>
<li>Количество фотографий продолжало расти и расти, даже самый большой инстанс <strong>EC2</strong> не справлялся</li>
<li>Решили вертикально разделить данные на несколько баз, с использованием механизма <strong>routers</strong> из ORM, параллельно избавившись от внешних ключей</li>
<li>Через несколько месяцев суммарный размер базы данных перевалил за 60Гб и перестало справляться и это решение</li>
<li>Следующим шагом стало горизонтальное разбиение данных <em>(sharding)</em>:</li>
<ul>
<li>Создали несколько тысяч логических баз данных.</li>
<li>Распределили их по существенно меньшему количеству физических серверов (читай: виртуальных машин).</li>
<li>Написали свой механизм определения где искать какую базу данных, с поддержкой миграции (вероятно тоже на основе routers).</li>
</ul>
<li>По последним данным под <strong>PostgreSQL</strong> используется 12+12 виртуальных машин с максимальной оперативной памятью (68.4Гб), а также сетевые диски EBS, объединенные в программный RAID посредством mdadm. Это необходимо, чтобы весь массив данных помещался в памяти, EBS не в состоянии обеспечить достаточную производительность.</li>
<li>С некоторыми задачами лучше справляется <strong>Redis</strong>:</li>
<ul>
<li>Для каждого пользователя в Redis есть список идентификаторов новых фотографий от других пользователей, на которых он подписан.</li>
<li>При отображении потока новых для пользователя фотографий делается выборка части такого списка, после чего посредством multiget достается подробная о них информация из memcached.</li>
<li>Пробовали возложить на него задачу хранения списков подписчиков, но в итоге вернулись к решению на <strong>PostgreSQL</strong> с небольшим кэшированием.</li>
<li>В Redis также хранится информация о сессиях.</li>
<li>Несколько фактов о Redis:</li>
<ul>
<li>Так как все находится в памяти&nbsp;&mdash; очень быстрые операции записи и работы с множествами.</li>
<li>Является не заменой, а дополнением к основному хранилищу данных.</li>
<li>Redis хорош для структур данных, которые относительно ограничены.</li>
<li>Отлично подходит для кэширования комплексных структур данных, где нужно большее, чем просто получить значение по ключу (например&nbsp;&mdash; счетчики, подмножества, проверка вхождения в множества).</li>
<li>Механизм репликации (посредством slaveof) позволяет легко масштабировать операции чтения.</li>
</ul>
</ul>
<li>Пользователи синхронно загружают фотографии на медиа-сервер с (опциональными) заголовком и месте на карте, все остальное происходит асинхронно посредством очередей, например:</li>
<ul>
<li>Сохраняются гео-метки, обновляется <strong>Solr </strong>(который впоследствии заменил postgis).</li>
<li>Идентификатор нового фото добавляется в обсуждавшиеся выше списки для всех подписчиков автора.</li>
</ul>
<li>Поначалу использовали <a href="/tag/apache/" target="_blank">Apache</a> + <strong>mod_wsgi</strong> для запуска <strong>Django</strong>, впоследствии перешли к gunicorn из-за меньшего потребления ресурсов и простоты настройки.</li>
<li>С недавних пор начали использовать <strong>Amazon ELB</strong> вместо <strong>DNS round-robin</strong> для первичной балансировки входяших HTTP-запросов, что позволило:</li>
<ul>
<li>избежать необходимости дешифровки <a href="/tag/ssl/" target="_blank"><strong>SSL</strong></a> посредством nginx;</li>
<li>ускорить исключение из балансировки проблемных серверов.</li>
</ul>
<li>Благодаря использованию <strong>xfs</strong> есть возможность &laquo;замораживать&raquo; и &laquo;размораживать&raquo; дисковые массивы при резервном копировании.</li>
</ul>
<h2>Подводим итоги</h2>
<ul>
<li>Многие проблемы с масштабируемостью&nbsp;&mdash; результат банальных человеческих ошибок.</li>
<li>Масштабирование = замена всех деталей в машине на скорости 150км/ч.</li>
<li>Заранее сложно узнать как в основном будут обращаться к данным, без реального использования.</li>
<li>В первую очередь попытайтесь адаптировать известные Вам технологии и инструменты для создания простого и понятного решения, прежде чем бросаться на поиски чего-то нетривиального.</li>
<li>Дополните свое основное хранилище более гибким компонентом, вроде Redis.</li>
<li>Постарайтесь не использовать два инструмента для решения одной и той же задачи.</li>
<li>Оставайтесь гибкими и ловкими = напоминайте себе о том, что на самом деле имеет значение.</li>
<li>Разрабатывайте решения, к которым не придется постоянно возвращаться из-за их сбоев.</li>
<li>Активное юнит- и функциональное тестирование стоят потраченного на них времени.</li>
<li>DRY: не делайте одну и ту же работу несколько раз.</li>
<li>Слабая связанность посредством уведомлений или сигналов позволяет легко менять структуру проекта.</li>
<li>Дисковый ввод-вывод часто оказывается узким местом, особенно на EC2.</li>
<li>Спускаться до C нужно только при необходимости, большую часть работы лучше делать в Python.</li>
<li>Короткий цикл разработки&nbsp;&mdash; залог быстрого развития.</li>
<li>Частые совместные рассмотрения кода нужны, чтобы все были в курсе происходящего.</li>
<li>Не изобретайте велосипед.</li>
<li>Окружите себя с толковыми <a href="/consulting/" target="_blank">консультантами</a>.</li>
<li>Культура открытости вокруг разработки.</li>
<li>Делитесь с <a href="/tag/opensource/" target="_blank">opensource</a> сообществом.</li>
<li>Фокусируйтесь на том, что вы делаете лучше всего.</li>
<li>Вашим пользователям абсолютно без разницы, написали ли Вы собственную СУБД или нет.</li>
<li>Не переоптимизируйте и не предполагайте заранее как сайт будет расти.</li>
<li>Не рассчитывайте, что &laquo;кто-то еще присоединится к команде и разберется с этим&raquo;.</li>
<li>Для социальных стартапов очень мало, или даже совсем нет, нерешимых вопросов, связанных с масштабируемостью.</li>
</ul>
<h2>Источник информации</h2>
<p>Упоминавшаяся во вступлении неприлично длинная презентация из 185 слайдов:</p>
<p><iframe id="doc_4510" src="http://www.scribd.com/embeds/89025069/content?start_page=1&amp;view_mode=list" frameborder="0" scrolling="no" width="100%" height="600" data-auto-height="true" data-aspect-ratio=""></iframe></p>
<p>На видео, к сожалению, это выступление не записывалось.</p>
<p>Часть информации взята из <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://instagram-engineering.tumblr.com/"  target="_blank">технического блога Instagram</a>.</p>
<p>&nbsp;</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=LKr6puJoEz4:-TvlCyJ8l5c:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=LKr6puJoEz4:-TvlCyJ8l5c:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=LKr6puJoEz4:-TvlCyJ8l5c:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=LKr6puJoEz4:-TvlCyJ8l5c:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=LKr6puJoEz4:-TvlCyJ8l5c:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/LKr6puJoEz4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/masshtabiruemost/arkhitektura-instagram/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/masshtabiruemost/arkhitektura-instagram/</feedburner:origLink></item>
		<item>
		<title>Повторное использование шаблонов</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/j1ES-eHXDok/</link>
		<comments>http://www.insight-it.ru/programmirovanie/javascript/povtornoe-ispolzovanie-shablonov/#comments</comments>
		<pubDate>Fri, 13 Apr 2012 04:00:12 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[mustache]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[rendering]]></category>
		<category><![CDATA[template]]></category>
		<category><![CDATA[template engine]]></category>
		<category><![CDATA[интерфейс]]></category>
		<category><![CDATA[шаблон]]></category>
		<category><![CDATA[шаблонизация]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1855</guid>
		<description><![CDATA[Лень&#160;&#8212; двигатель прогресса Сегодня мы рассмотрим способ, позволяющий немного упростить себе жизнь при создании интерактивного сайта путем повторного использования шаблонов. Визуально результат будет примерно таким же, как при дублировании бизнес-логики в браузере, но ценой существенно меньших трудозатрат на разработку JavaScript-клиента, да и на серверной части тоже. Хотите узнать как это провернуть? Небольшая ремарка, чтобы не было [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/Cesz2UOeG7oHrgbehRHXWxMuEk8/0/da"><img src="http://feedads.g.doubleclick.net/~a/Cesz2UOeG7oHrgbehRHXWxMuEk8/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/Cesz2UOeG7oHrgbehRHXWxMuEk8/1/da"><img src="http://feedads.g.doubleclick.net/~a/Cesz2UOeG7oHrgbehRHXWxMuEk8/1/di" border="0" ismap="true"></img></a></p><blockquote style="margin-left: 420px;">
<p>Лень&nbsp;&mdash; двигатель прогресса</p>
</blockquote>
<p>Сегодня мы рассмотрим способ, позволяющий немного упростить себе жизнь при создании <a href="/interactive/" target="_blank">интерактивного сайта</a> путем повторного использования шаблонов. Визуально результат будет примерно таким же, как при дублировании бизнес-логики в браузере, но ценой существенно меньших трудозатрат на разработку JavaScript-клиента, да и на серверной части тоже. Хотите узнать как это провернуть?</p>
<p><span id="more-1855"></span></p>
<div class="pretty">
<p>Небольшая ремарка, чтобы не было недопонимания из-за терминологии:</p>
<ul>
<li><strong>Шаблон</strong> <em>(template)</em>: HTML-документ с расширенным набором тегов, которые впоследствии используются для подстановки динамических данных.</li>
<li><strong>Шаблонизатор</strong> <em>(templating engine)</em>: библиотека, позволяющая на основе <em>шаблона</em> (использующего определенный синтаксис дополнительных тегов) и <em>динамических данных</em> получить итоговый HTML-документ, пригодный для отображения в браузере.</li>
<li><strong>Рендеринг</strong> <em>(rendering)</em>: в данном контексте&nbsp;&mdash; процесс, которым занимается шаблонизатор.</li>
</ul>
</div>
<h2>Общий принцип</h2>
<p>Чтобы сразу в голове сложилась нужная картина, начнем с дополненной схемы из статьи про <a href="http://www.insight-it.ru/masshtabiruemost/arkhitektura-interaktivnykh-sajjtov/"  target="_blank">архитектуру интерактивных сайтов</a>:</p>
<p><img class="aligncenter size-full wp-image-1859" title="Повторное использование шаблонов" src="http://www.insight-it.ru/wp-content/uploads/2012/04/templates.jpeg" alt="Повторное использование шаблонов" width="630" height="797" /></p>
<p>Если вкратце, то стандартный интерфейс внутренних сервисов, скрывающихся за блоком <strong>&laquo;Бизнес-логика&raquo;</strong>, можно реализовать таким образом, чтобы он возвращал все необходимые данные для рендеринга шаблона плюс его имя. База <strong>шаблонов</strong> у всех общая, у каждого уникальное имя, каждый сервер интерфейсов (обоих) держит по копии всех шаблонов в памяти.</p>
<p><strong>HTML интерфейс</strong> просто разбирает HTTP-запросы, отправляет на его основе сообщение(ия) внутренним сервисам, получает в ответ имя шаблона и данные для его заполнения, с помощью <strong>шаблонизатора</strong> рендерит итоговый <a href="/tag/html/" target="_blank">HTML</a> и отдает браузеру или роботу.</p>
<p><strong>Интерфейс сериализованных данных</strong> <em>(если он, как и обсуждалось ранее, работает через <a href="http://www.insight-it.ru/tekhnologii/soobshheniya/postoyannoe-soedinenie-mezhdu-brauzerom-i-serverom/"  target="_blank">постоянное соединение с браузером</a>)</em> каждому подключившемуся клиенту первым делом отправляет <a href="/tag/json/" target="_blank">JSON</a>-объект с шаблонами, по крайней мере если их не особо много, иначе лучше &laquo;по запросу&raquo;. При действии пользователя JavaScript-клиент отправляет сообщение с информацией, на его основе интерфейс сериализованных данных передает то же самое (а может и как-то модифицированное) сообщение внутреннему сервису, также получает в ответ имя шаблона и данные и перенаправляет их клиенту (возможно сконвертировав в другой формат). Клиенту остается передать их своему шаблонизатору и заменить результатом его работы какую-то часть уже имеющегося в окне браузера HTML-документа.</p>
<h2>Рендеринг</h2>
<p><strong>Шаблонизаторов</strong> сейчас доступно огромное количество под любую платформу, с разной производительностью и возможностями, но чтобы воплотить эту стратегию в жизнь подойдут далеко не все. Два основных требования:</p>
<ul>
<li><em>Отсутствие внешних вызовов при рендеринге</em>, то есть на входе только данные, если используются какие-то фильтры или что-то такое&nbsp;&mdash; они должны быть частью шаблонизатора.</li>
<li><em>Шаблонизатор должен иметь реализацию на JavaScript</em>, так как будет исполняться в том числе и в браузере.</li>
</ul>
<p>Да, многофункциональные шаблонизаторы вроде <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto//tag/jinja2/" target="_blank">Jinja2</a>&nbsp;&mdash; это очень удобно, но конкретно в данном случае богатый ассортимент возможностей не уместен. Наиболее известный <em>кроссплатформенный</em> шаблонизатор, не обремененный ничем лишним, называется <strong><a href="http://mustache.github.com/"  target="_blank">mustache</a></strong>. С его использованием иногда получаются довольно замысловатые конструкции, но зато он отлично подходит под этот сценарий использования и прост как три копейки, изучить можно за 5 минут, <em>рекомендую</em>.</p>
<p>В этой схеме напрашивается использование <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto//tag/node-js/" target="_blank">node.js</a> для реализации <strong>HTML-интерфейса</strong>, что откроет доступ к многочисленным шаблонизаторам, <a href="https://github.com/joyent/node/wiki/modules#wiki-templating"  target="_blank">реализованным исключительно на JavaScript</a>. Тем более кроме рендеринга шаблонов эта часть проекта практически ничего и не делает. В качестве бонуса требование про отсутствие внешних вызовов станет не таким строгим, да и в целом, если минималистичное решение вроде <a href="/tag/mustache/" target="_blank">mustache</a> по каким-то идеологическим соображениям не устраивает&nbsp;&mdash; любой написанный для node.js шаблонизатор наверняка станет отличным выходом.</p>
<h2>Структура шаблонов</h2>
<p>При рендеринге на клиентской стороне <strong>обычно</strong> нужно заменять лишь содержимое определенного блока, где располагается основной контент сайта. Изменения в в других частях сайта нужны существенно реже, соответственно стоит вынести их в отдельные шаблоны.</p>
<p>Таким образом большинство шаблонов, соответствующих страницам сайта, представляют собой содержимое одного блока. Отдельные шаблоны, актуальные для всего сайта, создаются для:</p>
<ul>
<li>Блока <strong>&lt;head&gt;</strong> документа</li>
<li>Видимой &laquo;шапки&raquo; сайта</li>
<li>Сайдбара(ов), если они не сильно зависят от основного контентом страниц</li>
<li>Видимого &laquo;подвала&raquo; сайта плюс тегов для подключения <a href="/tag/javascript/" target="_blank">JavaScript</a></li>
</ul>
<p><strong>HTML-интерфейс</strong> при чтении их из файловой системы &laquo;склеивает&raquo; их в полные шаблоны для каждой страницы, просто конкатенацией или с использованием механизмов шаблонизатора. <strong>Интерфейс сериализованных данных</strong> &laquo;заворачивает&raquo; шаблоны страниц в JSON (или другой используемый формат) прямо в исходном виде для вставки в блок с основным контентом. Из &laquo;общесайтовых&raquo; шаблонов браузерному клиенту вероятно могут понадобиться только сайдбар(ы), и то не всегда.</p>
<p>Изменения в остальных частях сайта лучше все же отдать на совесть <em>представлений</em> на основе клиентского фреймворка. В первую очередь это касается изменения <strong>&lt;title&gt;</strong> и других мета-тегов.</p>
<h2>Примечания</h2>
<ul>
<li>При использовании минималистичного шаблонизатора без внешних вызовов будьте морально готовы передавать ему &laquo;многоуровневые&raquo; объекты для вставки в шаблон. Например, если говорить о постраничной навигации, там, где в продвинутом шаблонизаторе было бы что-то вроде <strong>{% pagination (current_page, total_pages) %},</strong> может понадобится не тольно написать саму верстку (что, в целом, хорошая практика), а еще и передать информацию о точном списке страниц, какая именно из них активная, где пропуски и пр.</li>
<li>Стоит обращать внимание на производительность используемого шаблонизатора. Например, под одну из платформ &laquo;официальная&raquo; реализация <strong>mustache</strong>, как оказалось, проигрывает сторонней с отрывом в 2 порядка.</li>
<li>Хоть при таком подходе добиться одинакового внешнего вида страниц при рендеринге серверной и клиентской частью достаточно легко, следить за их соответствием все же стоит&nbsp;&mdash; какие-то детали можно и упустить.</li>
</ul>
<h2>Заключение</h2>
<p>Как я уже намекал в конце <a href="http://www.insight-it.ru/tekhnologii/soobshheniya/postoyannoe-soedinenie-mezhdu-brauzerom-i-serverom/"  target="_blank">предыдущего материала</a>, обсуждавшийся в этой статье подход не совсем <em>&laquo;идеологически правильный&raquo;</em>, по крайней мере с точки зрения используемого <em>клиентского фреймворка</em>. <strong>Модели</strong>, вероятно, будут использоваться для хранения библиотеки шаблонов и данных для их рендеринга, а не для объектов предметной области проекта. <strong>Представления</strong> будут отвечать лишь за рендеринг шаблонов и синхронизацию второстепенных элементов интерфейса. Если Вы все же пойдете по этому пути, хочется, чтобы Вы сделали это осознанно. Альтернативный сценарий создания полноценного JavaScript-приложения для работы в браузере для некоторых проектов по-прежнему может оказаться более предпочтительным.</p>
<p>В следующей статье мы наконец-то перейдем к более привычной для меня <em>серверной части</em> интерактивных сайтов, там тоже есть много интересных моментов, которые стоит обсудить.</p>
<div class="pretty">
<p>Эта статья&nbsp;&mdash; четвертая в <a href="http://www.insight-it.ru/interactive/"  target="_blank">серии про Интерактивные сайты</a>, автор&nbsp;&mdash; <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://blinkov.ru"  target="_blank">Иван Блинков</a>, основано на личном опыте, внешние источники информации при написании не использовались.</p>
<p>До встречи <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://feeds.feedburner.com/insight-it/feed"  target="_blank">на страницах Insight IT</a>!</p>
</div>
<p>&nbsp;</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=j1ES-eHXDok:t4Uk95m7oSo:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=j1ES-eHXDok:t4Uk95m7oSo:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=j1ES-eHXDok:t4Uk95m7oSo:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=j1ES-eHXDok:t4Uk95m7oSo:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=j1ES-eHXDok:t4Uk95m7oSo:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/j1ES-eHXDok" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/javascript/povtornoe-ispolzovanie-shablonov/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/programmirovanie/javascript/povtornoe-ispolzovanie-shablonov/</feedburner:origLink></item>
		<item>
		<title>Вакансия закрыта: PHP-полководец</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/r8579VsiRTI/</link>
		<comments>http://www.insight-it.ru/life/vakansii/vakansiya-php-polkovodec/#comments</comments>
		<pubDate>Thu, 12 Apr 2012 16:52:15 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Вакансии]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[teamlead]]></category>
		<category><![CDATA[вакансии]]></category>
		<category><![CDATA[менеджер]]></category>
		<category><![CDATA[разработчик]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1857</guid>
		<description><![CDATA[Компания RDM-Soft приглашает на работу полководца команды PHP-разработчиков (тимлидера). О компании История компании началась в 2003 году. С этого момента выпущено много проектов. Некоторыми из них Вы, возможно, так или иначе пользовались. Сейчас запускается еще один проект: SEO-биржа (родственником будет sape.ru). У Вас есть прекрасная возможность оказаться у истоков будущего хита в роли лидера команды разработчиков! [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/U9uNqv7_uDlq9rTVPYusu7_zqRg/0/da"><img src="http://feedads.g.doubleclick.net/~a/U9uNqv7_uDlq9rTVPYusu7_zqRg/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/U9uNqv7_uDlq9rTVPYusu7_zqRg/1/da"><img src="http://feedads.g.doubleclick.net/~a/U9uNqv7_uDlq9rTVPYusu7_zqRg/1/di" border="0" ismap="true"></img></a></p><div>Компания <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://rdm-soft.com/"  target="_blank">RDM-Soft</a> приглашает на работу полководца команды <a href="/tag/php/" target="_blank">PHP</a>-разработчиков <em>(тимлидера)</em>.</div>
<div><span id="more-1857"></span></div>
<h2>О компании</h2>
<p>История компании началась в 2003 году. С этого момента выпущено много проектов. Некоторыми из них Вы, возможно, так или иначе пользовались. Сейчас запускается еще один проект: <strong>SEO-биржа</strong> (родственником будет <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://sape.ru/"  target="_blank">sape.ru</a>). У Вас есть прекрасная возможность оказаться у истоков будущего хита в роли лидера команды разработчиков!</p>
<h2>Обязанности</h2>
<ul>
<li>Руководство группой разработчиков.</li>
<li>Ставить задачи команде и проверять их выполнение.</li>
<li>Бить по рукам за некачественный код, показывать как правильно писать.</li>
<li>Хвалить, раздавать пряники.</li>
<li>Иногда писать самому.</li>
</ul>
<h2>Требования</h2>
<ul>
<li>Опыт руководства командой разработчиков.</li>
<li>Понимание аспектов мотивации и демотивации программистов.</li>
<li>Опыт программирования от 3-х лет.</li>
<li>Отличное знание <strong>PHP5</strong> &amp;&amp; <strong>MVC</strong> &amp;&amp; <strong>SQL</strong>.</li>
<li>Базовые знания <strong>JavaScript</strong> &amp;&amp; <strong>HTML</strong>.</li>
<li>Базовые знания <strong>unix shell</strong>.</li>
<li>Опыт работы с <strong>svn</strong> || <strong>git</strong> || <strong>mercurial.</strong></li>
<li>Опыт работы с <strong>redmine</strong> || <strong>mantis</strong> || <strong>jira</strong>.</li>
</ul>
<h2>Плюсом будет</h2>
<ul>
<li>Навыки работы с NoSQL: <strong>MongoDB</strong> || <strong>Redis</strong> || <strong>Memcached</strong>.</li>
<li>Опыт разработки в области HA и HL.</li>
<li>Опыт применения lean- и agile- методологий в разработке.</li>
<li>Знания таких слов как: ДеМарко, КанБан, Таичи Оно, SCRUM.</li>
<li>Понимание цикла Деминга и SDLС в принципе.</li>
</ul>
<h2>Условия</h2>
<ul>
<li><strong>Удаленная работа.</strong></li>
<li>Гибкий график работы.</li>
<li>Работа в слаженной профессиональной команде.</li>
<li><em>Зарплата от $2500 до $3000 в месяц.</em></li>
</ul>
<h2>Вакансия закрыта</h2>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=r8579VsiRTI:DLoLqVSNA2Y:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=r8579VsiRTI:DLoLqVSNA2Y:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=r8579VsiRTI:DLoLqVSNA2Y:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=r8579VsiRTI:DLoLqVSNA2Y:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=r8579VsiRTI:DLoLqVSNA2Y:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/r8579VsiRTI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/life/vakansii/vakansiya-php-polkovodec/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/life/vakansii/vakansiya-php-polkovodec/</feedburner:origLink></item>
		<item>
		<title>Постоянное соединение между браузером и сервером</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/lU0pTsUpcqQ/</link>
		<comments>http://www.insight-it.ru/tekhnologii/soobshheniya/postoyannoe-soedinenie-mezhdu-brauzerom-i-serverom/#comments</comments>
		<pubDate>Mon, 09 Apr 2012 20:47:23 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Сообщения]]></category>
		<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Comet]]></category>
		<category><![CDATA[epoll]]></category>
		<category><![CDATA[EventSource]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[polling]]></category>
		<category><![CDATA[streaming]]></category>
		<category><![CDATA[WebSocket]]></category>
		<category><![CDATA[клиентская часть]]></category>
		<category><![CDATA[серверная часть]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1843</guid>
		<description><![CDATA[В статье про клиентскую часть интерактивного интернет-проекта мы подошли к вопросу возможности использования двухстороннего постоянного соединения между сайтом и JavaScript-клиентом для синхронизации их состояний. Такое соединение представляет собой канал для обмена сообщениями в реальном времени между браузером и серверным процессом, причем каждая сторона может быть инициатором отправки сообщения и имеет некую логику реакции на получаемые [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/pk279jY0wOoWAiinCk8dDgECxmI/0/da"><img src="http://feedads.g.doubleclick.net/~a/pk279jY0wOoWAiinCk8dDgECxmI/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/pk279jY0wOoWAiinCk8dDgECxmI/1/da"><img src="http://feedads.g.doubleclick.net/~a/pk279jY0wOoWAiinCk8dDgECxmI/1/di" border="0" ismap="true"></img></a></p><p>В статье про <a href="http://www.insight-it.ru/programmirovanie/javascript/klientskaya-chast-interaktivnogo-sajjta/"  target="_blank">клиентскую часть интерактивного интернет-проекта</a> мы подошли к вопросу возможности использования двухстороннего постоянного соединения между сайтом и JavaScript-клиентом для синхронизации их состояний. Такое соединение представляет собой канал для обмена сообщениями <em>в реальном времени</em> между браузером и серверным процессом, причем каждая сторона может быть инициатором отправки сообщения и имеет некую логику реакции на получаемые сообщения.</p>
<p>Сегодня мы рассмотрим основные варианты реализации этого принципа и как он сочетается с обсуждавшимися в предыдущих статьях <a href="/interactive/" target="_blank">серии</a> темами.</p>
<p><span id="more-1843"></span></p>
<h2>Транспорт</h2>
<p>Так как одной из сторон постоянного соединения является браузер, вопрос кроссбраузерности при его реализации стоит не менее остро, чем, например, при верстке. В 2001 году, когда появился на свет самый часто вспоминаемый недобрым словом браузер в мире, о подобных технологиях постоянного соединения между браузером и сервером практически никто не задумывался даже отдаленно.</p>
<p>Существуют несколько протоколов и связанных с ними технологий, которые позволяют реализовать постоянное с точки зрения приложения соединение между браузером и сервером, обычно их называют <strong>транспортами</strong>. Каждый из них обладает разной производительностью, особенностями реализации и нагрузкой на серверную часть. Возможно не полный их список c краткими пояснениями:</p>
<ul>
<li><strong><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://ru.wikipedia.org/wiki/WebSocket"  target="_blank">WebSocket</a>:</strong> пожалуй, самый эффективный с точки зрения производительности и нагрузки на сервер транспорт. Протокол относительно новый, появился в рамках работы над <a href="/tag/html5/" target="_blank">HTML5</a>. Доступен только в очень свежих браузерах, имеет несколько более-менее стандартных версий. Используется одно соединение для обоих направлений обмена сообщениями.</li>
<li><strong><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://dev.w3.org/html5/eventsource/"  target="_blank">EventSource</a>:</strong> появился примерно в то же время, что и WebSocket, но по задумке должен использоваться для получения односторонних уведомлений от сервера. В совокупности с простыми AJAX запросами для отправки событий из браузера может использоваться для двустороннего общения. Но так как он доступен примерно в тех же версиях браузеров, что и WebSocket, со сценариями, когда он оказывался бы более предпочтительным, я не сталкивался. Технически очень похож на следующий транспорт.</li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://en.wikipedia.org/wiki/Push_technology#HTTP_server_push"  target="_blank"><strong>AJAX Multipart </strong>aka</a><strong><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://en.wikipedia.org/wiki/Push_technology#HTTP_server_push"  target="_blank"> HTTP Streaming</a>:</strong> после получения HTTP-запроса от клиента сервер не &laquo;отпускает&raquo; его и по мере поступления отправляет в него свои сообщения. Для отправки сообщений из браузера при необходимости создается второе соединение.</li>
<li><strong><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://en.wikipedia.org/wiki/Push_technology#Long_polling"  target="_blank">AJAX/HTTP Polling</a>:</strong> в отличии от предыдущего транспорта, браузер закрывает HTTP-соединение после каждого отправленного в него сообщения или по прошествии определенного таймаута (обычно порядка 20-40 секунд). А браузер сразу же после получения сообщения открывает новое соединение, таким образом у сервера по-прежнему практически всегда есть соединение, куда можно отправить сообщения. Хоть по нагрузке на сервер этот вариант самый тяжелый, поддерживают его практически все браузеры.</li>
<li><strong><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/gimite/web-socket-js"  target="_blank">Adobe Flash</a>:</strong> эта платформа может эмулировать поддержку WebSocket при определенном стечении обстоятельств (удачная комбинация Flash-плеера и браузера). Немного нетривиальна в настройке из-за своих особенностей.</li>
</ul>
<p>По поводу поддержки каждого из них различными браузерами было бы неплохо составить табличку, но на самом деле нюансов там много и многое зависит не только от версии браузера, но и от других обстоятельств, вроде наличия и типа прокси, использования трюков с iframe, наличия Flash-плеера и т.п.</p>
<p>Все вышеизложенные транспорты в конечном итоге основываются на протоколе <a href="/tag/http/" target="_blank">HTTP</a>. Большинство из современных браузеров ограничивают количество одновременных HTTP-соединений с доменом <strong>до двух</strong>, что как раз достаточно даже для менее эффективных вариантов.</p>
<p>В любом случае работать напрямую с транспортами не обязательно, благо существует большое количество библиотек и сервисов, позволяющих от них абстрагироваться, к ним и переходим.</p>
<h2>Абстракция</h2>
<p>По сути такие библиотеки состоят из двух частей: клиентской на <a href="/tag/javascript/" target="_blank">JavaScript</a> и серверной для одной или нескольких платформ. Клиент определяет какой из доступных в текущем браузере транспортов является наиболее эффективным и с его помощью устанавливает соединение с сервером, который поддерживает несколько протоколов. С точки зрения разработчика интерфейс, ими предоставляемый, не зависит от транспорта и примерно одинаков:</p>
<ul>
<li>Метод для <strong>отправки</strong> сообщения противоположной стороне.</li>
<li>Регистрация обработчика события, который будет вызван <strong>при получении</strong> сообщения от противоположной стороны, с содержанием сообщения в аргументе.</li>
<li>Метод, который будет вызван при установке и разрывании соединения.</li>
<li>Инициатором соединения по очевидным причинам всегда является клиент, так что у него есть дополнительный механизм для этого, с возможностью указать какие-то настройки.</li>
</ul>
<p>При выборе такой библиотеки для конкретного проекта очень большую роль играет его основная серверная платформа: обычно хочется использовать тот же язык программирования для обработки сообщений, что и для реализаций основной серверной части. Чаще всего используется основанный на <a href="/tag/epoll/" target="_blank">epoll</a> или аналогах HTTP-сервер, что позволяет поддерживать большое количество пользователей онлайн:</p>
<ul>
<li><a href="/tag/node-js/" target="_blank">Node.js</a> на <a href="/tag/javascript/" target="_blank">JavaScript</a></li>
<li>На <a href="/tag/erlang/" target="_blank">Erlang</a> есть несколько очень эффективных HTTP-серверов:</li>
<ul>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/extend/cowboy"  target="_blank">cowboy</a></li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/ostinelli/misultin"  target="_blank">misultin</a></li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/mochi/mochiweb"  target="_blank">mochiweb</a></li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://yaws.hyber.org/"  target="_blank">yaws</a></li>
</ul>
<li><a href="/tag/tornado/" target="_blank">Tornado</a> на <a href="/tag/python/" target="_blank">Python</a></li>
<li><a href="/tag/netty/" target="_blank">netty</a> на <a href="/tag/java/" target="_blank">Java</a></li>
</ul>
<p>Так как самих библиотек этой категории существует примерно пару десятков, расскажу вкратце о наиболее заслуживающих внимания на мой взгляд:</p>
<ul>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://socket.io"  target="_blank">socket.io</a>: поддерживает практически все возможные транспорты, включая <a href="/tag/flash/" target="_blank">Flash</a>. Основная серверная платформа&nbsp;&mdash; <strong>node.js</strong>, силами сторонних разработчиков есть реализации протокола на других платформах. Имеет спорную репутацию, проект довольно громоздкий, в некоторых ситуациях ведет себя непредсказуемо.</li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/sockjs/sockjs-client"  target="_blank">SockJS</a>: очень молодой проект, поддерживает необходимый минимум транспортов, прост в эксплуатации. Относительно стабилен и предсказуем. Серверная часть доступна на <strong>node.js</strong>, <strong>Tornado</strong> и <strong>cowboy/misultin, </strong>активно работают над другими платформами.</li>
</ul>
<p>Существуют коммерческие решения, абсолютно идентичные по принципу работы и функционалу. Аналогичная обсуждавшимся opensource решениям библиотека дополняется брокером сообщений для организации паттерна &laquo;публикация-подписка&raquo; и в совокупности с хостингом &laquo;в облаках&raquo; продается с оплатой за количество переданных сообщений (или по подписке с каким-то лимитом), естественно с нехилой наценкой. Плюсы и минусы очевидны: отсутствие необходимости обо всем этом заботиться против относительно высокой стоимости, потере контроля при сбоях или необходимости изменений, привязке к стороннему поставщику услуг и т.п. Рекламировать их не буду, при желании легко гуглятся, ровно как и оставшиеся альтернативные opensource проекты.</p>
<h2>Вернемся к интерактивным сайтам</h2>
<p>Надеюсь, только что закончившегося лирического отступления на &frac34; статьи Вам будет достаточно, чтобы составить общее представление о построении постоянного соединения между браузером и сервером, а желательно и определиться с каким-то решением для автоматического выбора наиболее эффективного транспорта в контексте именно Вашего проекта.</p>
<p>Получив примитивный интерфейс в виде &laquo;отправить сообщение / отреагировать на сообщение&raquo; необходимо определиться с тем, что же мы будем передавать в этих сообщениях и как будем на них реагировать.</p>
<p>С форматом сериализации сообщений все довольно просто: выбор между XML и JSON очевиден в пользу последнего, а заморачиваться с чем-то более экзотическим смысла мало (хотя давно хочу попробовать в этой роли <a href="/tag/protocol-buffers/" target="_blank">Protocol Buffers</a> или <a href="/tag/bson/" target="_blank">BSON</a>, но никак руки не доходят).</p>
<p>Намного интереснее вопрос о том, что, собственно, будет в этих сообщениях содержаться. В <a href="http://www.insight-it.ru/programmirovanie/javascript/klientskaya-chast-interaktivnogo-sajjta/"  target="_blank">предыдущей статье</a> мы остановились на использовании фреймворка для организации кода JavaScript-клиента. Предлагаемая ими концепция <strong>модели</strong> обычно по-умолчанию предоставляет возможность синхронизации с сервером посредством <a href="/tag/ajax/" target="_blank">AJAX</a> запросов и механизм изменения этого поведения. Для использовавшегося в качестве примера <a href="/tag/backbone-js/" target="_blank">Backbone.js</a> для этого необходимо переопределить функцию <strong>Backbone.sync</strong>. При сохранении модели клиент будет отправлять объект с идентификатором модели и списком её изменений. Запрос изменений с сервера будет происходить асинхронно, то есть после отправки сообщения о том, что нужны данные для такой-то модели, посредством метода fetch он сам не получит ответа. Собственно изменения в модели произведет обработчик получения сообщений, в котором должна быть реализована соответствующая логика. Далее подписанные на события изменений в моделях объекты-представления будут соответствующим образом обновлять DOM-дерево страницы, отображая пользователю нужную информацию. <em>Это, пожалуй, наиболее <strong>правильный</strong> способ интегрировать постоянное соединение и клиентский фреймфорк.</em></p>
<p>Основными минусами его является очень серьезный объем работы по разработке клиентской части, а также дублирование достаточно большой части логики и HTML-шаблонов между серверной и клиентской сторонами. Я бы рекомендовал использовать этот подход, только если позволяют трудовые ресурсы (читай: есть хотя бы отдельный специализирующийся на JavaScript разработчик), либо когда проект по каким-то причинам решил отказаться от реализации статичного HTML-интерфейса.</p>
<p>В следующей статье я расскажу о менее трудозатратном способе добиться того же результата, который основан на жертве идеологической правильностью в пользу минимизации повторного написания кода.</p>
<div class="pretty">
<p>Эта статья&nbsp;&mdash; третья в <a href="http://www.insight-it.ru/interactive/"  target="_blank">серии про Интерактивные сайты</a>, автор&nbsp;&mdash; <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://blinkov.ru"  target="_blank">Иван Блинков</a>, основано на личном опыте, внешние источники информации при написании не использовались.</p>
<p>До встречи <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://feeds.feedburner.com/insight-it/feed"  target="_blank">на страницах Insight IT</a>!</p>
</div>
<p>&nbsp;</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=lU0pTsUpcqQ:wtYHXCzcR4M:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=lU0pTsUpcqQ:wtYHXCzcR4M:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=lU0pTsUpcqQ:wtYHXCzcR4M:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=lU0pTsUpcqQ:wtYHXCzcR4M:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=lU0pTsUpcqQ:wtYHXCzcR4M:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/lU0pTsUpcqQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/tekhnologii/soobshheniya/postoyannoe-soedinenie-mezhdu-brauzerom-i-serverom/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/tekhnologii/soobshheniya/postoyannoe-soedinenie-mezhdu-brauzerom-i-serverom/</feedburner:origLink></item>
		<item>
		<title>Клиентская часть интерактивного сайта</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/JKua9Wu43Nw/</link>
		<comments>http://www.insight-it.ru/programmirovanie/javascript/klientskaya-chast-interaktivnogo-sajjta/#comments</comments>
		<pubDate>Fri, 06 Apr 2012 17:17:35 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Backbone.js]]></category>
		<category><![CDATA[CoffeeScript]]></category>
		<category><![CDATA[JQuery]]></category>
		<category><![CDATA[клиентская часть]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1827</guid>
		<description><![CDATA[Клиентская часть сайта играет ключевую роль в обеспечении его интерактивности. Именно на нее возлагается переопределение стандартного поведения для создания впечатления живого организма вместо кучки бездушных страниц. В статье про архитектуру интерактивных сайтов я подробно изложил основные функции и требования, которые перед ним стоят. Сегодня же я представлю свое видение того, как его грамотно реализовать. На статус [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/qRqU97SjIkH2SjCBW1CP8gswsu0/0/da"><img src="http://feedads.g.doubleclick.net/~a/qRqU97SjIkH2SjCBW1CP8gswsu0/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/qRqU97SjIkH2SjCBW1CP8gswsu0/1/da"><img src="http://feedads.g.doubleclick.net/~a/qRqU97SjIkH2SjCBW1CP8gswsu0/1/di" border="0" ismap="true"></img></a></p><p>Клиентская часть сайта играет ключевую роль в обеспечении его <em>интерактивности</em>. Именно на нее возлагается переопределение стандартного поведения для создания впечатления живого организма вместо кучки бездушных страниц. В статье про <a href="http://www.insight-it.ru/masshtabiruemost/arkhitektura-interaktivnykh-sajjtov/"  target="_blank">архитектуру</a> интерактивных сайтов я подробно изложил основные функции и требования, которые перед ним стоят. Сегодня же я представлю свое видение того, как его грамотно реализовать. На статус единственно-правильного-решения не претендую, статью можно воспринимать просто как набор практических советов и рекомендаций. <span id="more-1827"></span></p>
<p>Итак, сегодня мы будем обсуждать создание JavaScript-клиента для интерактивного сайта. Начнем, пожалуй, с организации кода проекта с целью облегчения его сопровождения при росте кодовой базы, перейдем к переопределению ключевых обработчиков событий, затем к сохранению стандартного поведения браузера и закончим синхронизацией состояния между клиентом и сервером.</p>
<h2>Организация кода</h2>
<h3>Сборка</h3>
<p>Первое, чем стоит обзавестись перед разработкой сложного JavaScript-приложения, это системой его сборки. С точки зрения клиентской оптимизации весь <a href="/tag/javascript/" target="_blank">JavaScript</a>-код по возможности должен быть минифицирован и собран в один файл, подключенный в конце <a href="/tag/html/" target="_blank">HTML</a>, желательно асинхронно. Работать с ним в таком виде невозможно, соответственно надо иметь возможность легко собирать его из набора красиво отформатированных файлов, используемых при разработке.</p>
<p>На вопрос <em>&laquo;Какую систему сборки использовать?&raquo;</em> в большинстве случаев правильный ответ: ту же, что и для сборки серверной части. Make, rake, maven, ant, rebar&#8230;&nbsp;&mdash; любому из них без труда можно поручить эту задачу. Для конкатенации можно использовать хоть консольную команду <strong>&gt;&gt;</strong>, для минимизации есть много альтернативных библиотек, в порядке моих симпатий:</p>
<ul>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://developers.google.com/closure/compiler/"  target="_blank"><strong>Google Closure</strong></a></li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://developer.yahoo.com/yui/compressor/"  target="_blank"><strong>YUI Compressor</strong></a></li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/mishoo/UglifyJS"  target="_blank"><strong>UglifyJS</strong></a></li>
</ul>
<p>Если хочется чего-то более гибкого, могу порекомендовать воспользоваться <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/miracle2k/webassets"  target="_blank"><strong>Webassets</strong></a>, который я уже упоминал в статье про <a href="http://www.insight-it.ru/programmirovanie/python/jinja2/"  target="_blank">Jinja2</a>. В консольном режиме прекрасно подключается к любой системе сборки и языку программирования. Описать процесс сборки <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto//tag/javascript/" target="_blank">JavaScript</a> и <a href="/tag/css/" target="_blank">CSS</a> можно очень подробно и именно так, как считаете нужным, естественно на <a href="/tag/python/" target="_blank">Python</a>. Сопоставимый по возможностям проект из мира <a href="/tag/ruby/" target="_blank">Ruby</a>&nbsp;&mdash; <a href="http://synthesis.sbecker.net/pages/asset_packager"  target="_blank">Asset Packager</a>, наверняка есть много других.</p>
<h3>Читабельный код</h3>
<p>Не знаю как Вы, а я тихо ненавижу <strong>JavaScript</strong> все ~10 лет, которые я с ним знаком. Так как он по сути является монополией на рынке браузерных приложений (Flash, Java апплеты и ActiveX за альтернативы можно даже не считать), использовать его так или иначе приходится в любом сколько-либо серьезном интернет-проекте. Даже Google Dart вряд ли всерьез приживется.</p>
<p>При полном отсутствии конкуренции совершенно не удивительно, что у него никуда не годящийся синтаксис и набор не знаю откуда взявшихся абстракций в виде прототипов и замыканий. С этим всем определенно можно мириться и работать, особенно если только им и заниматься, но привыкший к серверным языкам программирования мозг определенно чувствует себя не комфортно.</p>
<p>Если Вас тоже не раз посещали подобные мысли, то Вы вероятно как и я при первой же возможности пересядете (или уже пересели) на <a href="/tag/coffeescript/" target="_blank"><strong>CoffeeScript</strong></a>, компилируемый в JavaScript язык программирования. Немного рекламы этого проекта:</p>
<ul>
<li>Золотое правило CoffeeScript: <strong>&laquo;It&#39;s just Javascript&raquo;</strong> <em>(это просто JavaScript)</em></li>
<li>Прямое преобразование кода в JavaScript</li>
<li>Доступны абсолютно все JavaScript-библиотеки</li>
<li>Никаких точек с запятой в конце каждой строки</li>
<li>Структурирование кода на основе отступов, как в Python</li>
<li>Объявление функций просто стрелочкой <strong>-&gt;</strong></li>
<li>При вызове методов даже скобки писать не обязательно</li>
<li>Человеческое наследование: простое <strong>class MyClass extends MyParent</strong> превращается в довольно хитрую конструкцию с использованием прототипов и замыканий:</li>
</ul>
<p><code>MyClass = (function() {<br />
__extends(MyClass, MyParent);<br />
function MyClass() {<br />
MyClass.__super__.constructor.apply(this, arguments);}<br />
MyClass.prototype.initialize = function() {};<br />
return MyClass;<br />
})();</code></p>
<ul>
<li>Много укороченных команд ветвления кода (<strong>if</strong>, <strong>switch, </strong>циклы и т.п.)</li>
<li>В целом код выходит раза в полтора-два короче и намного приятнее для глаз</li>
<li>Консольный компилятор с функцией наблюдения за директориями</li>
<li>Легко подключается как фильтр в Webassets</li>
<li>Подробнее с примерами на <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://coffeescript.org/"  target="_blank">официальном сайте</a></li>
<li>В общем рекомендую <img src='http://www.insight-it.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </li>
</ul>
<h3>Логическое разделение кода</h3>
<p>Если Вы сталкивались со сколько-либо сложным пользовательским интерфейсом &laquo;на jQuery&raquo;, то скорее всего не по наслышке понимаете откуда взялось выражение <strong>&laquo;спагетти-код&raquo;</strong>. В связи с событийной парадигмой разработки браузерных приложений, очень часто JavaScript-код с использованием jQuery или альтернатив превращается в так называемый <em>&laquo;коллбек на коллбеке, коллбеком погоняет&raquo;</em> (коллбек&nbsp;&mdash; транслит от английского callback&nbsp;&mdash; обработчик события). При отсутствии четкой структуры такой код становится очень сложно поддерживать при его увеличении в объемах. Но это не повод отказываться от jQuery&nbsp;&mdash; от событий никуда не деться, и эта библиотека отлично справляется с абстракций от особенностей их реализации в различных браузерах.</p>
<p>На мой взгляд, одним из наиболее резонных способов решения (или заблаговременного предотвращения) этой проблемы является использование в разумных пределах <a href="/tag/oop/">объектно-ориентированные</a> возможности <strong>JavaScript</strong> <em>(благо CoffeeScript это дело сильно упрощает)</em>. Соответственно, используемые классы можно разумно располагать в какой-то иерархии с точки зрения наследования (для обеспечения DRY, don&#39;t repeat yourself&nbsp;&mdash; &laquo;не повторяйся&raquo;) и с точки зрения расположения в файловой системе (с разложенными по папкам файлами работать намного проще, чем с здоровенной вереницей обработчиков событий в одном файле).</p>
<p>Собственно никто не мешает начать заворачивать код в классы на пустом месте, но я позволю себе предложить немного более элегантное решение, которое помимо организации кода пригодится нам и в дальнейшем. Подобно серверным фреймворкам, для клиентских приложений есть библиотеки, предоставляющие базовые классы для решения типичных задач:</p>
<ul>
<li><strong>Модель</strong> <em>(Model)</em>&nbsp;&mdash; как и в традиционном MVC представляет собой класс, объект которого содержит локальную копию каких-то данных и предоставляет механизмы для её синхронизации с внешним хранилищем. Основное отличие от серверных моделей&nbsp;&mdash; хранилищем выступает не СУБД, а либо локальное хранилище браузера через HTML5, либо удаленный сервис через REST или другой интерфейс. Плюс так как они находятся вне &laquo;зоны доверия&raquo;, то полученные от них данные нужно обязательно валидировать, фильтровать и проверять на серверной стороне, прежде чем что-либо с ними делать.</li>
<li><strong>Представление</strong> <em>(View)</em> или <strong>контроллер</strong> <em>(Controller)</em>&nbsp;&mdash; тут, по моим впечатлениям, образовалась путаница и за обоими названиями в нашем контексте имеют ввиду примерно одно и то же. Объект такого класса следит за изменениями и событиями в связанных с ним моделях и элементах DOM, каким-либо образом на них реагируя. Таким образом большая часть кода, которая раньше была &laquo;вереницей обработчиков событий&raquo;, оказывается методами этого класса. При этом базовый класс из библиотеки берет на себя нормальное поведение <strong>this</strong> и следит за тем, чтобы обработчики автоматически добавлялись на динамические созданные элементы DOM.</li>
<li><strong>Маршрутизатор</strong> <em>(Router)</em> - следит за состоянием адресной строки и позволяет обрабатывать изменения, понадобится для восстановления поведения браузера.</li>
<li><strong>Коллекция</strong> <em>(Collection)</em>&nbsp;&mdash; отсортированный набор однотипных моделей, с которым можно работать как с единым целым.</li>
</ul>
<div>Не стоит рассматривать эти абстракций как единственный верный способ делать клиентские приложения, но при их использовании появляется хоть какая-то логика и становится более-менее понятно где какой кусок кода должен находиться и где его потом искать. Для абстракции особенностей реализаций браузеров они по-прежнему полагаются на <strong>$</strong> в виде <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://jquery.com/"  target="_blank">jQuery</a> или <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://zeptojs.com/"  target="_blank">Zepto</a>.</div>
<p>Мне известны три библиотеки, предоставляющие большую часть изложенных выше абстракций. Вкратце о каждой:</p>
<ul>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://documentcloud.github.com/backbone/"  target="_blank"><strong>Backbone.js</strong></a> - самая широко распространенная из трех, используется во многих серьезных проектах. Основана на библиотеке <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://documentcloud.github.com/underscore/"  target="_blank">Underscore.js</a>, которая с одной стороны предоставляет массу удобных функций и шаблонизатор, но с другой стороны&nbsp;&mdash; не особо-то и часто они оказываются нужны.</li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://spinejs.com/"  target="_blank"><strong>Spine.js</strong></a> - библиотека по-моложе, которая очень похожа на Backbone.js, но написана на CoffeeScript и из-за отсутствия внешних зависимостей вышла компактнее. Отличия в основном в терминологии и деталях реализации.</li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://knockoutjs.com/"  target="_blank"><strong>Knockout.js</strong></a>&nbsp;&mdash; эта библиотека пропагандирует использование data-* атрибутов из HTML5 для хранения метаданных, которые как-то управляют изменениями тегов-владельцев при определенных событиях, практически забирая на себя роль представления. Концепция кажется мне мутноватой, так что лично для себя я её использование всерьез и не рассматривал никогда.</li>
</ul>
<p>Когда в этой статье дело будет доходить до примеров кода, я буду приводить их на основе <strong>Backbone.js</strong>, так как в свое время я остановился именно на ней. Почему? В основном из-за того, что она используется в очень многих проектах и стоит за ней целая компания, а не просто один разработчик, которому однажды может надоесть поддерживать проект (как в случае с Spine.js). Но в глубине души я, конечно, надеюсь, что однажды они уберут эту жесткую зависимость от Underscore.js, а то и может быть тоже перепишут все на CoffeeScript.</p>
<p>В целом я стараюсь изложить общую концепцию: те же принципы можно реализовать и с использованием альтернатив, и с использованием разрозненных библиотек, решающих более узкие задачи, и вообще с нуля, самостоятельно занимаясь вопросами кроссбраузерности и прочих особенностей современного Интернета. Последний путь, кстати, не настолько уж и безумен, как кажется, крупные компании и интернет-проекты обычно по нему и идут, если человеческие и финансовые ресурсы позволяют.</p>
<h2>Обработчики событий</h2>
<p>В предыдущем разделе мы прилично так отвлеклись от основной темы&nbsp;&mdash; <em>интерактивных сайтов</em>. Это было необходимо для того, чтобы достаточно комплексное JavaScript-приложение в итоге оказалось поддерживаемым и имело хоть какую-то структуру и логику.</p>
<p>Напомню, то, что раньше было просто независимым обработчиком событий становится методом представления (по терминологии Backbone.js). У каждого представления создается &laquo;оглавление&raquo; методов-обработчиков в атрибуте <strong>events</strong>. Наверное многим хотелось бы увидеть какой-то пример кода, но так как статьями с примерами примитивных приложений на Backbone.js пестрит весь Интернет, тратить на это время желания совершенно никакого, сошлюсь на самый популярный: <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://documentcloud.github.com/backbone/docs/todos.html"  target="_blank">список задач TODO</a>, для сравнения <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/maccman/spine.todos"  target="_blank">то же самое на Spine.js</a>. К слову, при использовании <em>CoffeeScript</em> использовать стандартный механизм <strong>Backbone.****.extend ({ &#8230; })</strong> не обязательно, <strong>class MyClass extends Backbone.****</strong> прекрасно делает то же самое.</p>
<p>По мне, так намного интереснее не какие именно события обрабатываются (все равно 90% уникальны для проекта), а как их распределить по разным представлениям. Обычно получается что-то в этом духе:</p>
<ul>
<li><strong>Пользовательское</strong> представление будет модифицировать страницу в тех местах, где оно как-то связано с текущим пользователем: форма авторизации, надпись &laquo;Привет, ****!&raquo;, кнопка выхода и пр. Вероятно, оно будет использовать <em>модель </em>текущего пользователя или в тривиальных случаях просто самостоятельно работать с cookie сессии.</li>
<li>Классы <em>модели</em> и <em>представления</em>, а вероятно и <em>коллекции</em>, понадобятся каждой <strong>логической сущности</strong>, которая каким-либо образом отражается в пользовательском интерфейсе. Это может быть что угодно, например задача в TODO-списке, статья, комментарий&nbsp;&mdash; все зависит от тематики проекта.</li>
<li>Если <strong>навигация</strong> по сайту каким-то образом динамически видоизменяется, то представление понадобится и для нее. Например, часто подсвечивают пункты в глобальной навигации на основе изменений в текущем адресе страницы.</li>
<li>И, последний пункт, который собственно и относится к сегодняшней теме&nbsp;&mdash; одно представление будет общим для всего сайта и будет отвечать за его <strong>интерактивность</strong>. Давайте его рассмотрим подробнее.</li>
</ul>
<p>Для отсутствия перезагрузок браузера внутри сайта, нам нужно переопределить:</p>
<ul>
<li>События клика на ссылки: по содержимому атрибута href нужно определить, что ссылка внутренняя и вызвать &laquo;цепную реакцию&raquo; в других представлениях, чтобы в итоге пользователь увидел то, что должен.</li>
<li>При отправке формы есть два сценария:</li>
<ul>
<li>Обновляется связанная <em>модель</em> и синхронизируется с сервером. В таком сценарии при необходимости можно вообще скрыть кнопку отправки и &laquo;автосохранять&raquo; изменения в модели.</li>
<li>Связанной модели по каким-то причинам нет и нужно просто на основе данных формы что-то сделать, например выполнить поиск по указанной в форме фразе или отправить запрос на авторизацию.</li>
</ul>
</ul>
<p>Для отмены стандартной реакции браузера на события у jQuery есть два основных механизма: <strong>event.preventDefault ()</strong> и <strong>return false</strong>. В данной ситуации (да и большинстве других), целесообразнее пользоваться последним, так как если вдруг в коде обработчика окажется какая-то ошибка, то пользователь просто увидит стандартную реакцию браузера, а не окажется в ситуации &laquo;некликающихся ссылок&raquo; и &laquo;неотправляющихся форм&raquo;.</p>
<h2>Восстановление поведения браузера</h2>
<p>В предыдущем разделе я даже не стал подробно останавливаться на том, как сделать так &laquo;чтобы пользователь увидел то, что должен&raquo;. Наверняка можно придумать массу способов решения этой задачи, но единственный реально применимый на практике&nbsp;&mdash; воспроизвести визуально то же самое, что происходит при обычной перезагрузке страницы.</p>
<p>И первое, с чего стоит начать&nbsp;&mdash; с адресной строки, именно там должен появиться тот адрес, который был в href ссылки и action формы. Но на самом деле проще сказать, чем сделать:</p>
<ul>
<li>Возможность просто полностью поменять текущий адрес в адресной строке из JavaScript без инициализации открытия страницы есть только в совсем свежих браузерах посредством <strong>HTML5 History API</strong> (<a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history#The_pushState().C2.A0method"  target="_blank">pushState</a>).</li>
<li>В старых браузерах переходы между внутренними страницами сайта можно эмулировать через изменения якоря ссылки, который в URL идет после <strong>#</strong> и обычно используется для &laquo;перелистывания&raquo; на середину HTML-документа. Для отслеживания таких изменений используется событие <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://developer.mozilla.org/en/DOM/window.onhashchange"  target="_blank">onhashchange</a>.</li>
<li>В еще более старых браузерах это событие эмулируют разными трюками с iframe и setInterval.</li>
</ul>
<p><strong>Backbone.history.start ()</strong> берет на себя абстракцию изменений в адресной строке, правда поддержку <em>pushState</em> нужно явно включить в аргументах. Заодно восстанавливается нормальное поведение кнопок &laquo;Назад&raquo; и &laquo;Вперед&raquo; в браузере.</p>
<p>Для обработки и создания событий, отражающихся в адресной строке, нужно сделать подкласс <strong>Backbone.Router</strong>. C ситуациями когда их имеет смысл создать несколько, я не сталкивался. По аналогии с серверными фреймворками в атрибуте <strong>routes</strong> задается соответствие паттернов адресов к методам-обработчикам, которые будут выполниться при переходе. В них вызываются необходимые изменения в коллекциях, моделях и представлениях, чтобы привести в нужное состояние текущий документ.</p>
<p>Для инициации &laquo;виртуального&raquo; перехода на новую внутреннюю страницу нужно вызвать метод <strong>navigate</strong> у нашего объекта-маршрутизатора, первым аргументом передав её адрес без первого /, а вторым&nbsp;&mdash; настройки:</p>
<ul>
<li><strong>trigger</strong>&nbsp;&mdash; вызывать ли обработчик из маршрутизатора?</li>
<li><strong>replace</strong>&nbsp;&mdash; добавлять ли страницу, с которой мы уходим в историю браузера, чтобы можно было на нее вернуться при нажатии кнопки &laquo;назад&raquo;?</li>
</ul>
<div class="pretty">
<p>Таким образом, во внутренних ссылках мы используем нормальные относительные URL, начинающиеся с /. По ним будут нормально ходить роботы и браузеры без JavaScript. В обработчике кликов на них мы:</p>
<ul>
<li>проверяем правда ли она внутренняя (начинается ли она с /);</li>
<li>&laquo;отменяем&raquo; стандартный переход, вернув <strong>false</strong>;</li>
<li>вызываем <strong>router.navigate (href.substring (1), {trigger: true})</strong>.</li>
</ul>
</div>
<p>Осталась еще несколько атрибутов поведения браузера, которые необходимо починить, чтобы визуально все выглядело &laquo;как обычно&raquo;:</p>
<ul>
<li>Клик по ссылке с зажатым Shift должен открывать её <em>в новом окне</em>, а с зажатым Ctrl или при клике средней кнопкой мыши&nbsp;&mdash; <em>в новой вкладке</em>. Довольно не хитро делается на основе атрибутов объекта-события, который передает обработчику jQuery (button, shiftkey, metakey), для открытия окна или вкладки&nbsp;&mdash; window.open.</li>
<li>Если пользователь сделал какое-то действие, а прореагировать на него мгновенно не получается (так как что-то грузится, вероятно)&nbsp;&mdash; нужно включить курсор ожидания, установив в CSS <strong>cursor: wait</strong>, и, желательно, анимированный <strong>favicon.ico</strong>. И, соответственно, вернуть все как было, когда страница примет нужный вид. Для смены favicon до сих пор пользуюсь каким-то довольно старым плагином к jQuery, который не особо шикарно, но все же работает. Его сайт, видимо, накрылся, так что продублировал: <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://gist.github.com/2320740" >https://gist.github.com/2320740</a>, если кто знает более адекватные альтернативы&nbsp;&mdash; дайте знать в комментариях, пожалуйста, руки поискать все никак не доходят.</li>
</ul>
<h2>Синхронизация состояния</h2>
<p>По-умолчанию Backbone.js предлагает хранить все состояние клиента в моделях и синхронизировать его с серверным посредством реализации простенького REST API на сервере (<a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://documentcloud.github.com/backbone/#Sync"  target="_blank">подробнее</a>), к которому запросы отправляются посредством обычного <strong>$.ajax</strong>. Чтобы инициировать процесс нужно вручную вызвать у экземпляра модели метод <strong>fetch</strong>, чтобы обновить клиентское состояние данными с сервера, или метод <strong>save</strong>, для обратного процесса.</p>
<p>Для многих приложений этого, в целом, достаточно. Но ограничение очевидно&nbsp;&mdash; нет возможности мгновенно узнать, что на сервере что-то изменилось. Чего-то близкого можно достичь вызовом <strong>fetch</strong> раз в N секунд для каждой модели, но если пользователей предполагается хоть сколько-либо много, нагрузка на серверную часть будет неоправданно велика.</p>
<p>Резонным дополнением этой схемы является использование постоянного соединения между клиентом и сервером для синхронизации их состояний. Именно это мы и обсудим в следующей статье серии.</p>
<div class="pretty">Эта статья&nbsp;&mdash; вторая в <a href="http://www.insight-it.ru/interactive/"  target="_blank">серии про Интерактивные сайты</a>, автор&nbsp;&mdash; <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://blinkov.ru"  target="_blank">Иван Блинков</a>, основано на личном опыте, внешние источники информации при написании не использовались.До встречи <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://feeds.feedburner.com/insight-it/feed"  target="_blank">на страницах Insight IT</a>!</p>
</div>
<p>&nbsp;</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=JKua9Wu43Nw:wBV7V8SC70s:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=JKua9Wu43Nw:wBV7V8SC70s:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=JKua9Wu43Nw:wBV7V8SC70s:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=JKua9Wu43Nw:wBV7V8SC70s:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=JKua9Wu43Nw:wBV7V8SC70s:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/JKua9Wu43Nw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/javascript/klientskaya-chast-interaktivnogo-sajjta/feed/</wfw:commentRss>
		<slash:comments>30</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/programmirovanie/javascript/klientskaya-chast-interaktivnogo-sajjta/</feedburner:origLink></item>
		<item>
		<title>Архитектура интерактивных сайтов</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/5b3ZoSm3XGg/</link>
		<comments>http://www.insight-it.ru/masshtabiruemost/arkhitektura-interaktivnykh-sajjtov/#comments</comments>
		<pubDate>Tue, 03 Apr 2012 21:05:00 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Масштабируемость]]></category>
		<category><![CDATA[архитектура]]></category>
		<category><![CDATA[Интерактивные сайты]]></category>
		<category><![CDATA[клиентская часть]]></category>
		<category><![CDATA[серверная часть]]></category>
		<category><![CDATA[схема]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1793</guid>
		<description><![CDATA[В анонсе серии статей &#171;Интерактивные сайты&#187; я постарался максимально доходчиво изложить свою мотивацию к ей созданию, да и актуальность самой темы, так что сразу к делу! Итак, мы хотим сделать так, чтобы с точки зрения пользователя наш сайт выглядел интерактивным. То есть воспринимался не как набор отдельно загружающихся страниц, а скорее как обычное приложение для компьютера. [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/vUcfziKGGxdqnQFfKgOsw4aChQU/0/da"><img src="http://feedads.g.doubleclick.net/~a/vUcfziKGGxdqnQFfKgOsw4aChQU/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/vUcfziKGGxdqnQFfKgOsw4aChQU/1/da"><img src="http://feedads.g.doubleclick.net/~a/vUcfziKGGxdqnQFfKgOsw4aChQU/1/di" border="0" ismap="true"></img></a></p><p>В <a href="http://www.insight-it.ru/masshtabiruemost/anons-serii-statejj-interaktivnye-sajjty/"  target="_blank">анонсе</a> серии статей &laquo;<a href="/interactive/" target="_blank">Интерактивные сайты</a>&raquo; я постарался максимально доходчиво изложить свою мотивацию к ей созданию, да и актуальность самой темы, так что сразу к делу!<span id="more-1793"></span></p>
<p>Итак, мы хотим сделать так, чтобы с точки зрения <em>пользователя</em> наш сайт выглядел <strong>интерактивным</strong>. То есть воспринимался не как набор отдельно загружающихся страниц, а скорее как обычное приложение для компьютера. Ему, в целом, не важно как именно мы этого добьемся, главное чтобы при этом браузер вел себя как обычно и визуально все реагировало почти мгновенно.</p>
<p>Сразу хочу предупредить, что далеко не всем сайтам такая глобальная <em>интерактивность</em> пойдет на пользу. Если пользователи сайта редко что-либо делают и большую часть времени читают длинные тексты, то, вероятно, этот проект как раз из этой категории: быстрота реакции на клик не так радует, когда большую часть времени приходится работать скроллом. Если Ваш интернет-проект является сайтом с длинным контентом в формате блога, новостей или вики&nbsp;&mdash; подумайте лишний раз, прежде чем переделывать весь сайт, вероятно будет достаточно интерактивных комментариев и голосований на <a href="/tag/ajax/" target="_blank">AJAX</a>. Для социальных сетей, сайтов знакомств, интернет-магазинов, поисковых систем, корпоративных сайтов и многих других визуально заметная <em>интерактивность</em> определенно станет одним из ключевых <strong>конкурентных преимуществ</strong>.</p>
<p>В этом посте я постараюсь нарисовать крупными мазками картину того, как этого можно достичь. Визуально её я представил следующим образом:</p>
<p><img class="aligncenter size-full wp-image-1802" title="Архитектура интерактивного сайта" src="http://www.insight-it.ru/wp-content/uploads/2012/04/interactive-site-architecture.jpeg" alt="Архитектура интерактивного сайта" width="630" height="759" /></p>
<h2>Общие замечания</h2>
<ul>
<li>Здесь и далее я буду описывать &laquo;среднестатистический&raquo; интернет-проект, в зависимости от специфики могут появляться дополнительные компоненты или убираться за ненадобностью упомянутые. Например, если известно, что пользоваться сайтом будут только сотрудники какой-то компании, то помимо замены Интернета на Интранет, можно запросто избавиться и от <strong>HTML-интерфейса</strong> и всего, что с ним связано&nbsp;&mdash; никаким роботам доступ к нему не нужен.</li>
<li>Наверное стоит прямым текстом сказать, что:</li>
<ul>
<li>Голубые элементы&nbsp;&mdash; компоненты проекта, а серые&nbsp;&mdash; внешние.</li>
<li>Связи в виде прямых линий означают двусторонний обмен данными, транспорт и формат пока особо не важны.</li>
<li>Схема логическая, так что вопросы балансировки нагрузки, отказоустойчивости, репликации и т.п. остались в стороне; за каждым блоком может стоять произвольное количество серверов.</li>
</ul>
<li>Я решил не загромождать схему мониторингом, резервным копированием, почтой, сервисом доменов и прочими хоть и важными, но не связанными напрямую с темой компонентами системы.</li>
<li>В этом посте не будет никаких конкретных примеров реализации и технологий, оставим это на <a href="/interactive/" target="_blank">следующие статьи</a>.</li>
</ul>
<h2>Клиентская часть</h2>
<ul>
<li>Начнем наше путешествие. Пользователь вводит в адресной строке браузера адрес нашего сайта и жмет Enter, инициируя тем самым сначала определение IP-адреса через DNS, а затем и HTTP-запрос к нашему <strong>HTML-интерфейсу</strong>.</li>
<li>Получив в ответ <strong>страницу</strong> в формате <a href="/tag/html/" target="_blank">HTML</a> браузер начинает загружать указанные в нем внешние ресурсы, в том числе и <strong>Javascript-клиент</strong>, которому и передается слово. Сама страница параллельно отрисовывается как и в статичном сайте.</li>
<li>Как уже упоминалось выше, некоторые проекты решают не тратить силы на поддержку двух интерфейсов к сайту, жертвуя тем самым доступом большинства роботов и браузеров без поддержки <a href="/tag/javascript/" target="_blank">JavaScript</a>. Стартовый HTML-документ в этом случае превращается просто в практически пустой статичный файл, который служит лишь для загрузки клиента.</li>
<li>Так как наша цель стоит в интерактивном взаимодействии пользователем, повторять эти действия при каждом переходе на другую страницу&nbsp;&mdash; <em>непозволительная роскошь</em>. Кстати, можно начинать думать и общаться не в терминах страниц, а в терминах <strong>экранов</strong>, которые видит пользователь.</li>
<li>Для обеспечения этого <strong>JavaScript-клиент</strong> должен переопределить стандартные обработчики событий перехода по внутренним ссылкам сайта и отправки форм. Ему нужно отменить стандартный механизм перехода на другую страницу и вместо него отправить запрос через <strong>интерфейс сериализованных данных</strong>. На основе полученного в ответ сообщения он меняет какую-то часть загруженного ранее HTML-документа, чтобы он соответствовал тому <em>экрану</em>, который ожидал увидеть пользователь. В итоге пользователь видит в браузере ровно ту же картинку, как если бы он ввел тот адрес, на который вела нажатая ссылка, или просто нажал на нее с отключенным JavaScript.</li>
<li>Важно не сломать при этом поведение браузера: кнопка &laquo;назад&raquo; должна работать как обычно, а в адресной строке должны меняться ссылки (это актуально, например, когда пользователь отправляет содержимое адресной строки кому-то по почте или через мессенджер).</li>
<li>При ожидании ответа от сервера стоит эмулировать курсор и иконку загрузки страницы, чтобы пользователь <em>не паниковал</em> в случае (пускай и редком) визуально заметных задержек.</li>
<li>На резонный вопрос <em>&laquo;Почему, собственно, этот трюк обеспечит интерактивность?&raquo;</em>, ответ хоть и не всегда однозначен, но он все же есть:</li>
<ul>
<li>Сериализованные <strong>изменения</strong> страницы занимают меньший объем, чем <strong>полный</strong> HTML со всеми связанными ресурсами, заголовками, версткой и прочим&nbsp;&mdash; <em>значительно меньше данных нужно передавать по сети</em>.</li>
<li>Как правило, есть возможность держать <strong>постоянное</strong> соединение между браузером и интерфейсом сериализованных данных, что позволяет <em>не делать лишние HTTP-запросы</em>. Обратная сторона медали&nbsp;&mdash; это самое соединение постоянно же использует часть серверных ресурсов, но есть способы минимизировать эти издержки.</li>
<li>Для некоторых действий изменения HTML не требуют ответа сервера и могут быть сделаны параллельно с отправкой запроса (например  различные вариации на тему +1 или написание комментария, текст которого можно взять из формы).</li>
<li>Как правило, можно предсказать наиболее вероятные переходы по экранам и <em>загрузить необходимые изменения заранее</em>. Хотя этот вопрос скорее из области оптимизации.</li>
<li><strong>Таким образом, в большинстве случаев есть техническая возможность снизить время отклика  на действие пользователя с 500-2000 мс в случае неплохо сделанного статического сайта до 20-200 мс., что вполне сопоставимо с откликом десктопного приложения.</strong></li>
</ul>
<li>Как все это сделать на практике&nbsp;&mdash; тема следующей статьи из серии.</li>
</ul>
<h2>Серверная часть</h2>
<ul>
<li>С серверной точки зрения основным отличием является четкое разделение двух входных точек:</li>
<ul>
<li><strong>HTML-интерфейс</strong> отдает готовые документы в ответ на HTTP-запросы.</li>
<li><strong>Интерфейс сериализованных данных</strong> использует какое-то постоянное соединение, хотя в некоторых случаях целесообразно ограничиться просто асинхронными HTTP-запросами.</li>
</ul>
<li>Если для статичных сайтов полное <em>выделение бизнес-логики в отдельные сервисы</em>&nbsp;&mdash; просто хорошее архитектурное решение, то для интерактивных сайтов&nbsp;&mdash; это практически <strong>необхохимо</strong>. Иначе придется реализовывать и поддерживать две копии кода для каждого интерфейса и надеяться, что они постоянно будут оставаться совместимыми и выдавать одинаковый результат.</li>
<li>Хорошей практикой является использование какого-то одного протокола общения между компонентами системы, в частности пользовательских интерфейсов с сервисами бизнес-логики и последних друг с другом. Желательно использовать что-то бинарное и с поддержкой разных языков программирования, хотя если весь проект на одной платформе и не планирует это менять&nbsp;&mdash; можно использовать и стандартный для этой платформы протокол.</li>
<li>Чтобы не включать элементы верстки при передаче через интерфейс сериализованных данных, рекомендую использовать кроссплатформенный формат HTML-шаблонов. Об этом будет отдельная статья.</li>
<li><strong>Интерфейс сериализованных данных</strong> при необходимости легко может быть адаптирован для использования в роли <em>API для сторонних сервисов или собственных приложений</em> для мобильных платформ или настольных компьютеров.</li>
<li>В целом внутренние сервисы общаются с остальными располагающимися на серверной части компонентами системы вполне обычным образом.</li>
<li>В статье про серверную часть подробно будет рассматриваться использование брокера сообщений для уведомлений пользователей в реальном времени.</li>
</ul>
<h2>Подводим итоги</h2>
<ul>
<li><em>Глобальная интерактивность</em> сайта требует использования достаточно сложного и комплексного JavaScript-клиента и создания дополнительного более легковесного внешнего инетрфейса на серверной части.</li>
<li>По-настоящему <em>мгновенной</em> реакцией сайта смогут насладиться лишь пользователи с <strong>современным браузером</strong> и относительно <strong>широким интернет-каналом</strong>. Из-за возможных сетевых задержек или особенностей устаревших браузеров эффект мгновенного перехода все же может теряться, но при должном тестировании реально добиться нормального поведения сайта и в таких ситуациях. Хотя зачастую &laquo;проваливание&raquo; до обычного режима статичных страниц в подобных ситуациях&nbsp;&mdash; вполне резонное решение.</li>
<li>Архитектура серверной части проекта в большинстве случаев не требует существенных изменений. Хотя если в ней все было хаотично и не продумано, то создание интерактивного клиента может послужить неплохим поводом пересмотреть и привести в порядок и её.</li>
<li>Кроме очевидной потребности в использовании JavaScript для клиента, особых ограничений на используемые технологии и языки программирования, обсуждаемая схема не накладывает.</li>
</ul>
<div class="pretty">Эта статья&nbsp;&mdash; первая в <a href="http://www.insight-it.ru/interactive/"  target="_blank">серии про Интерактивные сайты</a>, автор&nbsp;&mdash; <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://blinkov.ru"  target="_blank">Иван Блинков</a>, основано на личном опыте, внешние источники информации при написании не использовались.</p>
До встречи <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://feeds.feedburner.com/insight-it/feed"  target="_blank">на страницах Insight IT</a>!</div>
<p>&nbsp;</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=5b3ZoSm3XGg:Y6LaeIEikWU:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=5b3ZoSm3XGg:Y6LaeIEikWU:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=5b3ZoSm3XGg:Y6LaeIEikWU:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=5b3ZoSm3XGg:Y6LaeIEikWU:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=5b3ZoSm3XGg:Y6LaeIEikWU:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/5b3ZoSm3XGg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/masshtabiruemost/arkhitektura-interaktivnykh-sajjtov/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/masshtabiruemost/arkhitektura-interaktivnykh-sajjtov/</feedburner:origLink></item>
		<item>
		<title>Анонс серии статей: интерактивные сайты</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/Jnr43z_kbJE/</link>
		<comments>http://www.insight-it.ru/masshtabiruemost/anons-serii-statejj-interaktivnye-sajjty/#comments</comments>
		<pubDate>Sun, 01 Apr 2012 13:43:16 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Масштабируемость]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[архитектура]]></category>
		<category><![CDATA[интерактив]]></category>
		<category><![CDATA[интернет-приложения]]></category>
		<category><![CDATA[клиентская оптимизация]]></category>
		<category><![CDATA[сайты]]></category>
		<category><![CDATA[технологии]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1782</guid>
		<description><![CDATA[Интернет развивается огромными темпами. В борьбе за аудиторию крупные интернет-компании поднимают стандарты качества веб-приложений на все более и более высокий уровень. Одним из важнейших качеств современных сайтов является интерактивность, если раньше все они поголовно представляли собой коллекцию статичных страниц, где можно что-то почитать или посмотреть, то сегодня они&#160;&#8212; почти живой организм. Пользователи все больше привыкают [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/ifIE-BO7Pw0klEvAN2S1e8Qzio4/0/da"><img src="http://feedads.g.doubleclick.net/~a/ifIE-BO7Pw0klEvAN2S1e8Qzio4/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/ifIE-BO7Pw0klEvAN2S1e8Qzio4/1/da"><img src="http://feedads.g.doubleclick.net/~a/ifIE-BO7Pw0klEvAN2S1e8Qzio4/1/di" border="0" ismap="true"></img></a></p><p>Интернет развивается огромными темпами. В борьбе за аудиторию крупные интернет-компании поднимают стандарты качества веб-приложений на все более и более высокий уровень. Одним из важнейших качеств современных сайтов является <strong>интерактивность</strong>, если раньше все они поголовно представляли собой коллекцию статичных страниц, где можно что-то почитать или посмотреть, то сегодня они&nbsp;&mdash; почти живой организм.</p>
<p>Пользователи все больше привыкают узнавать о событиях и видеть реакцию на свои действия мгновенно, не дожидаясь загрузок страниц и прочих задержек. Раньше  это было возможно только для обычных приложений, но с сегодняшним уровнем технологий общаться с пользователем в реальном времени можно и <strong>посредством браузера</strong>, причем доступно это не только интернет-гигантам, а практически любому интернет-проекту.</p>
<p>За последний год <em>привнесение интерактивности в интернет-проекты</em>&nbsp;&mdash; пожалуй, одна из самых популярных тем, с которой ко мне <a href="http://www.insight-it.ru/consulting/"  target="_blank">обращаются за консультацией</a>.  В итоге я решил не жадничать и поделиться с общественностью своими знаниями в этой области, что в итоге должно вылиться в серию связанных статей <strong>&laquo;Интерактивные сайты&raquo;</strong>. В ней я хочу отразить практически пошаговую инструкцию от А до Я для создания интерактивного интернет-приложения с нуля или основываясь на существующем статичном проекте. Соответственно, по ходу дела сделаю легко доступное оглавление по аналогии с <a href="http://www.insight-it.ru/highload/"  target="_blank">архитектурой высоконагруженных интернет-проектов</a>.</p>
<h3>Ориентировочные темы статей</h3>
<ul>
<li><a href="http://www.insight-it.ru/masshtabiruemost/arkhitektura-interaktivnykh-sajjtov/"  target="_blank">Общая архитектура</a></li>
<li><a href="/programmirovanie/javascript/klientskaya-chast-interaktivnogo-sajjta/" target="_blank">Организация клиентской части</a></li>
<li><a href="http://www.insight-it.ru/tekhnologii/soobshheniya/postoyannoe-soedinenie-mezhdu-brauzerom-i-serverom/"  target="_blank">Постоянное соединение между браузером и сервером</a></li>
<li><a href="http://www.insight-it.ru/programmirovanie/javascript/povtornoe-ispolzovanie-shablonov/" >Повторное использование шаблонов</a></li>
<li>Серверная часть и потоки сообщений</li>
<li>Оптимизация</li>
</ul>
<p>Основной упор будет сделан именно на общую концепцию и сведение всех компонентов воедино, как на серверной стороне, так и на клиентской. Количество изобретаемых велосипедов постараюсь свести к минимуму: где-то будут просто рекомендации по использованию публично доступных технологий, где-то&nbsp;&mdash; сравнительные обзоры. Специфики каких-либо определенных типов проектов постараюсь избегать.</p>
<div class="pretty"><strong>Пожелания, предложения, советы и вопросы в комментариях к этому посту очень приветствуются <img src='http://www.insight-it.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </strong></div>
<p>&nbsp;</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=Jnr43z_kbJE:5ecOb-NriMI:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=Jnr43z_kbJE:5ecOb-NriMI:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=Jnr43z_kbJE:5ecOb-NriMI:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=Jnr43z_kbJE:5ecOb-NriMI:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=Jnr43z_kbJE:5ecOb-NriMI:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/Jnr43z_kbJE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/masshtabiruemost/anons-serii-statejj-interaktivnye-sajjty/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/masshtabiruemost/anons-serii-statejj-interaktivnye-sajjty/</feedburner:origLink></item>
		<item>
		<title>Twitter Storm</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/zDKSxnF8i94/</link>
		<comments>http://www.insight-it.ru/tekhnologii/soobshheniya/twitter-storm/#comments</comments>
		<pubDate>Fri, 30 Mar 2012 20:08:54 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Сообщения]]></category>
		<category><![CDATA[Clojure]]></category>
		<category><![CDATA[JVM]]></category>
		<category><![CDATA[opensource]]></category>
		<category><![CDATA[real time]]></category>
		<category><![CDATA[Storm]]></category>
		<category><![CDATA[Thrift]]></category>
		<category><![CDATA[Twitter]]></category>
		<category><![CDATA[Twitter Storm]]></category>
		<category><![CDATA[ZeroMQ]]></category>
		<category><![CDATA[ZooKeeper]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1768</guid>
		<description><![CDATA[Storm является распределенной системой для выполнения вычислений в реальном времени. Она родилась в рамках проекта Backtype, который специализировался на аналитике твитов и который в июле 2011 был приобретен Twitter.  Так же как Apache Hadoop предоставляет набор базовых абстракций, инструментов и механизмов для пакетной обработки данных, Twitter Storm делает это для задачи обработки данных в режиме [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/lYakCS7tgtM2nmMw-6lTFd81jIM/0/da"><img src="http://feedads.g.doubleclick.net/~a/lYakCS7tgtM2nmMw-6lTFd81jIM/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/lYakCS7tgtM2nmMw-6lTFd81jIM/1/da"><img src="http://feedads.g.doubleclick.net/~a/lYakCS7tgtM2nmMw-6lTFd81jIM/1/di" border="0" ismap="true"></img></a></p><p><em><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/nathanmarz/storm"  target="_blank">Storm</a> является распределенной системой для выполнения вычислений в реальном времени.</em> Она родилась в рамках проекта Backtype, который специализировался на аналитике твитов и который в июле 2011 был приобретен <a href="/tag/twitter/" target="_blank">Twitter</a>.  Так же как <a href="/tag/apache/" target="_blank">Apache</a> <a href="/tag/hadoop/">Hadoop</a> предоставляет набор базовых абстракций, инструментов и механизмов для пакетной обработки данных, <strong>Twitter Storm</strong> делает это для задачи обработки данных <em>в режиме реального времен</em>и. Хотите узнать в чем их отличие?</p>
<p><span id="more-1768"></span></p>
<h2>Отличие</h2>
<p>Не смотря на то, что <em>Storm</em> изначально появился на свет в процессе неудачных попыток приспособить <em>Hadoop</em> к задаче обработки данных в реальном времени, сравнивать их некорректно. Никакой хак или патч не сможет заставить Hadoop работать по-настоящему в режиме реального времени, так как в его основе лежит фундаментально другая концепция и набор принципов, которые актуальны лишь в контексте задачи пакетной обработки данных. <strong>Storm</strong> можно представить как <strong>&laquo;Hadoop для вычислений в реальном времени&raquo;</strong>, но по факту между ними нет практически ничего общего, кроме изначально-распределенной природы, слегка похожей архитектуры, работы внутри JVM и публичной доступности. Для понимания задачи, которая стоит перед Storm, лучше взглянуть на то, как она обычно решается.</p>
<p>Традиционно, если перед проектом или бизнесом вставала задача обработки какой-то информации в реальном времени, то она в итоге сводилась к цепочке преобразований данных и распределялась по серверам, которые их выполняют и передают результаты друг другу посредством сообщений и очередей-посредников. При таком подходе <em>существенная</em> часть времени уходила на маршрутизацию сообщений, настройку и развертывание новых промежуточных очередей и обработчиков, обеспечение отказоустойчивости и надежности. По сути <strong>Storm</strong> берет все вышеперечисленное на себя, позволяя разработчикам сосредоточиться на реализации логики обработки сообщений.</p>
<h2>Особенности</h2>
<p>Итак, основные особенности Storm, вытекающие из требований к подобным системам:</p>
<ul>
<li>Три основных варианта использования, но ими он не ограничивается:</li>
<ul>
<li><strong>Обработка потоков сообщений </strong><em>(stream processing)</em> в реальном времени, с возможностью внесения изменений во внешние базы данных;</li>
<li><strong>Постоянные вычисления</strong> <em>(continuous computation)</em> на основе источников данных с публикацией результатов произвольным клиентам в реальном времени;</li>
<li><strong>Распределенные удаленные вызовы</strong> <em>(distributed RPC)</em> с выполнением комплексных вычислений параллельно во время запроса.</li>
</ul>
<li><strong>Масштабируемость: </strong>Storm может обрабатывать огромное количество сообщений в секунду. Для масштабирование необходимо лишь добавить сервера в кластер и увеличить параллельность в настройках топологии. В одном из первых приложений для Storm обрабатывался 1 миллион сообщений в секунду на кластере из 10 серверов, при этом выполнялось несколько сотен запросов в секунду к внешней базе данных.</li>
<li><strong>Гарантия отсутствия потерь данных:</strong> в отличии от других систем обработки сообщений в реальном времени (например <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://incubator.apache.org/s4/"  target="_blank">S4</a> от Yahoo!) это свойство изначально является частью архитектуры Storm. Для этого используется механизм подтверждения <em>(acknowledgement)</em> успешной обработки каждого конкретного сообщения.</li>
<li><strong>Стабильность:</strong> в то время как <a href="/tag/hadoop/" target="_blank">Hadoop</a> позволительны простои по несколько часов, так как он априори не является системой реального времени, одной из основных целей Storm является стабильная бесперебойная работа кластера, с максимально безболезненным его управлением.</li>
<li><strong>Защита от сбоев:</strong> если что-то пошло не так во время выполнения вычисления, Storm переназначит задачи и попробует снова. В его задачи входит обеспечение бесконечной работы вычислений (или до момента запланированной или ручной остановки).</li>
<li><strong>Независимость от языка программирования:</strong> в то время как большая часть системы написана на <a href="/tag/clojure/" target="_blank">Clojure</a> и работает в <a href="/tag/jvm/" target="_blank">JVM</a>, сами компоненты системы могут быть реализованы на любом языке, что удобно для проектов, использующих в основном другие технологии.</li>
</ul>
<div>У Вас уже могло сложиться общее представление, о том что собой представляет <strong>Twitter Storm</strong> и насколько он актуален лично для Вас или Вашего проекта. Если интерес все еще не погас, предлагаю перейти к концепции, предлагаемой Storm для разработки приложений под эту платформу.</div>
<h2>Концепция</h2>
<p>Для начала пройдемся по основным абстракциям, которые используются в Storm:</p>
<ul>
<li><strong>Поток</strong> <em>(Stream)</em>: неограниченный поток сообщений, представленных в виде кортежей (произвольных именованный список значений). При этом все кортежи в одном потоке должны иметь одинаковую схему: элемент на каждой позиции должен иметь один и тот же тип данных и значение.</li>
<li><strong>Струя воды из крана</strong> <em>(Spout)</em>: источник потоков, который берет их из какой-то внешней системы.</li>
<li><strong>Cтруя состояния</strong> <em>(state spout)</em>: предоставляет распределенный доступ к некому общему состоянию, которое кэшируется в памяти на исполнителях и синхронно обновляется при внешних изменениях. Таким образом возможно избежать обращений к внешней базе данных при обработке каждого сообщения. В случае с Twitter этим общим состоянием является сам социальный граф.</li>
<li><strong>Молния</strong> <em>(Bolt)</em>: обрабатывает входящие потоки и создает исходящие потоки, производя какую-либо обработку данных (по сути здесь реализуется основная бизнес-логика). Помимо этого никто не запрещает использовать при обработке какие угодно внешние сервисы вроде <a href="/tag/subd/" target="_blank">СУБД</a>.</li>
<li><strong>Топология</strong> <em>(Topology)</em>: произвольная связанная сеть из &laquo;молний&raquo; и &laquo;струй&raquo;. При создании топологии можно указать:</li>
<ul>
<li><em>уровень параллелизма</em> для каждого компонента, что создаст необходимое количество его потоков исполнения в кластере.</li>
<li><em>группировку потоков</em>, то есть как именно сообщения будут распределяться между созданными потоками исполнения каждого компонента, есть четыре основных варианта&nbsp;&mdash; случайно <em>(shuffle)</em>, каждый получит по копии <em>(all)</em>, хэш по определенным полям сообщения <em>(fields)</em>, один  поток получает все сообщения  <em>(global)</em>.</li>
</ul>
</ul>
<div>Таким образом, для создания приложения для обработки данных в реальном времени с использованием <strong>Storm</strong>, необходимо:</div>
<div>
<ol>
<li>Определить схему(ы) потока(ов) сообщений.</li>
<li>Реализовать источник(и) сообщений, основанные на парсинге каких-то внешних данных (для Backtype это был Twitter firehose, поток всех твитов) или реакции на события (допустим действия пользователей в виде HTTP-запросов).</li>
<li>Реализовать обработчик(и) сообщений, которые преобразуют входящие сообщения и либо создают новые потоки сообщений, либо как-то влияют на внешний мир, например изменяя что-то в базе данных (они используют <a href="/tag/cassandra/" target="_blank">Cassandra</a> для этого).</li>
<li>Объединить реализованные компоненты в топологию и запустить её на кластере.</li>
<li>При необходимости оптимизировать систему, включив общее состояние в топологию.</li>
</ol>
<div>С точки зрения разработчика приложения большего знать и не нужно, но самое интересное происходит как раз дальше. Что собой представляет Storm-кластер и как с его помощью исполняется реализованное описанным выше способом приложение?</div>
<h2>Архитектура</h2>
<div>Проект очень сильно завязан на <a href="/tag/zookeeper/" target="_blank">Zookeeper</a> для координации работы кластера, с чем он очень неплохо справляется. Все остальные компоненты системы системы не содержат в себе состояния, что обеспечивает их быстрый запуск, даже после kill -9.</div>
</div>
<h2><a href="http://www.insight-it.ru/wp-content/uploads/2012/03/storm-cluster.png" ><img class="aligncenter size-full wp-image-1772" title="storm-cluster" src="http://www.insight-it.ru/wp-content/uploads/2012/03/storm-cluster.png" alt="" width="320" height="259" /></a></h2>
<p>В остальном все достаточно просто:</p>
<ul>
<li>Мастер-сервер <em>(Nimbus)</em> отвечает за распространение кода, распределение задач и мониторинг сбоев.</li>
<li>На каждом сервере в кластере запускается процесс-надсмотрщик <em>(Supervisor)</em>, который запускает локально потоки исполнения, отвечающие за выполнение назначенных ему компонентов топологий.</li>
<li>Передача сообщений между компонентами топологий осуществляется напрямую, посредством <a href="/tag/zeromq/" target="_blank">ZeroMQ</a>.</li>
<li>Топологии являются <a href="/tag/thrift/" target="_blank">Thrift</a>-структурами, а мастер-сервер&nbsp;&mdash; <a href="/tag/thrift/" target="_blank">Thrift</a>-сервисом, что позволяет осуществлять регистрацию топологий и другие операции программно из любого языка программирования.</li>
</ul>
<div>Присутствующий в единственном экземпляре мастер-сервер является единственной точкой отказа лишь на первый взгляд. По факту он используется лишь для внесение изменений в кластер и топологии, так что его непродолжительное отсутствие не повлияет на функционирование запущенных вычислений. А так как состояние кластера хранится в <em>Zookeeper</em>, то запуск мастера на другой машине в случае аппаратного сбоя&nbsp;&mdash; вопрос лишь грамотно настроенного мониторинга и максимум одной минуты.</div>
<div></div>
<div>Используемый механизм подтверждений успешной обработки сообщения <em>(acknowledgement)</em> гарантирует, что все сообщения, попавшие в систему, рано или поздно будут обработаны, даже при локальных сбоях оборудования. Хотя более глобальные катаклизмы вроде &laquo;потери&raquo; стойки все же могут нарушить функционирование системы, про работу в нескольких датацентрах речь также не идет.</div>
<h2>Планы на будущее</h2>
<ul>
<li>Использование <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://incubator.apache.org/mesos/"  target="_blank">Mesos</a> для распределения и изоляции вычислительных ресурсов.</li>
<li>Изменение кода &laquo;на лету&raquo;, сейчас для этого нужно остановить старую топологию и запустить новую, что может означать простой в пару минут.</li>
<li>Автоматическое определения необходимого уровня параллельности и адаптация под изменения в интенсивности входящего потока сообщений.</li>
<li>Еще более высокоуровневые абстракции.</li>
</ul>
<h2>Подводим итоги</h2>
<ul>
<li>На самом деле подход, лежащий в основе Storm, не является чем-то кардинально-новым. Помимо упоминавшегося выше S4 можно найти еще несколько альтернатив, пускай и менее близких по идеологии. Подробнее про эту тему можно узнать погуглив <strong><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.google.com/search?q=complex+event+processing"  target="_blank">complex event processing</a></strong> или <strong><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.google.com/search?q=real-time+stream+processing"  target="_blank">real-time stream processing</a></strong>.</li>
<li><strong>Storm</strong> выделяет из их числа простота, гибкость, масштабируемость и отказоустойчивость в одном флаконе. Обеспечивает это в первую очередь простая и понятная архитектура, основанная на (уже) проверенном временем и многими проектами распределенном координаторе в виде <strong>Zookeeper</strong>.</li>
<li>Хоть за проектом и стоит крупный интернет-проект в лице <a href="http://www.insight-it.ru/masshtabiruemost/arkhitektura-twitter-dva-goda-spustya/"  target="_blank">Twitter</a>, он достаточно молод и нужно быть морально готовым к возможным сбоям и неудачным моментам. Плюс не забывайте, что существенная часть написана на <strong>Clojure</strong>&nbsp;&mdash; для, пожалуй, большинства разработчиков изучение исходников проекта будет капитальным &laquo;выносом мозга&raquo;. Мое первое знакомство с <strong>Lisp</strong> <em>(Clojure&nbsp;&mdash; его диалект, работающий в JVM)</em> надолго засело в памяти из-за обилия скобочек за каждым углом <img src='http://www.insight-it.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </li>
<li>В любом случае из доступных <a href="/tag/opensource/" target="_blank">opensource</a> реализаций систем для распределенных вычислений в реальном времени <strong>Storm</strong> на мой взгляд является наиболее перспективным для применения в интернет-проектах.</li>
<li>Если Вашему проекту нужна лишь одна-две топологии и особо большого кластера не планируется, то подобную схему достаточно не сложно реализовать и просто посредством <strong>Zookeeper</strong> + <strong>ZeroMQ</strong> или альтернативных технологий. Это избавит проект от возможных заморочек с Clojure и другими &laquo;особенностями&raquo; Storm, ценой вероятно существенно большей собственной кодовой базы, которую придется самостоятельно тестировать и поддерживать. Какой путь ближе&nbsp;&mdash; команда каждого проекта решает для себя сама.</li>
<li>Помимо различных вариаций <strong>веб-аналитики</strong> заманчивыми применениями подобной системы в Интернете может стать:</li>
<ul>
<li><strong>построение индекса для поисковых систем</strong>, на сколько я знаю от <a href="http://www.insight-it.ru/goto//tag/mapreduce/" target="_blank">MapReduce</a> здесь отказался только <a href="http://www.insight-it.ru/masshtabiruemost/arkhitektura-google-2011/"  target="_blank">Google</a>;</li>
<li><strong>поведенческий таргетинг для рекламы</strong>&nbsp;&mdash; собираем действия пользователей и делаем на их основе выводы в реальном времени;</li>
<li><strong>ведение рейтингов чего-либо в реальном времени</strong>&nbsp;&mdash; в зависимости от специфики проекта можно определять и показывать лучшие, самые просматриваемые или самые комментируемые статьи/фото/видео/музыку/товары/комментарии/что-нибудь-еще;</li>
<li><em>предлагаем свои варианты в комментариях</em>.</li>
</ul>
<li>Удачи в построении приложений для вычислений в реальном времени и <a href="/feed/" target="_blank">до встречи на страницах <strong>Insight IT</strong></a>!</li>
</ul>
<h2>Источники информации</h2>
<ul>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.infoq.com/presentations/Storm"  target="_blank">Видео презентации проекта</a></li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.slideshare.net/nathanmarz/storm-distributed-and-faulttolerant-realtime-computation"  target="_blank">Слайды с презентации проекта</a></li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/nathanmarz/storm"  target="_blank">Репозиторий проекта</a></li>
</ul>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=zDKSxnF8i94:h_c8BOgq-fE:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=zDKSxnF8i94:h_c8BOgq-fE:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=zDKSxnF8i94:h_c8BOgq-fE:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=zDKSxnF8i94:h_c8BOgq-fE:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=zDKSxnF8i94:h_c8BOgq-fE:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/zDKSxnF8i94" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/tekhnologii/soobshheniya/twitter-storm/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/tekhnologii/soobshheniya/twitter-storm/</feedburner:origLink></item>
		<item>
		<title>Архитектура YouTube 2012</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/r-aGQ2W_mXE/</link>
		<comments>http://www.insight-it.ru/masshtabiruemost/arkhitektura-youtube-2012/#comments</comments>
		<pubDate>Sat, 24 Mar 2012 12:50:42 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Масштабируемость]]></category>
		<category><![CDATA[bson]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[lighttpd]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[pycurl]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[spitfire]]></category>
		<category><![CDATA[Vitess]]></category>
		<category><![CDATA[wiseguy]]></category>
		<category><![CDATA[YouTube]]></category>
		<category><![CDATA[ZooKeeper]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1745</guid>
		<description><![CDATA[Выбирайте самое простое решение с наиболее общими гарантиями, которые практически полезны. &#8212; Дао YouTube YouTube практически на протяжении всех 7 лет своего существования является мировым лидером в сфере интернет-видео. С точки зрения технической реализации проект оказался достаточно консервативным&#160;&#8212; команда придерживается того же курса и стека технологий, с которых все начиналось еще до приобретения проекта Google. [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/QVRkLBc3KuQ5a_g2rvdHjTRVmKY/0/da"><img src="http://feedads.g.doubleclick.net/~a/QVRkLBc3KuQ5a_g2rvdHjTRVmKY/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/QVRkLBc3KuQ5a_g2rvdHjTRVmKY/1/da"><img src="http://feedads.g.doubleclick.net/~a/QVRkLBc3KuQ5a_g2rvdHjTRVmKY/1/di" border="0" ismap="true"></img></a></p><blockquote>
<p>Выбирайте самое простое решение с наиболее общими гарантиями, которые практически полезны.<br />
<em>&mdash; Дао YouTube</em></p>
</blockquote>
<p><strong>YouTube</strong> практически на протяжении всех 7 лет своего существования является мировым лидером в сфере интернет-видео. С точки зрения технической реализации проект оказался достаточно консервативным&nbsp;&mdash; команда придерживается того же курса и стека технологий, с которых все начиналось еще до приобретения проекта <strong>Google</strong>. Но с 2008 года, когда я написал первый обзор <a href="http://www.insight-it.ru/net/scalability/arkhitektura-youtube/"  target="_blank">архитектуры YouTube</a>, все же произошли интересные изменения, о которых я и хотел бы сегодня вкратце рассказать.</p>
<p><span id="more-1745"></span></p>
<h2>Статистика</h2>
<ul>
<li>4 млрд. просмотров страниц в день</li>
<li>60 часов видео загружается каждую минуту</li>
<li>350 миллионов устройств подключено к YouTube</li>
<li>На февраль 2012 года в США по данным comScore:</li>
<ul>
<li>147,4 млн. уникальных зрителей</li>
<li>16,7 млрд. просмотров видео (в октябре 2011 было больше 20 млрд.)</li>
<li>Каждый зритель посмотрел в среднем 7 часов видео за месяц</li>
<li>1.1 млрд. просмотров видео рекламы, суммарной длительностью в 10.8 млн. часов</li>
</ul>
</ul>
<h2>Технологии</h2>
<ul>
<li><a href="/tag/linux/" target="_blank">Linux</a>&nbsp;&mdash; операционная система</li>
<li><a href="/tag/apache/" target="_blank">Apache</a>&nbsp;&mdash; основной HTTP-сервер</li>
<li><a href="/tag/lighttpd/" target="_blank">lighttpd</a>&nbsp;&mdash; отдача видео из YouTube CDN</li>
<li><a href="/tag/zookeeper/" target="_blank">Zookeeper</a>&nbsp;&mdash; распределенные блокировки, хранение конфигураций</li>
<li><a href="/tag/python/" target="_blank">Python</a>:</li>
<ul>
<li><a href="/tag/wiseguy/" target="_blank">wiseguy</a>&nbsp;&mdash; FastCGI-прослойка между Apache и Python</li>
<li><a href="/tag/pycurl/" target="_blank">pycurl</a>&nbsp;&mdash; лучшая доступная реализация HTTP-клиента, но в итоге все равно заменили на самописное низкоуровневое решение, выиграв 8% в потреблении вычислительных ресурсов.</li>
<li><a href="/tag/spitfire/" target="_blank">spitfire</a>&nbsp;&mdash; высокопроизводительный шаблонизатор на основе абстрактного синтаксического дерева с регулируемым уровнем оптимизации (как в gcc)</li>
<li><a href="/tag/bson/" target="_blank">bson</a> в качестве формата сериализации</li>
</ul>
<li><a href="/tag/bigtable/" target="_blank">BigTable</a>&nbsp;&mdash; хранение изображений</li>
<li><a href="/tag/mysql/" target="_blank">MySQL</a>&nbsp;&mdash; используется просто как хранилище данных, версия 5.1.52 с InnoDB</li>
<li><a href="/tag/vitess/" target="_blank">Vitess</a>&nbsp;&mdash; система для масштабирования MySQL-кластера</li>
</ul>
<h2>Vitess</h2>
<ul>
<li>Основная цель проекта&nbsp;&mdash; предоставление всех необходимых инструментов и серверов для горизонтального масштабирования баз данных на основе MySQL, с учетом потребностей современных интернет-проектов.</li>
<li>Реализован на <a href="/tag/go/" target="_blank">Go</a>&nbsp;&mdash; все еще экзотическом языке программирования, также родившемся в стенах <a href="/tag/google/" target="_blank">Google</a>. Сравним по производительности с <a href="/tag/c/" target="_blank">C++</a> и <a href="/tag/java/" target="_blank">Java</a>, но несколько более &laquo;выразителен&raquo;.</li>
<li>Опубликован в opensource 24 февраля 2012 года, совсем недавно, так что <a href="/tag/youtube/" target="_blank">YouTube</a>&nbsp;&mdash; по-прежнему единственный пример его использования на практике в крупном проекте.</li>
<li>Готовые клиентские библиотеки пока только для <strong>Python</strong> и <strong>Go</strong>, что не удивительно, но есть и универсальные интерфейсы на основе HTTP и просто TCP-сокетов.</li>
<li>Основной формат данных&nbsp;&mdash; <strong>bson</strong>, как и в <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto//tag/mongodb/" target="_blank">MongoDB</a>, но по словам разработчиков <em>Vitess</em> <a href="http://code.google.com/p/vitess/source/browse/#hg%2Fgo%2Fbson"  target="_blank">их реализация</a> выполняет (де)сериализацию в 10-15 раз быстрее.</li>
<li>Ядром проекта выступает <strong>Vtocc</strong>,  SQL-прокси с RPC интерфейсом, позволяющий перераспределять запросы от большого количества (более 10 тыс.) одновременно подключенных клиентов в сравнительно небольшое количество соединений с базами данных. Пропускная способность порядка 10 тыс. запросов в секунду.</li>
<li>Встроенные возможности <strong>Vtocc</strong>:</li>
<ul>
<li>парсер и анализатор SQL-запросов для оптимизации их выполнения;</li>
<li>заполнение типичных запросов переменными с поддержкой кэширования результатов;</li>
<li>управление транзакциями и сроками их выполнения (&laquo;убивает&raquo; затянувшиеся);</li>
<li>для каждого пространства ключей (логической таблицы) можно указать фактор репликации, что создаст необходимое количество второстепенных баз данных в дополнение к мастеру;</li>
<li>можно явно указать, что чтение необходимо произвести с мастера (важно когда пользователь только что выполнил какое-то действие и должен сразу же увидеть его результат);</li>
<li>отдельные пулы соединений для выполнения операций чтения и записи;</li>
<li>исключение &laquo;зависших&raquo; соединений из пулов;</li>
<li>перезапуск без простоя системы;</li>
<li>поддержка DML.</li>
</ul>
<li><strong>Партиционирование:</strong></li>
<ul>
<li>Во всех таблицах должна быть колонка с уникальным ключем, на основе которого данные будут распределяться по кластеру.</li>
<li>Партиционирование основано на диапазонах ключей, что позволяет держать &laquo;карту&raquo; партиций в памяти и очень быстро определять где располагаются те или иные данные, но обратной стороной медали является вероятное возникновение &laquo;горячих&raquo; узлов в кластере, особенно при монотонно увеличивающихся значениях ключей (рекомендуется использовать случайные).</li>
<li>Поддерживаются ключи в виде натуральных чисел или произвольных бинарных данных.</li>
<li>При высокой нагрузке на одну партицию она может быть распределена на две путем фильтрованной репликации; в дальнейшем планируется реализовать и обратный процесс.</li>
<li>Еще в планах:</li>
<ul>
<li>Поэтапное внесение изменений в схему данных без видимого простоя системы;</li>
<li>Поддержка работы в нескольких датацентрах с концентрацией мастер-серверов в одном датацентре и использования остальных в режиме только для чтения.</li>
</ul>
</ul>
</ul>
<h2>Подводим итоги</h2>
<ul>
<li><strong>YouTube</strong>&nbsp;&mdash; еще один проект мирового масштаба, который с самого начала использовал MySQL и оказался не в силах от него отказаться, не смотря на трудности с горизонтальным масштабированием.</li>
<li>По аналогичному пути пошли и другие проекты, схожие с <strong>Vitess</strong> надстройки над MySQL используются в <a href="http://www.insight-it.ru/masshtabiruemost/arkhitektura-facebook/"  target="_blank">Facebook</a> и <a href="http://www.insight-it.ru/masshtabiruemost/arkhitektura-twitter-dva-goda-spustya/"  target="_blank">Twitter</a>:</li>
<ul>
<li>В <a href="/tag/facebook/" target="_blank">Facebook</a> она дополнена сильной интеграцией с <a href="/tag/memcached/" target="_blank">memcached</a> и сильно ограниченным интерфейсом, не имеющим практически ничего общего с SQL. Планы о публикации в opensource, кажется, были, но я не слышал чтобы они воплотились в жизнь. <em>// Уже почти дописав статью случайно заметил в коде, а потом и мелким шрифтом в документации, что в Vitess тоже используется memcached для кэширования из-за проблем со сборщиком мусора Go.</em></li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto//tag/twitter/" target="_blank">Twitter</a> по-прежнему использует свою связку <a href="https://github.com/twitter/flockdb"  target="_blank">FlockDB</a> + <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/twitter/gizzard/"  target="_blank">Gizzard</a> на <a href="/tag/scala/" target="_blank">Scala</a>, которые уже пару лет публично доступны. В отличии от Vitess она заточена на хранение информации о социальных графах, по-этому сфера её применения как в Twitter, так и за его пределами ограничена.</li>
</ul>
<li>Vitess&nbsp;&mdash; пожалуй первая относительно успешная попытка построить распределенную горизонтально масштабируемую СУБД на основе реляционной базы данных, сохранив при этом SQL-интерфейс, пускай и с некоторыми ограничениями.</li>
<li>Выбирайте подходящее хранилище для каждого типа данных в системе&nbsp;&mdash; если Vitess стал подходящим решением для структурированных данных вроде информации о пользователях, метаданных видео и комментариев, это не значит, что он хорошо (или плохо) справится, например, с медиа-файлами вроде изображений и видео (для них в <a href="/tag/youtube/" target="_blank">YouTube</a> по-прежнему используют стек технологий <a href="/tag/google/" target="_blank">Google</a>, подробности не публикуются).</li>
<li><a href="/tag/python/" target="_blank">Python</a>&nbsp;&mdash; вполне пригодный инструмент для реализации бизнес-логики интернет-проектов, свет клином на <a href="/tag/php/" target="_blank">PHP</a> не сошелся. Python предлагает широкий ассортимент инструментов для решения любых типичных для интернет-проектов задач, хотя субъективно выбор некоторых из них разработчиками YouTube мне кажется странным.</li>
</ul>
<div class="pretty">В комментариях предлагаю обсудить слабые и сильные стороны использования надстроек над реляционными базами данных, скажем по сравнению с использованием изначально-распределенных СУБД, таких как Riak, Cassandra и многих других. Может быть кто-то уже успел прикрутить к своему проекту <strong>Vitess</strong> или хотя бы <strong>FlockDB</strong> и готов поделиться впечатлениями?</div>
<h2>Источники информации</h2>
<ul>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://www.youtube.com/watch?v=G-lGCC4KKok"  target="_blank">Mike Solomon на PyCon &#39;12</a> (один из первых разработчиков проекта)</li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://code.google.com/p/vitess/"  target="_blank">О проекте Vitess</a></li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.comscore.com/Press_Events/Press_Releases/2012/3/comScore_Releases_February_2012_U.S._Online_Video_Rankings"  target="_blank">Статистика comScore на февраль &#39;12</a></li>
</ul>
<p>&nbsp;</p>
<p>&nbsp;</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=r-aGQ2W_mXE:IL0JTVpisE4:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=r-aGQ2W_mXE:IL0JTVpisE4:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=r-aGQ2W_mXE:IL0JTVpisE4:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=r-aGQ2W_mXE:IL0JTVpisE4:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=r-aGQ2W_mXE:IL0JTVpisE4:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/r-aGQ2W_mXE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/masshtabiruemost/arkhitektura-youtube-2012/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/masshtabiruemost/arkhitektura-youtube-2012/</feedburner:origLink></item>
		<item>
		<title>Erlang в интернет-проектах</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/JRIy_lAFM40/</link>
		<comments>http://www.insight-it.ru/programmirovanie/erlang/erlang-v-internet-proektakh/#comments</comments>
		<pubDate>Sat, 17 Mar 2012 15:43:42 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Erlang]]></category>
		<category><![CDATA[Beam]]></category>
		<category><![CDATA[DETS]]></category>
		<category><![CDATA[ERTS]]></category>
		<category><![CDATA[ETS]]></category>
		<category><![CDATA[EVM]]></category>
		<category><![CDATA[Mnesia]]></category>
		<category><![CDATA[OTP]]></category>
		<category><![CDATA[VM]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1722</guid>
		<description><![CDATA[С моей точки зрения, Erlang&#160;&#8212; один из наиболее продуманных языков программирования. Его создатели выбирали каждую деталь и особенность реализации так, чтобы сделать его идеальным для решения вполне конкретных телекоммуникационных задач, с которыми они сталкивались в 80-90-х годах. Во многом из-за этого он так и не стал универсальным языком программирования как C++, Python и другие, а так [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/7uN3YwVF9Icub2Xnp3FLKE6OGH0/0/da"><img src="http://feedads.g.doubleclick.net/~a/7uN3YwVF9Icub2Xnp3FLKE6OGH0/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/7uN3YwVF9Icub2Xnp3FLKE6OGH0/1/da"><img src="http://feedads.g.doubleclick.net/~a/7uN3YwVF9Icub2Xnp3FLKE6OGH0/1/di" border="0" ismap="true"></img></a></p><p>С моей точки зрения, <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.erlang.org/"  target="_blank"><strong>Erlang</strong></a>&nbsp;&mdash; один из наиболее продуманных языков программирования. Его создатели выбирали каждую деталь и особенность реализации так, чтобы сделать его идеальным для решения вполне конкретных телекоммуникационных задач, с которыми они сталкивались в 80-90-х годах. Во многом из-за этого он так и не стал универсальным языком программирования как <a href="/tag/c/" target="_blank">C++</a>, <a href="/tag/python/" target="_blank">Python</a> и другие, а так и остался спустя многие годы специализированным инструментом. Сегодня спрос и предложение на специалистов по <a href="/tag/erlang/" target="_blank">Erlang</a> на рынке труда относительно малы, что для большинства проектов является основным аргументом <em>против Erlang</em>, хотя порой они и сталкиваются с задачами, где он запросто бы стал тем самым <em>&laquo;идеальным инструментом&raquo;</em>. В этой статье я хотел бы обсудить, в каких именно ситуациях применительно к интернет-проектам использование <strong>Erlang</strong> оправдано и почему. Но начать придется издалека&nbsp;&mdash; с того, чем же он так <em>уникален</em>.<br />
<span id="more-1722"></span></p>
<h2>Что такое Erlang?</h2>
<p>Под словом Erlang обычно подразумевают совокупность сразу нескольких компонентов:</p>
<ul>
<li>Сам одноименный язык программирования&nbsp;&mdash; по сути синтаксис и идеологию;</li>
<li><strong>ERTS </strong><em>(Erlang Run-Time System)</em>&nbsp;&mdash; реализация всех низкоуровневых абстракций на <a href="/tag/c/" target="_blank">C</a>. Подробнее о них ниже.</li>
<li><strong>BEAM </strong><em>(Bogdans&#39; Erlang Abstract Machine)</em>&nbsp;&mdash; стандартная реализация виртуальной машины, с помощью которой обычно исполняются программы на Erlang после компиляции в байт-код (она очень эффективна; хотя компиляция Erlang в нативный код и возможна, оно того чаще всего не стоит). BEAM используется по-умолчанию в основных дистрибутивах <a href="/tag/linux/" target="_blank">Linux</a> и других операционных системах. Когда говорят &laquo;виртуальная машина Erlang&raquo; обычно подразумевается совокупность ERTS и BEAM.</li>
<li><strong>OTP </strong><em>(Open Telecom Platform)</em>&nbsp;&mdash; набор качественно реализованных высокоуровневых абстракций, использование которых стало почти стандартом де-факто в мире Erlang, так как оно позволяет не изобретать велосипеды и избегать типичных ошибок при реализации типичных же паттернов. Немного забегая вперед, приведу несколько примеров: <strong>gen_server</strong> (просто процесс, который принимает какие-то запросы и как-то на них реагирует), <strong>gen_fsm</strong> (конечный автомат), <strong>supervisor </strong>(мониторинг других процессов).</li>
</ul>
<h2>Ключевые особенности</h2>
<ul>
<li><strong>Параллельное программирование </strong><em>(concurrent programming)</em>&nbsp;&mdash; программы на Erlang состоят из независимых задач, которые <em>могут</em> выполняться параллельно, что на практике дает свободу виртуальной машине планировать их выполнение наиболее эффективным образом с учетом доступных системных ресурсов.</li>
<li><strong>Процессная модель</strong> <em>(process model)</em>&nbsp;&mdash; единицей параллельного выполнения в Erlang является <em>процесс</em>, который технически представляет собой лишь часть потока исполнения <em>(thread)</em> операционной системы и обладает нижеизложенными свойствами, которые обеспечивает их реализация в ERTS:</li>
<ul>
<li><strong>Параллельность</strong> <em>(concurrency)</em>&nbsp;&mdash; каждый процесс выполняет свою часть кода вне зависимости от других процессов, со своим темпом.</li>
<li><strong><strong>Изоляция процессов </strong></strong><em>(process isolation)</em> - в отличии от потоков исполнения в операционных системах и других языках программирования, между процессами Erlang&#39;а нет общей памяти. Помимо этого сбой в одном из процессов напрямую не влияет на другие процессы в системе. Именно по-этому они называются <em>процессами</em>, так как в этом ключе скорее похожи на полноценные процессы операционной системы.</li>
<li><strong>Низкое потребление ресурсов</strong> <em>(low resource consumption)</em>&nbsp;&mdash; так как процессы Erlang являются лишь абстракцией внутри потока исполнения операционной системы, используют зачастую <em>меньше килобайта</em> оперативной памяти и требует минимальных вычислительных ресурсов, то один сервер может при необходимости иметь <strong>сотни тысяч и даже миллионы</strong> запущенных <strong>процессов</strong> (теоретически возможный максимум&nbsp;&mdash; 268435456, хотя по-умолчанию стоит ограничение в 32768 процессов). Для сравнения: суммарное количество потоков выполнения на сервере обычно измеряется сотнями и редко превышает тысячу.</li>
<li><strong>Слабая связанность</strong> <em>(loose coupling)</em>&nbsp;&mdash; процессы общаются друг с другом посредством асинхронного обмена сообщениями <em>(message passing),</em> для чего часть памяти каждого процесса выделяется под &laquo;почтовый ящик&raquo;. При отправке сообщения в списке входящих сообщений процесса-получателя создается копия сообщения, составленного в процессе-отправителе. При этом протокол отправки сообщений между процессами скрыт от разработчика и не зависит от того, находится ли получатель в той же виртуальной машине или в удаленной (на другом сервере), что позволяет легко и практически прозрачно распределять приложения по многим физическим серверам <em>(горизонтальное масштабирование, scale out)</em>.</li>
<li><strong>Дерево ответственности </strong><em>(responsibility tree)</em>&nbsp;&mdash; создаваемые внутри системы процессы образуют иерархию, где родители несут ответственность за потомков. В упомянутом чуть выше примере сбой одного из процессов вызывает его завершение и рассылку уведомлений связанным процессам-соседям по иерархии (с информацией о том, где и почему произошел сбой), на которые они могут как-то реагировать. Типичных сценария реагирования два: также завершить работу и разослать аналогичные уведомления, вызывая цепную реакцию (такие процессы называют исполнителями, <em>worker</em>), либо на основе уведомления принять какое-то действие, например попытаться заново запустить часть дерева процессов, аналогичную остановленной (такие называют надсмотрщиками, <em>supervisor</em>). Использование этого механизма позволяет приложению добиться <em>отказоустойчивости</em>.</li>
</ul>
<li><strong>Ссылочная прозрачность </strong><em>(referential transparency)</em> - как только переменная получила какое-то значение его уже нельзя изменить <em>(single assignment)</em>, для нового значения нужно заводить новую переменную. На первый взгляд выглядит полным бредом, но именно эту цену нужно заплатить для гарантии того, что какая-то другая часть кода втихаря не &laquo;испортит&raquo; значение. Плюс отсутствие изменений в структурах данных в памяти дает большую свободу для применения различных оптимизаций компилятору, сборщику мусора и планировщику процессов.</li>
<li><strong>Планировщик процессов</strong> <em>(scheduler)</em>&nbsp;&mdash; виртуальная машина Erlang с точки зрения операционной системы выглядит как один процесс с несколькими потоками исполнения <em>(threads)</em>, каждый из которых имеет собственный планировщик, управляющий группой Erlang-процессов. Процессы могут прозрачно перемещаться из одного потока в другой для <em>балансировки нагрузки</em>. Помимо этого планировщик берет на себя управление вводом-выводом, которые на низком уровне реализованы в неблокирующей, основанной на событиях, манере с использованием <a href="/tag/epoll/" target="_blank">epoll</a> или аналогов, но для конечного разработчика представляется в упрощенном виде.</li>
<li><strong>Сборщик мусора в памяти</strong> <em>(garbage collector)</em>&nbsp;&mdash; в отличии от других виртуальных машин (в частности <a href="/tag/jvm/" target="_blank">JVM</a>) сборка мусора в Erlang не влечет за собой значимых задержек в работе приложений, так как благодаря изоляции процессов для сборки мусора они останавливаются по очереди, пока все остальные продолжают работать. Обычно область памяти выделенная под один процесс очень невелика (для сравнения: под новый процесс в Erlang выделяется около 1 килобайта, под новый поток исполнения в <a href="/tag/java/" target="_blank">Java</a>&nbsp;&mdash; более 512 килобайт в зависимости от реализации), так что сборка мусора для каждого процесса не занимает много времени. Планировщик может определить какие процессы нужно пропустить при очередной сборке мусора, если они не исполнялись с момента предыдущей сборки. Если процесс создается для выполнения кратковременной задачи, то он может успеть сделать свое дело и завершиться без единой сборки мусора, полностью освободив свою память по окончании работы.</li>
<li><strong><strong>Функциональное программирование</strong></strong> <em>(functional programming)</em> - если рассмотреть один Erlang-процесс внутри, отбросив его связь с внешним миром (обмен сообщениями), то можно увидеть программу, полностью соответствующую функциональной парадигме: алгоритмы выражаются в виде вызовов функций, которые, в свою очередь, являются единицами данных наравне с числами и сложными структурами. На практике же это означает другой стиль программирования и используемые абстракции (рекурсия вместо циклов, поведения вместо интерфейсов и т.п.), по сравнению с более распространенными <a href="/tag/oop/" target="_blank">объектно-ориентированными</a> языками; подробно это будет интересно лишь программистам, так что оставим это для другой статьи про <a href="/tag/erlang/" target="_blank">Erlang</a>.</li>
<li>Доступно три механизма хранения данных вне памяти процессов:</li>
<ul>
<li><strong>ETS</strong> <em>(erlang term storage)</em>&nbsp;&mdash; очень похожий на хранилище пар ключ-значение механизм, работающий в оперативной памяти самой виртуальной машины и доступный всем или части её процессов (есть ограничения доступа). Данные хранятся в пространствах имен (таблицы без жесткой структуры), а доступ осуществляется по ключу, который являются частью значения (обычно первым элементом в хранящейся структуре данных).</li>
<li><strong>DETS</strong> <em>(disk erlang term storage)</em> - предоставляется аналогичный ETS интерфейс и формат хранения данных, с той лишь разницей, что данные хранятся в файлах на диске, а не в памяти виртуальной машины. При использовании <strong>не</strong>твердотельных дисков операции поиска данных значительно медленнее аналогов из модуля ETS.</li>
<li><strong>Mnesia</strong>&nbsp;&mdash; полноценная СУБД на основе ETS/DETS, с поддержкой атомарных транзакций <em>(atomic transactions)</em>, репликации <em>(replication)</em> и партиционирования <em>(sharding)</em>. Позволяет абстрагироваться от физического расположения данных, осуществлять поиск/выборки данных в реальном времени, а также вносить изменения в конфигурацию и схему данных без перезапуска.</li>
</ul>
<li><strong>Горячее обновление кода</strong> <em>(hot code loading)</em>&nbsp;&mdash; виртуальная машина может держать в памяти и параллельно выполнять две версии одного и того же кода (единицей измерения здесь является <em>модуль</em>, то есть один скомпилированный файл исходного кода), процесс переключается со старого кода на новый при выполнении <em>внешнего</em> вызова к одной из его функций (что в целом полностью в руках разработчика). Эта возможность позволяет полностью избежать недоступности приложения при обновлениях, что очень важно для всех приложений, работающих в реальном времени, к которым также относятся все сайты и интернет-сервисы.</li>
</ul>
<h2> Применение на практике</h2>
<p>Телекоммуникации и Интернет на сегодняшний день хоть и являются совершенно разными областями <em>информационных технологий</em>, но все же глобальная цель у них общая: позволять людям легко общаться удаленно. Предлагаю вернуться к изначальной теме статьи: в каких конкретно ситуациях <strong>Erlang</strong>, вместе со своими изложенными выше особенностями и ограничениями, может оказаться уместным решением задач интернет-проекта? Примеры могут показаться субъективными, так что с удовольствием готов обсудить их и другие ситуации в комментариях.</p>
<h3>Входящие пользовательские соединения</h3>
<p>Еще в далеком 2002 году в сети часто мелькал сравнительный <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.sics.se/~joe/apachevsyaws.html"  target="_blank">бенчмарк</a> <strong>Apache</strong> <em>&copy;</em> и <strong>Yaws</strong> <em>(Erlang)</em> по обработке HTTP-запросов, где Yaws представлялся &laquo;победителем&raquo; с огромным отрывом. С тех пор конечно же многое поменялось, появился стремительно набирающий обороты <a href="/tag/nginx/" target="_blank">nginx</a> и &laquo;популярные в узких кругах&raquo; решения вроде <a href="/tag/node-js/" target="_blank">node.js</a> или <a href="/tag/tornado/" target="_blank">Tornado</a>.</p>
<p>Но <strong>Erlang</strong> тоже не стоит на месте. Благодаря целенаправленной работе по оптимизации ERTS в целом и планировщика процессов в частности, современные реализации HTTP-серверов на Erlang по-прежнему легко <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.ostinelli.net/a-comparison-between-misultin-mochiweb-cowboy-nodejs-and-tornadoweb/"  target="_blank">дают фору</a> более распространенным решениям.</p>
<p>В последние годы появляется все больше интернет-проектов, использующие постоянные соединения <em>(websocket, long polling, etc.)</em> между браузером и HTTP-сервером для обновления страниц сайта в реальном времени. Здесь также Erlang легко справляется с задачей, так как для поддержания постоянного соединения обычно используется лишь 1 Erlang-процесс (хотя иногда 2), которые, как уже упоминалось, потребляют минимум  оперативной памяти и вычислительных ресурсов. Как следствие, HTTP-сервер на Erlang способен поддерживать очень постоянное соединение с онлайн пользователями, даже если их количество измеряется десятками тысяч.</p>
<p>Хочется отметить, что в этом примере речь идет именно об обработке соединений с пользователями, то есть внутри HTTP-сервера минимум логики, он просто &laquo;разбирает&raquo; запрос и, вероятно, передает его дальше внутрь системы через брокер сообщений или напрямую внутренним сервисам. К вопросу с сколько-либо сложной бизнес-логикой вернемся чуть позже.</p>
<h3>Отдача статики</h3>
<p>Для отдачи статики в Erlang часто используют тот же системный вызов <strong>sendfile</strong>, что и в <a href="/tag/nginx/" target="_blank">nginx</a>. Но на практике ситуация здесь неоднозначна:</p>
<ul>
<li>прямой доступ к sendfile через встроенные вызовы <em>(BIF, Built-In Functions)</em> появился в Erlang только в самом последнем на сегодняшний день релизе&nbsp;&mdash; R15B;</li>
<li>раньше использовалась <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/tuncer/sendfile"  target="_blank">обертка</a> с использованием нативных функций <em>(NIF, native implemented functions)</em> или просто чтение файла, что <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.erlang-factory.com/upload/presentations/71/JoeWilliams-Web_Server_Deathmatch.pdf"  target="_blank">работало не очень хорошо</a>.</li>
</ul>
<div>На готовые бенчмарки по отдаче статики из последних версий Erlang&#39;а я не натыкался, так что могу предложить взглянуть на небольшой тест <strong>cowboy</strong> vs <strong>nginx</strong> на обычном домашнем оборудовании: Ubuntu в роли сервера, iMac в роли клиента (JMeter), 100Мбит между ними. Какого-либо тюнинга настроек не производилось.</div>
<ul>
<li>1.1Мб картинка в 10 потоков <em>(нехитрая математика говорит о том, что все упираются в сеть)</em>:</li>
<ul>
<li><a href="http://www.insight-it.ru/wp-content/uploads/2012/03/cowboy-static.jpg"  target="_blank">Cowboy без sendfile</a>: 853мс. в среднем, 639 запросов в минуту, отклонение 428мс.</li>
<li><a href="http://www.insight-it.ru/wp-content/uploads/2012/03/cowboyR15B-static.jpg"  target="_blank">Cowboy с sendfile</a>: 853мс. в среднем, 639 запросов в минуту, отклонение 395мс.</li>
<li><a href="http://www.insight-it.ru/wp-content/uploads/2012/03/nginx-static.jpg"  target="_blank">Nginx</a>: 882мс. в среднем, 638 запросов в минуту, отклонение 515мс.</li>
</ul>
<li>112б текстовый файл в 1000 потоков:</li>
<ul>
<li><a href="http://www.insight-it.ru/wp-content/uploads/2012/03/cowboy-static2.jpg"  target="_blank">Cowboy без sendfile</a>: 37мс. в среднем (но медиана&nbsp;&mdash; 3мс., то есть небольшая часть запросов сильно тормозит, а с остальной все нормально), 259 тыс. запросов в минуту, отклонение 234мс.</li>
<li><a href="http://www.insight-it.ru/wp-content/uploads/2012/03/cowboyR15B-static.jpg"  target="_blank">Cowboy с sendfile</a>: 17 мс. в среднем, 267 тыс. запросов в минуту, отклонение 27мс.</li>
<li><a href="http://www.insight-it.ru/wp-content/uploads/2012/03/nginx-static2.jpg"  target="_blank">Nginx</a>: 2мс. в среднем, 315 тыс. запросов в минуту, отклонение 3мс.</li>
</ul>
</ul>
<div>Не претендуя на хоть на какую-либо точность и применимость в боевых условиях, эти цифры и графики показывают, что в деле отдачи статики <strong>nginx</strong> хоть и по-прежнему лидер, но в не-экстремальных ситуациях особой разницы можно и не заметить. Хотя при использовании решений на Erlang определенно можно начать &laquo;скучать&raquo; по нестандартным конфигурациям nginx с какой-нибудь компрессией на лету, rewrite&#39;ами и пр.</div>
<p>В любом случае, для отдачи статики в сколько-либо серьезных интернет-проектов рекомендую пользоваться услугами <a href="/tag/cdn/" target="_blank">CDN</a>.</p>
<h3>Балансировка нагрузки</h3>
<p>Откровенно говоря, я не слышал о каком-либо проекте на Erlang для балансировки HTTP и/или TCP запросов, хотя бы отдаленно сравнимом по возможностям, надежности и производительности с <a href="/tag/haproxy/" target="_blank">HAProxy</a> и &laquo;железными&raquo; решениями.</p>
<p>Хотя по мне так сами свойства Erlang прекрасно подходят для решения этой задачи, но те проекты, на которые я натыкался (<a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/mdaguete/tcpbalance"  target="_blank">пример</a>), выглядят просто как &laquo;поделки&raquo; по сравнению с проверенными временем решениями.</p>
<p>В любом случае HTTP/TCP балансировщик нагрузки на Erlang&nbsp;&mdash; отличная  тема для нового opensource проекта, если вдруг кому-то нечем заняться в свободное время <img src='http://www.insight-it.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h3>Брокер сообщений</h3>
<p>В статье про <strong><a href="http://www.insight-it.ru/tekhnologii/soobshheniya/rabbitmq/"  target="_blank">RabbitMQ</a></strong> я уже подробно рассказывал о том, как Erlang вписывается в роль <em>брокера сообщений</em>, то есть посредника между различными компонентами системы, обеспечивающего их слабую связанность путем обмена сообщениями.</p>
<p>В дополнение хочется сказать, что хоть изобретать велосипед и редко когда оказывается хорошей затеей, Erlang отлично подошел бы и для реализации собственной схемы обмена сообщениями внутри системы, например без использования централизованного брокера, как это в итоге получается с использованием RabbitMQ или аналогов.</p>
<h3>Бизнес-логика</h3>
<p>Этот аспект является практически уникальным от проекта к проекту, так что здесь придется ограничиться лишь какими-то общими рекомендациями.</p>
<p>Основной слабой стороной Erlang является <strong>обработка данных</strong>, в частности:</p>
<ul>
<li>Текстовые строки в Erlang реализованы как <em>однонаправленный</em> <em>связанный список целых чисел</em>, то есть на каждый символ выделяется <strong>восемь байт</strong> памяти: четыре на код символа, четыре&nbsp;&mdash; на указатель на следующий символ; плюс еще четыре байта для указателя на начало списка. Для 64-битных систем эти цифры нужно удвоить, так как машинное слово вдвое длиннее. Помимо неоправданных расходов памяти, эта схема усложняет различные операции со строками, например чтобы посчитать длину строки нужно &laquo;пройтись&raquo; по ней целиком. А чтобы приписать один символ в конец строки, нужно сделать её полную копию (для записи в начало это не так, как не трудно догадаться).</li>
<li>Бинарные строки хранятся в памяти последовательно, так что объем не удваивается из-за указателей. Изменения в итоге также создают копии данных, что для больших строк накладно. В любом случае там где это возможно я бы рекомендовал использовать бинарные строки вместо текстовых.</li>
<li>С математическими задачами все не так плачевно: хоть и реализация базовых операций в виртуальной машине несколько отстает по производительности от чистого <a href="/tag/c/" target="_blank">С</a>, при желании его можно практически догнать средствами нативной компиляции, грамотной реализации алгоритма и отсутствия &laquo;палок в колесах&raquo; у компилятора. Альтернативный сценарий: использование NIF.</li>
</ul>
<p>Для не-англоязычных проектов трудностью может оказаться довольно сомнительная <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.erlang.org/doc/apps/stdlib/unicode_usage.html"  target="_blank">поддержка Unicode</a>: особого типа данных нет, в тех же текстовых строках код символа может выходить за пределы таблицы ASCII (не зря же на него 32 или 64 бита выделили), а в бинарных строках можно хранить что угодно, в т.ч. и Unicode-текст. Как прореагирует на Unicode тот или иной встроенный модуль или используемая библиотека никто не гарантирует, но обычно все более-менее нормально.</p>
<p>Хоть на самом деле это и является роскошью, но при реализации бизнес-логики на Erlang порой недостает ORM-подобных механизмов в духе &laquo;вытащил объект из базы, поменял в нем что-нибудь, положил обратно&raquo;. Не то чтобы таких библиотек нет, просто эта схема не очень хорошо &laquo;ложится&raquo; на функциональную парадигму и реализуется обычно через не особо предназначенные для этого механизмы словарей <em>(dict)</em> или именованных кортежей <em>(record)</em>.</p>
<p>В качестве резюме хочется сказать, что на <strong>Erlang</strong> можно реализовать бизнес-логику практически любого интернет-проекта. Просто если она сложнее, чем просто передать какие-то данные от одного пользователя другому, то вероятно из-за искусственных ограничений и недостаточной выразительности языка для эффективной её разработки на Erlang может потребоваться существенно больше времени и усилий, чем на более приспособленных для этого языках вроде <a href="/tag/ruby/" target="_blank">Ruby</a>, <a href="/tag/php/" target="_blank">PHP</a> и <a href="/tag/python/" target="_blank">Python</a>.</p>
<h3>Базы данных</h3>
<p>Здесь все довольно просто: обычно <strong>Erlang</strong> используется как распределенная надстройка над встраиваемыми <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto//tag/subd/" target="_blank">СУБД</a> или особыми форматами файлов. Основные представители: <a href="http://basho.com/products/riak-overview/"  target="_blank">Riak</a> <em>(Google LevelDB)</em>, <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://couchdb.apache.org/"  target="_blank">CouchDB</a> <em>(свой формат)</em>, <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.erlang.org/doc/man/mnesia.html"  target="_blank">Mnesia</a> <em>(DETS)</em>, <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.couchbase.com/"  target="_blank">Couchbase</a> <em>(memcached и SQLite)</em>&nbsp;&mdash; все совершенно разные, обсуждать и сравнивать можно до бесконечности, так что оставим это на другой раз.</p>
<p>Из общих особенностей вышеперечисленных решений можно выделить:</p>
<ul>
<li>Прозрачная <em>горизонтальная масштабируемость</em>;</li>
<li>Настраиваемый уровень <em>репликации</em> данных;</li>
<li>Обычно <em>доступность</em> и <em>персистентность</em> в ущерб строгой целостности (AP из CAP-теоремы);</li>
<li>Поддержка<em> сложных распределенных выборок</em> (<a href="/tag/mapreduce/" target="_blank">MapReduce</a>, многокритериальная фильтрация, полнотекстный поиск и т.п., за исключением Couchbase)</li>
<li>Способность легко справляться с большим <em>потоком изменений данных</em> (за исключением, пожалуй, CouchDB);</li>
<li>Отсутствие строгой схемы данных и SQL-подобного интерфейса.</li>
</ul>
<h2>Подводим итоги</h2>
<p><strong>Erlang</strong> в умелых руках может послужить и правда удачным решением для реализации многих аспектов интернет-проектов, благодаря качественной, проверенной временем, основе в виде виртуальной машины и OTP, а также продуманной модели легковесных процессов. <em>В результате получаются высокопроизводительные, горизонтально масштабируемые приложения, полностью приспособленные для стабильной бесперебойной работы в боевых условиях.</em></p>
<p>Высокий барьер обучения специалистов по-прежнему остается весомым аргументом &laquo;против&raquo;, но если в проекте команда разработчиков уровня выше среднего - вряд ли это станет серьезным препятствием. Недостаток &laquo;готовых&raquo; квалифицированных специалистов по Erlang на трудовом рынке также не особо радует, но ситуация определенно постепенно улучшается.</p>
<p>В комментариях предлагаю обсудить по каким еще причинам на сегодняшний день Erlang столь редко можно увидеть в технологическом стеке интернет-проектов? Какие еще вопросы смущают руководство и разработчиков? В каких ситуациях преодоление сложностей и ограничений, связанных с Erlang, того стоит?</p>
<p><em>Эта статья определенно будет далеко не последней про Erlang, так что если эта тема Вам близка&nbsp;&mdash; рекомендую <a href="/feed/" target="_blank">подписаться на RSS</a>.</em></p>
<p>&nbsp;</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=JRIy_lAFM40:2pRIkGp0ha0:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=JRIy_lAFM40:2pRIkGp0ha0:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=JRIy_lAFM40:2pRIkGp0ha0:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=JRIy_lAFM40:2pRIkGp0ha0:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=JRIy_lAFM40:2pRIkGp0ha0:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/JRIy_lAFM40" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/erlang/erlang-v-internet-proektakh/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/programmirovanie/erlang/erlang-v-internet-proektakh/</feedburner:origLink></item>
		<item>
		<title>RabbitMQ</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/f-KZeX7H6qw/</link>
		<comments>http://www.insight-it.ru/tekhnologii/soobshheniya/rabbitmq/#comments</comments>
		<pubDate>Fri, 09 Mar 2012 21:19:07 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Сообщения]]></category>
		<category><![CDATA[AMQP]]></category>
		<category><![CDATA[Erlang]]></category>
		<category><![CDATA[RabbitMQ]]></category>
		<category><![CDATA[брокер]]></category>
		<category><![CDATA[брокер сообщений]]></category>
		<category><![CDATA[обмен сообщениями]]></category>
		<category><![CDATA[сообщения]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1711</guid>
		<description><![CDATA[Когда веб-приложение перестает быть просто коллекцией скриптов, генерирующих HTML, встает вопрос о взаимодействии различных компонентов системы. Есть два основных подхода: обращение напрямую посредством протоколов вроде Thrift или Protocol Buffers; либо посредством брокера сообщений, посредника, берущего на себя вопросы их маршрутизации и доставки одному или нескольким получателям, даже в случае сбоев оборудования и недоступности сетевого соединения. [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/DlikVMm9lzyDkDaigCWadC4pTHA/0/da"><img src="http://feedads.g.doubleclick.net/~a/DlikVMm9lzyDkDaigCWadC4pTHA/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/DlikVMm9lzyDkDaigCWadC4pTHA/1/da"><img src="http://feedads.g.doubleclick.net/~a/DlikVMm9lzyDkDaigCWadC4pTHA/1/di" border="0" ismap="true"></img></a></p><p>Когда веб-приложение перестает быть просто коллекцией скриптов, генерирующих HTML, встает вопрос о взаимодействии различных компонентов системы. Есть два основных подхода:</p>
<ul>
<li>обращение <strong>напрямую</strong> посредством протоколов вроде <a href="/tag/thrift/" target="_blank">Thrift</a> или <a href="/tag/protocol-buffers/" target="_blank">Protocol Buffers</a>;</li>
<li>либо посредством <strong>брокера сообщений</strong>, посредника, берущего на себя вопросы их маршрутизации и доставки одному или нескольким получателям, даже в случае сбоев оборудования и недоступности сетевого соединения.</li>
</ul>
<div>Сегодня я хотел бы рассказать об одной из лучших, на мой взгляд, реализаций брокера сообщений, <strong>RabbitMQ</strong>. Хотите узнать почему я так считаю?&nbsp;&mdash; Дочитайте до конца <img src='http://www.insight-it.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </div>
<p><span id="more-1711"></span></p>
<h2>Основные понятия</h2>
<p>Слоганом <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.rabbitmq.com/"  target="_blank"><strong>RabbitMQ</strong></a> является <em>&laquo;обмен сообщениями, который просто работает&raquo;</em>. Отчасти с этим утверждением можно согласиться, для того чтобы сервис обмена сообщениями &laquo;просто заработал&raquo; достаточно простой команды <strong>aptitude install rabbitmq-server</strong> или аналога для операционных систем, не основанных на <a href="/tag/debian/" target="_blank">Debian</a>. Но кому этого будет достаточно? Как минимум нужно научить свой проект эти сообщения отправлять и принимать, а как максимум&nbsp;&mdash; обрабатывать десятки и сотни тысяч сообщений в секунду, но обо всем по порядку.</p>
<p>В основе RabbitMQ лежит протокол <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.amqp.org/"  target="_blank">AMQP</a>, который вводит три основных понятия:</p>
<ul>
<li><strong>Сообщение </strong><em>(message)</em> - единица информации, которая передается от отправителя к получателю(ям); состоит из набора заголовков и содержания, которое брокером никак не интерпретируются.</li>
<li><strong>Точка обмена </strong><em>(exchange)</em> - распределяет отправленные сообщения между одной или несколькими очередями в соответствии с их заголовками.</li>
<li><strong>Очередь </strong><em>(queue)</em> - место, где хранятся сообщения до тех пор, пока их не заберет получатель.</li>
</ul>
<p>Базовые механизмы взаимодействия с брокером очень просты:</p>
<ul>
<li><strong>Отправить сообщение </strong><em>(publish)</em>&nbsp;&mdash; сообщение сериализуется в определенный формат, при необходимости снабжается маршрутной меткой <em>(routing key)</em> и передается в RabbitMQ;</li>
<li><strong>Получать сообщение </strong><em>(consume или subscribe)</em>&nbsp;&mdash; приложение регистрируется в RabbitMQ с указанием какие именно сообщения оно готово получать и обрабатывать, после чего ожидает их доставки.</li>
</ul>
<p>Перед началом любого взаимодействия с брокером клиент должен указать какая точка обмена должна заниматься обработкой его сообщений, что при необходимости её и зарегистрирует. При этом он указывает её название и тип, которых доступно три:</p>
<ul>
<li><strong>Отправка всем </strong><em>(fanout)</em>&nbsp;&mdash; как следует из названия, каждое сообщение получат все очереди, связанные с данной точкой обмена, типичная публикация-подписка <em>(publish-subscribe)</em>.</li>
<li><strong>Прямая </strong><em>(direct)</em> - сообщение получит только та очередь, которая имеет название, соответствующее маршрутной метке сообщения, типичная очередь сообщений <em>(message queue).</em></li>
<li><strong>Тематическая </strong><em>(topic) </em>&mdash; очереди при регистрации указывают паттерн маршрутных меток сообщений, которые они хотели бы получать. Этот механизм позволяет наиболее гибко управлять маршрутизацией сообщений и строить нетривиальные схемы доставки. Вместо регулярных выражений используется очень простая схема: метки в виде слов, разделенных точками; в паттерне * заменяет ровно одно слово, #&nbsp;&mdash; ноль или больше; при отсутствии этих символов работает как прямая точка обмена.</li>
</ul>
<div class="prettify">Если Вашему приложению достаточно простых подписки-публикации или очереди сообщений, а также нет необходимости гарантировать доставку сообщений или обрабатывать потоки сообщений, превышающие возможности одного сервера, то можно рассмотреть более простые в эксплуатации решения, не основанные на AMQP. В такой ситуации я рекомендовал бы первым делом взглянуть на <a href="/tag/redis/" target="_blank">Redis</a>. Если это не про Вас, то продолжаем разбираться с RabbitMQ.</div>
<h2>Типичные сценарии</h2>
<h3>Выполнение длительных операций</h3>
<p>Представим себя интернет-проектом, который размещает у себя пользовательские видео или фото. Когда он получает по HTTP очередной файл, ему требуется сконвертировать его в стандартный формат для просмотра другими пользователями, а также, например, сделать несколько превью разного размера.</p>
<p>По-старинке эти операции делают последовательно в том же обработчике запроса, который и принял от пользователя файл. В схеме с брокером же после принятия файла он отправляет сообщение, в содержании которого будет, вероятно, ссылка на файла-оригинал, после чего он возвращает браузеру сообщение об успешной загрузке файла. Для отправки таких сообщений используют <strong>прямую точку обмена</strong>, с какой-то стандартной маршрутной меткой и соответствующим именем очереди, например process_video или create_thumbnails. Процессы, реализующие совершенно независимый сервис по выполнению этих длительных операций, будут по очереди забирать сообщения с &laquo;заданиями&raquo; из брокера, позволяя легко создавать любое количество исполнителей c <strong>балансировкой нагрузки</strong>, что обеспечит горизонтальное масштабирование этой подсистемы.</p>
<p>Еще один доступный механизм, который вписывается в эту задачу&nbsp;&mdash; <strong>подтверждение о получении сообщения </strong><em>(acknowledgement)</em>. Получатель должен отправить брокеру дополнительное сообщение о том, что такое-то сообщение было успешно получено, в противном случае оно останется в очереди ожидать следующего получателя. Если процессы-исполнители будут подтверждать получение только после успешного выполнения длительной операции, это будет гарантировать, что все задания будут успешно выполнены вне зависимости от сбоев на каждом конкретном исполнителе, что обеспечивает <strong>отказоустойчивость</strong>.</p>
<h3>Удаленный вызов (RPC)</h3>
<p>Для некоторых приложений важно не только отправить запрос на выполнение какой-то операции, но и получить в ответ какой-то результат. На самом деле использование брокера сообщений в этой ситуации не всегда является удачным решением, проще делать это напрямую посредством других технологий. Но если в системе итак присутствует брокер, а для удаленного вызова нет строгих требований по времени выполнения, плюс хочется подобно предыдущему примеру легко получить отказоустойчивость и балансировку нагрузки, то можно реализовать удаленный вызов и через брокер сообщений.</p>
<p>Для этого предусмотрено два заголовка сообщений:</p>
<ul>
<li><strong>Обратный адрес</strong> <em>(reply to)</em>&nbsp;&mdash; исполнитель должен отправить результат в очередь с указанным именем; отравитель сразу же после передачи сообщения-запроса брокеру начинает получать сообщения из указанной в этом заголовке очереди.</li>
<li><strong>Идентификатор запроса</strong> <em>(correlation id)</em>&nbsp;&mdash; должен быть уникальным среди запросов, чтобы отправитель мог сопоставить результаты с запросами.</li>
</ul>
<h3>Сообщения пользователям</h3>
<p>Очереди можно использовать как входящие почтовые ящики для пользователей веб-приложений. Какие-то компоненты системы или другие пользователи с использованием <em>прямой точки обмена</em> отправляют сообщения в очереди, содержащие в названии уникальный идентификатор пользователя-получателя. Там они ожидают пока он их не прочитает, например, зайдя на определенную страницу сайта.</p>
<p>В этом примере очень важно использовать режим постоянных сообщений <em>(persistant, путем установки заголовка delivery_mode=2)</em>, так как получатель сообщения может появиться очень не скоро и важно чтобы сообщения &laquo;переживали&raquo; даже полный перезапуск брокера сообщений. Для более короткоживущих сообщений это менее критично, но тоже порой актуально, особенно как еще одна мера для обеспечения <strong>отказоустойчивости</strong>.</p>
<p>Пример хоть и немного оторванный от реальности из-за очистки почтового ящика после каждого прочтения, но в каких-то ситуациях все же может иметь право на существование.</p>
<h3>Двустороннее соединение с браузером</h3>
<p>Пожалуй, самый &laquo;вкусный&raquo; пример, хоть и лежащий на поверхности. На многих крупных <a href="/highload/" target="_blank">интернет-проектах</a>, особенно социальной направленности можно увидеть уведомления <em>в реальном времени</em> о событиях на сайте&nbsp;&mdash; кто-то что-то написал, поставил +1, проголосовал и т.п.</p>
<p>Реализация этого функционала требует довольно серьезной работы как на стороне браузера, так и на серверной стороне. Браузерный вопрос выходит за рамки этой статьи (хотя тут у меня тоже есть что рассказать, отдельным постом когда-нибудь обязательно напишу), а вот на серверной стороне брокер сообщений окажется очень даже кстати, особенно в реализации RabbitMQ.</p>
<p>На серверной части эта задача делится на две части:</p>
<ul>
<li>Поддерживать  <strong>постоянное соединение</strong> со всеми пользователями, кто находится онлайн&nbsp;&mdash; здесь на помощь обычно приходит либо <a href="/tag/erlang/" target="_blank">Erlang</a>, либо неблокирующий сервер на <a href="/tag/epoll/" target="_blank">epoll</a>. Оба варианта очень неплохие, выбирайте сами.</li>
<li>Дальше нужно как-то организовать <strong>доставку сообщений</strong> (информацию о событиях в системе) между пользователями, где и вступает в игру брокер. Обработчик соединения подписывается на сообщения о публичных событиях (точка обмена &laquo;отправить всем&raquo;), и туда же отправляет информацию о действиях пользователя-владельца.</li>
</ul>
<p>Чем больше пользователей онлайн, тем больше сообщений в единицу времени будет проходить через брокер. Один сервер перестанет справляться довольно быстро, так что следующий раздел статьи окажется очень кстати.</p>
<h2>Кластеризация</h2>
<p>Многое из вышеизложенного справедливо и для других реализаций <a href="/tag/amqp/" target="_blank">AMQP</a>, но в вопросе кластеризации <strong>RabbitMQ</strong> предстает во всей красе. Залогом этого в первую очередь является использование <a href="/tag/erlang/" target="_blank">Erlang</a>, не знаю почему я до сих пор не написал статью про этот язык программирования, здесь достаточно было бы на нее сослаться и все стало бы ясно.</p>
<p>Если вкратце, то в Erlang реализована внутренняя система легковесных процессов, не имеющая общего состояния и взаимодействующая друг с другом <em>исключительно</em> посредством обменом сообщений. При этом с точки разработчика отправка сообщений другому процессу на том же физическом сервером и на удаленном выглядит одинаково, и даже является одним из операторов языка&nbsp;&mdash; &laquo;!&raquo;, наравне с &laquo;=&raquo;, &laquo;+&raquo; и.т.п. Этот факт позволяет приложениям или их частям взаимодействовать по сети так же легко, как и в рамках одного сервера.</p>
<p>Чтобы определить разрешено ли разным Erlang-сервера взаимодействовать друг с другом, они обмениваются хэшем пароля (который правда называют <strong>cookie</strong>, хотя с одноименным механизмом браузеров он ничего общего не имеет) и продолжают работу только если он совпал. Он должен быть одинаковым на всех узлах и хранится в файле ~/.erlang.cookie, для RabbitMQ это обычно  /var/lib/rabbitmq/.erlang.cookie&nbsp;&mdash; первым делом нужно решить этот вопрос, а также убедиться, что используется нестандартное значение.</p>
<p>Узлы в RabbitMQ кластере могут быть двух типов: работающие только <strong>в памяти</strong> и сохраняющие данные<strong> на диск</strong>. Так как состояние системы реплицируется между узлами кластера, в большинстве случаев достаточно иметь лишь 2-3 дисковых узла, а остальные избавить от необходимости работать с дисковой подсистемой для увеличения производительности.</p>
<p>Важно понимать, что под состоянием системы здесь имеются ввиду лишь привязки и настройки брокеров, каждая же очередь и хранящиеся в ней сообщения располагаются на одном конкретном узле, что приведет к потери части сообщений при сбое одного из серверов. Этот вопрос можно решить и средствами операционной системы, но чаще всего правильнее выделить критически-важные для системы очереди сообщений и включить их репликацию средствами RabbitMQ, этот механизм называется <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.rabbitmq.com/ha.html"  target="_blank"><strong>зеркальные очереди</strong></a> <em>(mirrored queues)</em>.  Репликация происходит по принципу <strong>мастер-слуга</strong> <em>(master-slave)</em>, как и в реляционных СУБД: все операции осуществляются на основном сервере (мастере), он транслирует их на один или несколько вторичных серверов (слуги), при каком-либо сбое на основном один из слуг &laquo;повышается&raquo; до статуса мастера и берет на себя его функции. Очереди могут быть объявлены зеркальными только при создании, но новые узлы в роли слуг могут добавляться и позже, в таком случае новый слуга начнет получать входящие сообщения и рано или поздно начнет полностью отражать его состояние, механизма синхронизации при подключении дополнительного слуги не предусмотрено. Последним шагом для гарантированной доставки сообщений, не упоминавшимся ранее, является механизм <strong>уведомления отправителя об успешной записи сообщения</strong> в очередь (на все сервера для зеркальных).</p>
<p>В кластерном окружении может понадобиться <strong>объединение точек обмена</strong> <em>(exchange federation)</em>, что реализуется посредством пересылки сообщений по однонаправленным связям. При этом учитывается наличие на принимающей стороне очередей, готовых принять каждое конкретное сообщение. Практического применения в веб-проектах этому пока особо не вижу, разве что при кросс-датацентровой работе. Кстати, для этого поддерживается работа поверх <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.rabbitmq.com/ssl.html"  target="_blank">SSL</a>.</p>
<p>Для подключения узлов к кластеру можно использовать консольную утилиту (для временных изменений) или конфигурационные файлы (для постоянных настроек), подробно <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.rabbitmq.com/clustering.html"  target="_blank">останавливаться не буду</a>.</p>
<h2>Подводим итоги</h2>
<p>Используя брокер сообщений при технической реализации интернет-проекта, можно перевести его на совершенно новый уровень с точек зрения <em>отказоустойчивости</em> и <em>горизонтальной масштабируемости</em>. Во многих случаях он становится &laquo;сердцем&raquo; приложения, без которого его существование было бы немыслимо, но в то же время благодаря кластеризации не становится <strong>единственной точкой отказа</strong> <em>(single point of failure)</em>.</p>
<p>Хоть многое из упомянутого в статье можно реализовать и с помощью других технологий, <strong>RabbitMQ</strong> является наиболее приспособленной к реалиям современного Интернета реализацией брокера сообщений и AMQP в частности, в первую очередь благодаря распределенной природе Erlang и качественно спроектированной архитектуре этого продукта.</p>
<p>В комментариях с удовольствием обсудил бы применение RabbitMQ и других брокеров сообщения в различных практических ситуациях; еще можно подискутировать по поводу его преимуществ и недостатков по сравнению с альтернативами, в каких ситуациях это проявляется.</p>
<p><em><strong>Жду Вас среди <a href="/feed/" target="_blank">постоянных читателей Insight IT</a>, число которых недавно перевалило за 14 тысяч <img src='http://www.insight-it.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </strong></em></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=f-KZeX7H6qw:EaMef6euJq8:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=f-KZeX7H6qw:EaMef6euJq8:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=f-KZeX7H6qw:EaMef6euJq8:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=f-KZeX7H6qw:EaMef6euJq8:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=f-KZeX7H6qw:EaMef6euJq8:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/f-KZeX7H6qw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/tekhnologii/soobshheniya/rabbitmq/feed/</wfw:commentRss>
		<slash:comments>27</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/tekhnologii/soobshheniya/rabbitmq/</feedburner:origLink></item>
		<item>
		<title>Tornado</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/0YJFEkqQ8Kg/</link>
		<comments>http://www.insight-it.ru/programmirovanie/python/tornado/#comments</comments>
		<pubDate>Tue, 28 Feb 2012 19:03:17 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[epoll]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[Tornado]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1701</guid>
		<description><![CDATA[Tornado - масштабируемый неблокирующий HTTP-сервер на основе epoll, написанный полностью на Python. Изначально он был разработан в рамках проекта FriendFeed, на сегодняшний же день его поддержкой занимается Facebook. Сегодня я хотел бы рассказать о том, как с его помощью можно быстро и легко создавать веб-проекты на Python, которые в дальнейшем будет относительно легко горизонтально масштабировать. HTTP Не смотря на приличное количество [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/f8i0LHxT_dPdMbRikMKw0au4M3E/0/da"><img src="http://feedads.g.doubleclick.net/~a/f8i0LHxT_dPdMbRikMKw0au4M3E/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/f8i0LHxT_dPdMbRikMKw0au4M3E/1/da"><img src="http://feedads.g.doubleclick.net/~a/f8i0LHxT_dPdMbRikMKw0au4M3E/1/di" border="0" ismap="true"></img></a></p><p><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.tornadoweb.org/"  target="_blank"><strong>Tornado</strong></a> - масштабируемый неблокирующий <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto//tag/http/" target="_blank">HTTP</a>-сервер на основе <a href="/tag/epoll/" target="_blank">epoll</a>, написанный полностью на <a href="/tag/python/" target="_blank">Python</a>. Изначально он был разработан в рамках проекта FriendFeed, на сегодняшний же день его <a href="https://github.com/facebook/tornado"  target="_blank">поддержкой</a> занимается <a href="/tag/facebook/" target="_blank">Facebook</a>. Сегодня я хотел бы рассказать о том, как с его помощью можно быстро и легко создавать веб-проекты на Python, которые в дальнейшем будет относительно легко горизонтально масштабировать.</p>
<p><span id="more-1701"></span></p>
<h2>HTTP</h2>
<p>Не смотря на приличное количество опциональных модулей, идущих в комплекте с Tornado, проект в первую очередь является именно HTTP-сервером. Используемый механизм <a href="http://www.insight-it.ru/tekhnologii/kak-rabotaet-epoll/"  target="_blank">epoll</a> (по ссылке можно прочитать о том, в чем он заключается) практически полностью определяет основные принципы работы Tornado:</p>
<ul>
<li>он работает в рамках одного процесса;</li>
<li>использование потоков внутри него нежелательно;</li>
<li>для использования всех доступных ядер процессора обычно запускают несколько копий одинаковых процессов на разных портах (недавно добавили модуль <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.tornadoweb.org/documentation/process.html"  target="_blank">tornado.process</a> для упрощения реализации этого);</li>
<li>обычно обрабатывает HTTP-запросы не напрямую, а через балансировщик нагрузки (<a href="/tag/nginx/" target="_blank">nginx</a> или <a href="/tag/haproxy/" target="_blank">HAProxy</a>).</li>
</ul>
<p>Эта ситуация мотивирует с самого начала задумываться о распределении нагрузки, а также о выносе выполнения вычислительно сложных задач в отдельные сервисы, скажем конвертирование фото/видео или подсчет какой-то статистики.</p>
<p>Стоит добавить, что вместе с проектом поставляется модуль <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.tornadoweb.org/documentation/wsgi.html"  target="_blank">tornado.wsgi</a>, который позволяет запускать внутри себя другие веб-ориентированные проекты на Python (в частности небезызвестный <a href="/tag/django/" target="_blank">Django</a>), а также &laquo;притворяться&raquo; таковым для каких-то внешних серверов или сервисов, которые умеют общаться с Python-приложениями только по WSGI-протоколу, например таковым является <a href="/tag/gae/" target="_blank">Google App Engine</a>. Пользоваться этим модулем крайне не рекомендую, только при постепенном мигрировании проекта с каких-то других технологий.</p>
<h2>Обработка запросов</h2>
<p>При использовании <strong>Tornado</strong> не приходится работать с HTTP напрямую&nbsp;&mdash; разбор заголовков и URL он берет на себя. От разработчика требуется лишь словарь, состоящий из регулярных выражений и соответствующих им классов-обработчиков запросов.</p>
<p>При создании этих классов настоятельно рекомендую по полной воспользоваться возможностями <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto//tag/oop/" target="_blank">ООП</a>, в частности наследования. Tornado предоставляет базовый класс <a href="http://www.tornadoweb.org/documentation/web.html#tornado.web.RequestHandler"  target="_blank">RequestHandler</a>, который берет на себя всю грязную работу, а разработчику предлагается реализовать лишь логику, переопределив метод(ы) <em>get</em>, <em>post</em>, <em>delete</em> или <em>head</em>. На практике же обычно удобнее иметь свой собственный базовый класс для обработчиков запросов, который унаследован от RequestHandler и реализовывает общую для текущего конкретного проекта логику (примеры ниже).</p>
<h2>Доступ к базе данных</h2>
<p>Модуль <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.tornadoweb.org/documentation/database.html"  target="_blank">tornado.database</a> предлагает довольно простой доступ к <a href="/tag/mysql/" target="_blank">MySQL</a>. С одной стороны благодаря нему можно сходу начинать разрабатывать приложение на Tornado без использования дополнительных библиотек, с другой&nbsp;&mdash; далеко не в каждом проекте используется именно эта <a href="/tag/subd/" target="_blank">СУБД</a>.</p>
<p>В любом случае никто не запрещает использовать любую другую библиотеку для доступа к любой другой СУБД, но есть одно большое <strong>НО! </strong>Большинство из них являются <em>блокирующими</em>, то есть не возвращают управление до тех пор, пока СУБД не вернет ответ. Почуяли неладное? Правильно, в таком случае весь процесс Tornado, вместе со всеми попавшими в него запросами, будет простаивать пока управление не будет получено обратно, что очень не здорово.</p>
<p>Решается эта неприятная ситуация путем отправки асинхронных запросов к СУБД, то есть после отправки запроса управление сразу же возвращается, а для обработки запроса регистрируется callback, который получит управление, когда прийдет ответ от СУБД. За планирование очередности передачи управления отвечает <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.tornadoweb.org/documentation/ioloop.html"  target="_blank">IOLoop</a>, который и является &laquo;сердцем&raquo; Tornado.</p>
<p><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/facebook/tornado/wiki/Links"  target="_blank">Ассортимент</a> готовых библиотек, интегрированных с <strong>Tornado IOLoop</strong>, довольно широк и не ограничивается одним доступом к СУБД. Хотя готовое решение получается найти все же не всегда&nbsp;&mdash; приходится возиться с этим всем вручную или мириться с блокировками&#8230;</p>
<h2>Взаимодействие с внешним миром</h2>
<p>В комплекте с Tornado идет неблокирующий <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.tornadoweb.org/documentation/httpclient.html"  target="_blank">HTTP-клиент</a>, так что внутренние сервисы проще всего реализовывать с интерфейсом на JSON over HTTP. Им же можно и обращаться к API внешних сервисов.</p>
<p>С <a href="/tag/thrift/" target="_blank">Thrift</a> и <a href="/tag/protocol-buffers/" target="_blank">Protocol Buffers</a> ситуация несколько более печальна&nbsp;&mdash; о прецедентах их интеграции в <strong>Tornado IOLoop</strong> я не слышал, если кто-то может поделиться информацией&nbsp;&mdash; буду благодарен, довольно актуальный вопрос.</p>
<h2>Генерация HTML</h2>
<p>Шаблонизатор также предлагается свой собственный (не очень удачный, но вполне можно использовать), но его особо никто не навязывает&nbsp;&mdash; необходимо лишь переопределить метод render у базового RequestHandler с использованием любого другого аналогичного продукта.</p>
<p>Например, <a href="http://www.insight-it.ru/programmirovanie/python/jinja2/"  target="_blank">Jinja2</a>, о котором я недавно писал, подключается примерно вот так:</p>
<pre>from connections import env
from tornado.web import RequestHandler
class BaseHandler(RequestHandler):
  def render(self, template, context = None):
    if not context: context = {}
    context['user'] = self.current_user
    self.write(env.get_template(template).render(context))
    self.flush()</pre>
<h2>Прочие бонусы</h2>
<ul>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.tornadoweb.org/documentation/gen.html"  target="_blank">tornado.gen</a>&nbsp;&mdash; набор инструментов для упрощения написания асинхронного кода. Благодаря использованию механизма генераторов (yield), позволяет уместить в рамках одного метода и отправку асинхронного запроса и обработку его результата.</li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.tornadoweb.org/documentation/websocket.html"  target="_blank">tornado.websocket</a> предлагает реализацию нескольких последних редакций одноименного протокола,  доступна пара более кроссбраузерных альтернатив с поддержкой нескольких протоколов: <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/MrJoes/sockjs-tornado"  target="_blank">sockjs-tornado</a> и <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/MrJoes/tornadio2"  target="_blank">TornadIO</a>.</li>
<li>С помощью <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.tornadoweb.org/documentation/twisted.html"  target="_blank">tornado.platform.twisted</a> можно запускать код, написанный под <strong>Twisted</strong> (несколько более громоздкий и пожилой конкурент), внутри <strong>Tornado IOLoop.</strong> Актуально для &laquo;мигрирующих&raquo; проектов и прикручивания библиотек, написанных под Twisted.</li>
<li>Без <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.tornadoweb.org/documentation/autoreload.html"  target="_blank">tornado.autoreload</a> разработка превратилась бы в настоящий кошмар.</li>
</ul>
<h2> Заключение</h2>
<p>Асинхронная модель обработки запросов&nbsp;&mdash; и правда может оказаться очень большой головной болью, но к ней вполне реально приспособиться и получить выгоды в виде, как минимум:</p>
<ul>
<li>возможности поддерживать открытыми больше пользовательских соединений при фиксированных ресурсах;</li>
<li>априори горизонтально масштабируемой архитектуры на уровне приложения (базы данных&nbsp;&mdash; отдельная тема);</li>
<li>частичной независимости от быстродействия используемых сторонних и внутренних сервисов;</li>
<li>мотивации выносить вычислительно-тяжелые операции в отдельные сервисы (даже при многопоточной модели так стоит делать), а заодно и использовать брокер сообщений внутри системы <em>(весь последний пункт связан лишь косвенно)</em>.</li>
</ul>
<p>В комментариях предлагаю подискутировать на тему пригодности <strong>Tornado</strong> и аналогичных продуктов для использования в различных интернет-проектах, как высоконагруженных, так и маленьких. Еще мне было бы интересно узнать насколько велик интерес аудитории к чуть более прикладным, чем обычно, статьям, вроде этой&nbsp;&mdash; с удовольствием выслушаю Ваше мнение. <a href="/feed/" target="_blank">До новых встреч!</a></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=0YJFEkqQ8Kg:Fvtakbe1R-U:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=0YJFEkqQ8Kg:Fvtakbe1R-U:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=0YJFEkqQ8Kg:Fvtakbe1R-U:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=0YJFEkqQ8Kg:Fvtakbe1R-U:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=0YJFEkqQ8Kg:Fvtakbe1R-U:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/0YJFEkqQ8Kg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/python/tornado/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/programmirovanie/python/tornado/</feedburner:origLink></item>
		<item>
		<title>Архитектура Tumblr</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/wGSgRJMbnQs/</link>
		<comments>http://www.insight-it.ru/masshtabiruemost/arkhitektura-tumblr/#comments</comments>
		<pubDate>Tue, 21 Feb 2012 12:29:51 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Масштабируемость]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[Capistrano]]></category>
		<category><![CDATA[CentOS]]></category>
		<category><![CDATA[Finagle]]></category>
		<category><![CDATA[Func]]></category>
		<category><![CDATA[gearman]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[Hadoop]]></category>
		<category><![CDATA[HAProxy]]></category>
		<category><![CDATA[HBase]]></category>
		<category><![CDATA[jenkins]]></category>
		<category><![CDATA[kafka]]></category>
		<category><![CDATA[Kestrel]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[Memcached]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[puppet]]></category>
		<category><![CDATA[Redis]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Scala]]></category>
		<category><![CDATA[Thrift]]></category>
		<category><![CDATA[Tumblr]]></category>
		<category><![CDATA[Varnish]]></category>
		<category><![CDATA[ZooKeeper]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1671</guid>
		<description><![CDATA[Tumblr&#160;&#8212; одна из самых популярных в мире платформ для блоггинга, которая делает ставку на привлекательный внешний вид, юзабилити и дружелюбное сообщество. Хоть проект и не особо на слуху в России, цифры говорят сами за себя: 24й по посещаемости сайт в США с 15 миллиардами просмотров страниц в месяц. Хотите познакомиться с историей этого проекта, выросшего из [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/d4DgRwr9X7lMb09U0Ex3DvgYG8Y/0/da"><img src="http://feedads.g.doubleclick.net/~a/d4DgRwr9X7lMb09U0Ex3DvgYG8Y/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/d4DgRwr9X7lMb09U0Ex3DvgYG8Y/1/da"><img src="http://feedads.g.doubleclick.net/~a/d4DgRwr9X7lMb09U0Ex3DvgYG8Y/1/di" border="0" ismap="true"></img></a></p><p><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.tumblr.com"  target="_blank"><strong>Tumblr</strong></a>&nbsp;&mdash; одна из самых популярных в мире платформ для блоггинга, которая делает ставку на привлекательный внешний вид, юзабилити и дружелюбное сообщество. Хоть проект и не особо на слуху в России, цифры говорят сами за себя: 24й по посещаемости сайт в США с 15 миллиардами просмотров страниц в месяц. Хотите познакомиться с историей этого проекта, выросшего из простого стартапа?<br />
<span id="more-1671"></span></p>
<h2>Введение</h2>
<p>Как и всем успешным стартапам, Tumblr удалось преодалеть опасную пропать между начинающим проектом и широко известной компанией. Поиск правильных людей, эволюция инфраструктуры, поддержка старых решений, паника по поводу значительного роста посещаемости от месяца к месяцу, при этом в команде только 4 технических специалиста&nbsp;&mdash; все это заставляло руководство Tumblr принимать тяжелые решения о том над чем стоит работать, а над чем&nbsp;&mdash; нет. Сейчас же технический персонал расширился до 20 человек и у них достаточно энергии для преодоления всех текущих проблем и разработки новых интересных технических решений.</p>
<p>Поначалу Tumblr был вполне типичным большим <a href="/tag/lamp/" target="_blank">LAMP</a> приложением. Сейчас же они двигаются в направлении модели распределенных сервисов, построенных вокруг существенно менее распространенных технологий. Основные усилия сейчас вкладываются в постепенный уход от <a href="/tag/php/" target="_blank">PHP</a> в пользу более &laquo;правильных&raquo; и &laquo;современных&raquo; решений, оформленных в виде сервисов. Параллельно с переходом к новым технологиям идут изменения и в команде проекта: от небольшой группы энтузиастов к полноценной команде разработчиков, имеющей четкую стуктуру и сферы ответственности, но тем не менее жаждущей реализовывать новый функционал и обустраивать совершенно новую инфраструктуру проекта.</p>
<h2>Платформа</h2>
<ul>
<li><a href="/tag/centos/" target="_blank">CentOS</a> на серверах, <a href="/tag/mac-os-x/" target="_blank">Mac OS X</a> для разработки</li>
<li><a href="/tag/apache/" target="_blank">Apache</a>&nbsp;&mdash; основной веб-сервер</li>
<li><a href="/tag/php/" target="_blank">PHP</a>, <a href="/tag/scala/" target="_blank">Scala</a>, <a href="/tag/ruby/" target="_blank">Ruby</a>&nbsp;&mdash; языки программирования</li>
<li><a href="/tag/finagle/" target="_blank">Finagle</a> - асинхронный RPC сервер и клиент</li>
<li><a href="/tag/mysql/" target="_blank">MySQL</a>, <a href="/tag/hbase/" target="_blank">HBase</a> - СУБД</li>
<li><a href="/tag/memcached/" target="_blank">memcached</a>, <a href="/tag/redis/" target="_blank">Redis</a> - кэширование</li>
<li><a href="/tag/varnish/" target="_blank">Varnish</a>, <a href="/tag/nginx/" target="_blank">nginx</a>&nbsp;&mdash; отдача статики</li>
<li><a href="/tag/haproxy/" target="_blank">HAProxy</a>&nbsp;&mdash; балансировка нагрузки</li>
<li><a href="/tag/kestrel/" target="_blank">kestrel</a>, <a href="/tag/gearman/" target="_blank">gearman</a>&nbsp;&mdash; очередь задач</li>
<li><a href="/tag/thrift/" target="_blank">Thrift</a>&nbsp;&mdash; сериализация</li>
<li><a href="/tag/kafka/" target="_blank">Kafka</a>&nbsp;&mdash; распределенная шина сообщений</li>
<li><a href="/tag/hadoop/" target="_blank">Hadoop</a>&nbsp;&mdash; обработка статистики</li>
<li><a href="/tag/zookeeper/" target="_blank">ZooKeeper</a>&nbsp;&mdash; хранение конфигурации и состояний системы</li>
<li><a href="/tag/git/" target="_blank">git</a>&nbsp;&mdash; система контроля версий</li>
<li><a href="/tag/jenkins/" target="_blank">Jenkins</a>&nbsp;&mdash; непрерывное тестирование</li>
</ul>
<h2>Статистика</h2>
<ul>
<li>Около 500 миллионов просмотров страниц в день</li>
<li>Более 15 миллиардов просмотров страниц в месяц</li>
<li>Посещаемость растет примерно на 30% в месяц</li>
<li>Пиковые нагрузки порядка 40 тысяч запросов в секунду</li>
<li>Около 20 технических специалистов в команде</li>
<li>Каждый день создается около 50Гб новых постов и 2.7Тб обновлений списков последователей</li>
<li>Более 1Тб статистики обрабатывается в <a href="/tag/hadoop/" target="_blank">Hadoop</a> ежедневно</li>
<li>Используется порядка 1000 серверов:</li>
<ul>
<li>500 веб-серверов c Apache и PHP-приложением</li>
<li>200 серверов баз данных (существенная их часть&nbsp;&mdash; резервные)</li>
<ul>
<li>47 пулов</li>
<li>30 партиций (шардов)</li>
</ul>
<li>30 серверов <a href="/tag/memcached/" target="_blank">memcached</a></li>
<li>25 серверов Redis</li>
<li>15 серверов Varnish</li>
<li>25 серверов HAProxy</li>
<li>8 серверов nginx</li>
<li>14 серверов для очередей задач</li>
</ul>
</ul>
<h2 dir="ltr">Типичное использование</h2>
<ul>
<li><strong>Tumblr</strong> используется несколько по-другому, чем другие социальные сети:</li>
<ul>
<li>При более чем 50 миллионах постов в день, каждый из них попадает в среднем к нескольким сотням читателей. Это и не несколько пользователей с миллионами читателей (например, популярные личности в Twitter) и не миллиарды личных сообщений.</li>
<li>Ориентированность на длинные публичные сообщения, полные интересной информацией и картинками/видео, заставляет пользователей проводить долгие часы каждый день за чтением Tumblr.</li>
<li>Большинство активных пользователей подписывается на сотни других блоггеров, что практически гарантирует много страниц нового контента при каждом заходе на сайт. В других социальных сетях поток новых сообщений переполнен ненужным контентом и толком не читается.</li>
<li>Как следствие, при сложившемся количестве пользователей, средней аудиторией каждого и высокой активностью написания постов, системе приходится обрабатывать и доставлять огромное количество информации.</li>
</ul>
<li>Публичные блоги называют Tumblelog&#39;ами, они не так динамичны и легко кэшируются.</li>
<li>Сложнее всего масштабировать Dashboard, страницу, где пользователи в реальном времени читают что нового у блоггеров, на которых они подписаны.</li>
<ul>
<li>Кэширование практически бесполезно, так как для активных пользователей запросы редко повторяются.</li>
<li>Информация должна отображаться в реальном времени, быть целостной и не &laquo;задерживаться&raquo;.</li>
<li>Около 70% просмотров страниц приходится именно на Dashboard, почти все пользователи им пользуются.</li>
</ul>
</ul>
<h2>Старая архитектура</h2>
<div>
<ul>
<li>Когда проект только начинался, Tumblr размещался в Rackspace и последние выдавали каждому блогу с собственным доменом A-запись. Когда они переросли Rackspace, они не смогли полноценно мигрировать в новый датацентр, в том числе из-за количества пользователей. Это было в 2007 году, но у них по-прежнему часть доменов ведут на Rackspace и перенаправляются в новый датацентр с помощью HAProxy и Varnish. Подобных &laquo;унаследованных&raquo; проблем у проекта очень много.</li>
<li>С технической точки зрения проект прошел по пути типичной эволюции <strong>LAMP</strong>:</li>
<ul>
<li>Исторически разработан на <strong>PHP</strong>, все началось с веб-сервера, сервера баз данных и начало потихоньку развиваться.</li>
<li>Чтобы справляться с нагрузкой они начали использовать memcache, затем добавили кэширование целых страниц и статических файлов, потом поставили HAProxy перед кэшами, после чего сделали партиционирование на уровне <strong>MySQL</strong>, что сильно облегчило им жизнь.</li>
<li>Они делали все, чтобы выжать максимум из каждого сервера.</li>
<li>Было разработано два сервиса на C: генератор уникальных идентификаторов на основе HTTP и libevent, а также <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://engineering.tumblr.com/post/7819252942/staircar-redis-powered-notifications"  target="_blank">Staircar</a>, использующий Redis для обеспечения уведомлений в реальном времени на Dashboard.</li>
</ul>
<li>Dashboard использует подход &laquo;<a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www2.parc.com/istl/projects/ia/papers/sg-sigir92/sigir92.html"  target="_blank">разбрасывать-собирать</a>&raquo;, так как из-за отсортировоннасти данных по времени традиционные схемы партиционирования работали не очень хорошо. По их прогнозам текущая реализация позволит им рости еще в течении полугода.</li>
</ul>
</div>
<h2>Новая архитектура</h2>
<div>
<ul>
<li>Приоритетным направлением стали технологии, основанные на JVM, по причине более быстрой разработки и доступности квалификацированных кадров. Мотивация несколько спорная, особенно  если учесть, что речь идет в первую очередь о <a href="/tag/scala/" target="_blank">Scala</a>, а не о <a href="/tag/scala/" target="_blank">Java</a>.</li>
<li>Основная цель&nbsp;&mdash; вынести все из PHP приложения в отдельные сервисы, что сделает его лишь тонким клиентом к внутреннему API.</li>
<li>Почему выбор пал именно на <strong>Scala</strong> и <strong>Finagle</strong>?</li>
<ul>
<li>Многие разработчики имели опыт с Ruby и PHP, так что Scala был привлекательным (цитата, логики мало)</li>
<li><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/twitter/finagle"  target="_blank">Finagle</a> был одним из основных факторов в пользу JVM: это библиотека, разработканная в Twitter, которая решает большинство распределенных задач вроде маршрутизации запросов и обнаружение/регистрацию сервисов&nbsp;&mdash; не пришлось реализовывать это все с нуля.</li>
<li>В Scala не принято использовать общие состояния, что избавляет разработчиков от забот с потоками выполнения и блокировками.</li>
<li>Им очень нравится Thrift в роли программного интерфейса из-за его высокой производительности (он кроссплатформенный и к JVM никак не относится)</li>
<li>Нравится <a href="/tag/netty/" target="_blank">Netty</a>, но не хочется связываться с Java, еще один аргумент в пользу Scala.</li>
<li>Рассматривали <a href="/tag/node-js/" target="_blank">Node.js</a>, но отказались так как под JVM проще найти разработчиков, а также из-за отсутствия стандартов, &laquo;лучших практик&raquo; и большого количества качественно протестированного кода.</li>
</ul>
<li>Старые внутренние сервисы также переписываются с C + libevent на Scala + Fingle.</li>
<li>Был создан общий каркас для построения внутренних сервисов:</li>
<ul>
<li>Много усилий было приложено для автоматизации управления распределенной системой.</li>
<li>Создан аналог скаффолдинга&nbsp;&mdash; используется некий шаблон для создания каждого нового сервиса.</li>
<li>Все сервисы выглядят одинаково с точки зрения системного администратора: получение статистики, мониторинг, запуск и остановка реализованы одинаково для всех сервисов.</li>
<li>Созданы простые инструменты для сборки сервисов без вникания в детали используемых стандартных решений.</li>
</ul>
<li>Используется 6 внутренних сервисов, над которыми работает отдельная команд. На запуск сервиса с нуля уходит около 2-3 недель.</li>
<li>Новые, нереляционные СУБД, такие как HBase и Redis, вводятся в эксплуатацию, но основным хранилищем по-прежнему остается сильно партиционированный MySQL.</li>
<li>HBase исользуется для сервиса сокращенных ссылок для постов, а также всех исторических данных и аналитики. HBase хорошо справляется с ситуациями, где необходимы миллионы операций записи в секунду, но он не достаточно стабилен, чтобы полностью заменить проверенное временем решение на MySQL в критичных для бизнеса задачах.</li>
<li>Партиционированный MySQL плохо справляется с отсортированными по времени данными, так как один из серверов всегда оказывается существенно более &laquo;горячим&raquo;, чем остальными. Также сталкивались с значительными задержками в репликации из-за большого количества параллельных операций добавления данных.</li>
<li>Используется 25 серверов Redis с 8-32 процессами на каждом, что означает порядка 300-400 экземпляров Redis в сумме.</li>
<ul>
<li>Используется для уведомлений в реальном времени на Dashboard (о событиях вроде &laquo;кому-то понравился Ваш пост&raquo;).</li>
<li>Высокое соотношений операций записи к операциям чтения сделало MySQL не очень подходящим кандидатом.</li>
<li>Уведомления не так критичны, их потеря допустима, что позволило отключить персистентность Redis.</li>
<li>Был создан интерфейс между Redis и отложенными задачами в Finagle.</li>
<li>Сервис коротких ссылок также использует Redis как кэш, а HBase для постоянного хранения.</li>
<li>Вторичный индекс Dashboard также построен вокруг Redis.</li>
<li>Redis также используется для хранения задач Gearman, для чего был написан memcache proxy на основе Finale.</li>
<li>Постепенно отказываются от memcached в пользу Redis в роли основного кэша. Производительность у них сопоставима.</li>
</ul>
<li>Внутренним сервисам необходим доступ к потоку всех событий в системе (создание, редактирование и удаление постов, нравится или не нравится и т.п.), для чего была созданна внутренняя шина сообщений <em>(англ. firehose, пожарный шланг)</em>:</li>
<ul>
<li>Пробовали использовать в этой роли Scribe, но так как оно по сути свелось к пропусканию логов через grep в реальном времени&nbsp;&mdash; нагрузки оно не выдержало.</li>
<li>Текущая реализация основана на Kafka, решению аналогичной задачи от LinkedIn на Scala.</li>
<li>MySQL также не рассматривался из-за большой доли операций записи.</li>
<li>Внутри сервисы используют HTTP потоки для чтения данных, хотя Thrift интерфейс также используется.</li>
<li>Поток сообщений хранит события за последнюю неделю с возможностью указать момент времени с которого считывать данные при открытии соединения.</li>
<li>Поддерживается абстракция &laquo;группы потребителей&raquo;, которая позволяет группе клиентов вместе обрабатывать один поток данных вместе и независимо, то есть одно и то же сообщение не попадет дважды к клиентам из одной группы.</li>
<li>ZooKeeper используется для периодического сохранения текущей позиции каждого клиента в потоке.</li>
</ul>
<li>Новая архитектура Dashboard основана на принципе ячеек или ящиков входящих сообщений:</li>
<ul>
<li>Каждая &laquo;ячейка&raquo; отвечает за группу пользователей и читает новые события с шины сообщений, если один из её пользоваиелей-подопечных подписан на автора только что опубликованного поста, то пост добавляется в &laquo;почтовый ящик&raquo; подписанного пользователя.</li>
<li>Когда пользователь заходит в Dashboard его запрос попадает в его ячейку, которая возвращает ему нужную часть непрочитанных постов.</li>
<li>Каждая ячейка состоит из трех групп серверов:</li>
<ul>
<li>HBase для постоянного хранения копий постов и почтовых ящиков;</li>
<li>Redis для кэширование свежих данных;</li>
<li>Сервис, читающий данные из шины и предоставляющий доступ к ящикам посредством Thrift.</li>
</ul>
<li>В HBase используется две таблицы:</li>
<ul>
<li>Отсортированный <strong>список идентификаторов постов</strong> для каждого пользователя в ячейке, именно в том виде, как они будут отображены в итоге.</li>
<li><strong>Копии всех постов</strong> по идентификаторам, что позволяет выдать все данные для отрисовки Dashboard без обращений к серверам вне одной ячейки.</li>
</ul>
<li>Ячейки представляют собой независимые единицы, что позволяет легко масштабировать систему при росте числа пользователей.</li>
<li>Платой за относительно безболезненность масштабирования является чрезвычайная избыточность данных: при том что ежедневно создается лишь 50Гб постов, суммарный объем данных в ячейках растет на 2.7Тб в день.</li>
<li>Альтернативой было бы использование общего кластера со всеми постами, но тогда он бы стал единственной точкой отказа и потребовалось бы делать дополнительные удаленные запросы. Помимо этого выигрыш по объему был бы не велик&nbsp;&mdash; списки идентификаторов занимают значительно больше места, чем сами посты.</li>
<li>Пользователи, которые подписаны или на которых подписаны миллионы других пользователей, обрабатываются отдельно&nbsp;&mdash; страницы с их постами генерируются не заранее (как описывалось выше), а при поступлении запроса&nbsp;&mdash; это позволяет не тратить впустую много ресурсов (этот подход называется выборочная материализация, <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://research.yahoo.com/pub/3203"  target="_blank">подробнее</a>).</li>
<li>Количество пользователей в одной ячейке позволяет управлять балансом между уровнем надежности и стоимостью содержания этой подсистемы.</li>
<li>Параллельное чтение их шины сообщений оказывает серьезную нагрузку на сеть, в дальнейшем из ячеек можно будет составить иерархию: только часть будет читать напрямую из шины сообщений, а остальным сообщения будут ретранслироваться.</li>
</ul>
<li>Tumblr географически по-прежнему находится в одном датацентре (если не считать незначительное присутствие в Rackspace), распределение по нескольким лишь в планах.</li>
</ul>
<h2>Развертывание</h2>
<div>
<ul>
<li>Начиналось как несколько rsync-скриптов для распространения PHP-приложения. Как только машин стало больше 200 такой подход стал занимать слишком много времени.</li>
<li>Следующий вариант был основан на <a href="/tag/capistrano/" target="_blank">Capistrano</a>: были созданы три стадии процесса развертывания (разработка, тестирование, боевой). Неплохо справлялся с десятками серверов, но на сотнях также был слишком медленным, так как основывался на SSH.</li>
<li>Итоговый вариант основан на <strong>Func</strong>, решении от <a href="/tag/redhat/" target="_blank">RedHat</a>, позволившим заменить <a href="/tag/ssh/" target="_blank">SSH</a> на более легковесный протокол.</li>
</ul>
</div>
</div>
<h2>Разработка</h2>
<ul>
<li>Поначалу философия была такова, что каждый мог использовать любые технологии, которые считал уместным. Но довольно скоро пришлось стандартизировать стек технологий, чтобы было легче нанимать и вводить в работу новых сотрудников, а также для более оперативного решения технических проблем.</li>
<li>Каждый разработчик имеет одинаковую заранее настроенную рабочую станцию, которая обновляется посредством <a href="/tag/puppet/" target="_blank">Puppet</a>:</li>
<ul>
<li>Настроена публикация изменений, тестирование и развертывание новых версий.</li>
<li>Разработчики используют vim и Textmate.</li>
</ul>
<li>Новый PHP код систематически инсптируется другими разработчиками.</li>
<li>Внутренние сервисы подвергаются непрерывному тестированию посредством Jenkins.</li>
</ul>
<h2>Структура команд</h2>
<div>
<ul>
<li>Проект разбит на 6 команд:</li>
<ul>
<li><strong>Инфраструктура:</strong> все, что ниже 5 уровня по модели OSI&nbsp;&mdash; маршрутизация, TCP/IP, DNS, оборудование и.т.п.</li>
<li><strong>Платформа:</strong> разработка основного приложения, партиционирование SQL, взаимодействие сервисов.</li>
<li><strong>Надежность (SRE):</strong> сфокусирована на текущие потребности с точки зрения надежности и масштабируемости.</li>
<li><strong>Сервисы:</strong> занимается более стратегической разработкой того, что понадобится через один-два месяца.</li>
<li><strong>Эксплуатация:</strong> отвечает за обнаружение и реагирование на проблемы, плюс тонкая настройка.</li>
</ul>
</ul>
</div>
<h2>Найм</h2>
<ul>
<li>На интервью они обычно избегают математики и головоломок, основной упор идет в основном именно на те вещи, которым придется заниматься кандидату.</li>
<li>Основной вопрос: будет ли он успешно решать поставленные задачи? Цель в том, чтобы найти отличных людей, а не в том, чтобы никого не брать.</li>
<li>Разработчиков обязательно просят привести пример своего кода, даже во время телефонных интервью.</li>
<li>Во время интервью кандидатов не ограничивают в наборе инструментов, можно даже гуглить.</li>
<li>Поиск людей с опытом в крупных проектах достаточно сложен, так как всего нескольких компаниях по всему миру решают подобные проблемы.</li>
</ul>
<h2>Подводим итоги</h2>
<ul>
<li>Автоматизация&nbsp;&mdash; ключ к успеху крупного проекта.</li>
<li>При партиционировании MySQL может масштабироваться, но лишь при преобладании операций чтения.</li>
<li>Redis с отключенной персистентностью легко может заменить memcached.</li>
<li>Scala достойно себя проявляет в роли языка программирования для внутренних сервисов, во многом благодаря обширной Java-экосистеме.</li>
<li>Внедряйте новые технологии постепенно, поначалу работать с HBase и Redis было очень болезненно, они были включены в основной стек технологий только после испытаний в некритичных сервисах и подпроектах, где цена ошибки не так велика.</li>
<li>Проект должен строиться вокруг навыков его команды, а не наоборот.</li>
<li>Нужно нанимать людей только если они вписываются в команду и в состоянии довести работу до результата.</li>
<li>При выборе технологического стека одну из ключевых ролей играет доступность соответствующих специалистов на кадровом рынке.</li>
<li>Читайте публикации и статьи в блогах. Ключевые аспекты архитектуры, включая &laquo;ячейки&raquo; и частичную материализацию были позаимствованы из внешних источников.</li>
<li>Поспрашивайте своих коллег, кто-то из них мог общаться с специалистами из <a href="http://www.insight-it.ru/masshtabiruemost/arkhitektura-facebook/"  target="_blank">Facebook</a>, <a href="http://www.insight-it.ru/masshtabiruemost/arkhitektura-twitter-dva-goda-spustya/"  target="_blank">Twitter</a>, <a href="http://www.insight-it.ru/masshtabiruemost/arkhitektura-google-2011/"  target="_blank">Google</a> или <a href="http://www.insight-it.ru/net/scalability/arkhitektura-linkedin/"  target="_blank">LinkedIn</a>&nbsp;&mdash; если нет прямого доступа, всегда можно получить нужную информацию через одно-два &laquo;рукопожатия&raquo;.</li>
</ul>
<div class="pretty">
<p>Статья написана на основе <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://highscalability.com/blog/2012/2/13/tumblr-architecture-15-billion-page-views-a-month-and-harder.html"  target="_blank">интервью</a> <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.linkedin.com/in/bmatheny"  target="_blank">Blake Matheny</a>, директора по разработке платформы Tumblr.</p>
<p>Я никогда не брал подобных интервью, но с удовольствием попробовал бы: если у Вас есть высоконагруженный интернет-проект и Вы готовы поделиться особенностями его технической реализации, а заодно и слегка прорекламировать его среди 14 тысяч <a href="/feed/" target="_blank">подписчиков</a> <strong>Insight IT</strong>&nbsp;&mdash; свяжитесь со мной, попробуем организовать.</p>
</div>
<p>&nbsp;</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=wGSgRJMbnQs:veZxvgPOrlo:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=wGSgRJMbnQs:veZxvgPOrlo:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=wGSgRJMbnQs:veZxvgPOrlo:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=wGSgRJMbnQs:veZxvgPOrlo:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=wGSgRJMbnQs:veZxvgPOrlo:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/wGSgRJMbnQs" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/masshtabiruemost/arkhitektura-tumblr/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/masshtabiruemost/arkhitektura-tumblr/</feedburner:origLink></item>
		<item>
		<title>Jinja2</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/I71GgA8XKZc/</link>
		<comments>http://www.insight-it.ru/programmirovanie/python/jinja2/#comments</comments>
		<pubDate>Sun, 19 Feb 2012 14:39:50 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[compass]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[Jinja2]]></category>
		<category><![CDATA[less]]></category>
		<category><![CDATA[Memcached]]></category>
		<category><![CDATA[sass]]></category>
		<category><![CDATA[webassets]]></category>
		<category><![CDATA[клиентская оптимизация]]></category>
		<category><![CDATA[шаблон]]></category>
		<category><![CDATA[шаблонизация]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1636</guid>
		<description><![CDATA[Я уже много раз упоминал в комментариях и других постах, что когда мне приходится программировать, последние пару лет я чаще всего использую Python. При этом так забавно получилось, что в рубрике &#171;Программирование&#187; об этом языке практически ни слова, даже подрубрики не было. Сегодня я попробую потихоньку начать исправлять данную ситуацию, речь пойдет об одном из самых [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/umJWcJUTJb84YrWJo89edoTVM1c/0/da"><img src="http://feedads.g.doubleclick.net/~a/umJWcJUTJb84YrWJo89edoTVM1c/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/umJWcJUTJb84YrWJo89edoTVM1c/1/da"><img src="http://feedads.g.doubleclick.net/~a/umJWcJUTJb84YrWJo89edoTVM1c/1/di" border="0" ismap="true"></img></a></p><p>Я уже много раз упоминал в комментариях и других постах, что когда мне приходится программировать, последние пару лет я чаще всего использую <a href="/tag/python/" target="_blank">Python</a>. При этом так забавно получилось, что в рубрике &laquo;<a href="/category/programmirovanie/" target="_blank">Программирование</a>&raquo; об этом языке практически ни слова, даже подрубрики не было. Сегодня я попробую потихоньку начать исправлять данную ситуацию, речь пойдет об одном из самых продвинутых шаблонизаторов под Python&nbsp;&mdash; <strong>Jinja2</strong>. Встречаем!<span id="more-1636"></span></p>
<h2>Введение</h2>
<p>Я хочу рассказать о том, что можно интересного сделать с помощью Jinja2, по вопросам как именно&nbsp;&mdash; лучше обратиться к <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://jinja.pocoo.org/docs/"  target="_blank">официальной документации</a>. Хотя на конкретные вопросы по реализации в комментариях с удовольствием отвечу.</p>
<p>Прежде чем перейти к делу, хочу напомнить что имеется ввиду под словом <em>шаблонизатор</em>: механизм для создания HTML-страниц путем заполнения HTML-шаблонов динамическими данными, получаемыми из СУБД или внешних источников. Шаблонизатор предоставляет некую надстройку над синтаксисом HTML для создания шаблонов и API для их использования.</p>
<h2>Базовый функционал</h2>
<p>Многое из этого можно увидеть и в альтернативных реализациях шаблонизаторов, так что ничего особенного:</p>
<ul>
<li><strong>{{ &#8230; }}</strong> позволяет распечатать значение переменной или какого-то выражения, синтаксис достаточно свободный&nbsp;&mdash; можно обращаться к элементам коллекций, методам/атрибутам объектов и.т.п.</li>
<li><strong>{% &#8230; %}</strong> позволяет вызвать дополнительные теги, среди которых условные выражения, различные варианты циклов и многое другое.</li>
<li>Присутствиет концепция <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://jinja.pocoo.org/docs/templates/#builtin-filters"  target="_blank"><strong>фильтров</strong></a>, сильно напоминающих UNIX pipes: начинается все с переменной или выражения, после чего можно через символ <strong>|</strong> указать как её обработать перед выводом в итоговый документ. Например, <strong>{{ foo|lower }}</strong> выведет содержимое строки foo в нижнем регистре. Как и в pipes, из фильтров можно делать цепочки.</li>
<li>Механизм <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://jinja.pocoo.org/docs/templates/#template-inheritance"  target="_blank"><strong>наследования</strong></a> позволяет избежать избыточности в коде. В коде шаблона выделяются именованные блоки тегом {% block &#8230; %}, после чего шаблон-потомок может переопределить содержимое блоков шаблона-родителя произвольным образом. Типичный пример использования:</li>
<ul>
<li>Создается базовый шаблон страницы, состоящий из основного каркаса страницы  и всех общих для всего сайта элементов (ссылки на файлы стилей, общие <a href="/tag/javascript/" target="_blank">JavaScript</a> файлы и библиотеки, какие-то мета теги, title по-умолчанию)</li>
<li>В базовом шаблоне содержимое каждой части выделяется в именованный блок (как минимум шапка, место под контент, 1-2 сайдбара и подвал), иногда рядом со стилями и скриптами оставляют по пустому блоку на случай, если шаблонам-наследникам потребуется что-то специфичное.</li>
<li>Если какой-то блок будет содержать одну и ту же информацию на большинстве страниц сайта, то её тоже обычно помещают в базовый шаблон.</li>
<li>Создается по шаблону-потомку на каждый тип используемых на сайте страниц, в которых переопределяется как правило  (но далеко не всегда) только блок с конткентом и заголовок страницы. Из шаблонов-потомков также можно составить иерархию в случае, если у них есть много общей информации.</li>
<li>Стоит упомянуть, что есть альтернативный механизм включения (include) шаблонов по-аналогии с <a href="/tag/php/" target="_blank">PHP</a>-файлами, но я достойных применений ему не нашел.</li>
</ul>
<li>Очень много внимания уделено <strong>экранированию символов</strong>, хотя особо на него надеяться не стоит&nbsp;&mdash; с точки зрения безопасности намного важнее фильтровать попадающие на сайт данные, а не выводимые в шаблонах. Хотя как дополнительная подстраховка не помешает.</li>
<li>Простая <strong><a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://jinja.pocoo.org/docs/extensions/#i18n-extension"  target="_blank">интеграция с gettext</a> </strong>придется кстати интернациональным проектам.</li>
<li>Опциональное <strong>считывание шаблонов с диска</strong> при каждом запросе страницы незаменимо при разработке.</li>
</ul>
<h2>Производительность</h2>
<p>Сравнительные тесты производительности шаблонизаторов под Python довольно условны, очень многое зависит от конкретных шаблонов и динамических данных. Тем не менее, во всех из них <strong>Jinja2</strong> определенно не в аутсайдерах, в топ5 вполне стабильно.</p>
<p>Шаблоны компилируются в байткод для последующего использования, с этой особенностью связаны два момента, которые спользовать:</p>
<ul>
<li>Байткод можно <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://jinja.pocoo.org/docs/api/#bytecode-cache"  target="_blank">хранить</a> в <a href="/tag/memcached/" target="_blank">memcached</a> или любом другом внешним хранилище, достаточно лишь реализовать минимальный get/set интерфейс.</li>
<li>Доступен опциональный <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://jinja.pocoo.org/docs/intro/#more-speed-with-markupsafe"  target="_blank">модуль</a> на <a href="/tag/c/" target="_blank">C</a>, который берет на себя часть работы по заполнению шаблонов, что делает этот процесс несколько быстрее.</li>
</ul>
<h2>Расширяемость</h2>
<p><strong>Jinja2</strong> предоставляет широкие возможности по подключению дополнительных модулей и самостоятельной реализации и использованию аналогов любых компонентов системы. Можно разрабатывать и подключать свои фильтры, проверки, глобальные функции, загрузчики шаблонов, расширения и пр. Пройдемся по потенциальным вариантам использования этих возможностей на благо проекта, в основном в целях клиентской оптимизации.</p>
<h3>Webassets</h3>
<p>Этот <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://github.com/miracle2k/webassets"  target="_blank">проект</a> позволяет делать с подключаемыми внешними Javascript и CSS файлами практически все, что угодно. В типичном варианте использования используется тег <strong>{% assets %}</strong> для:</p>
<ul>
<li>Указания списка изначальных CSS/JS файлов, для конкатенации и последующей обработки.</li>
<li>Указания окружения ссылки на итоговый файл, т.е. как именно должен выглядеть тег &lt;script&gt; или &lt;style&gt;.</li>
<li>Списка фильтров, для минимизации или других преобразований кода.</li>
<li>Возможно использование <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://sass-lang.com/"  target="_blank">sass</a> или <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://lesscss.org/"  target="_blank">less</a> файлов вместо чистого CSS.</li>
<li>Отключение конкатенации и минимизации при разработке доступно изменением одного флага.</li>
</ul>
<div>В итоге вопрос с подготовкой минимизированных статических файлов становится полностью автоматическим.</div>
<p>Доступна интегрирация и с другими Python шаблонизаторами, в Jinja2 он подключается просто как расширение.</p>
<h3>Минимизация HTML</h3>
<p>Этот вопрос решается путем наследования от поставляемого вместе с шаблонизатором загрузчика шаблонов. API позволяет делать между чтением шаблона и генерацией байткода что угодно с текстом шаблона, например можно пропустить все через примитивную регулярку (вернее через несколько) и свернуть тем самым весь HTML в одну строку. Хочется обратить внимание, что осуществляется этот процесс очень редко (особенно при использовании кэша байт кода), так что можно делать на этом этапе даже сильно ресурсоемкие преобразования текста.</p>
<h3>Другой формат данных</h3>
<p>В одном из моих проектов при первом заходе на сайт или при отключенном JS сервер полностью отрисовывал страницу, а при переходах по ссылкам внутри сайта делался AJAX-запрос и сервер выдавал какие блоки нужно обновить и каким содержимым в формате JSON. Про клиентскую часть всего этого дела можно легко написать отдельную здоровую статью, так что в подробности не вдаюсь.</p>
<p>Да, наверняка многие скажут, что в этой ситуации надо было использовать универсальные шаблоны для JS и серверной части, но на Jinja2 такое тожно можно реализовать, с той лишь разницей, что пришлось гонять по сети не только сами данные, но и часть HTML-разметки (что, порой, тоже не плохо). Реализуется как и минимизация HTML посредством переопределения загрузчика страниц, который использовался вместо стандартного, если запрос пришел через AJAX.</p>
<h3>Сэкономим еще пару байт</h3>
<p>С подобного рода оптимизацией не заборачивается, наверное и 0.01% интернет-проектов, но я в свое время как-то увлекся и написал штуку для &laquo;выжимания&raquo; десятка-другого байт с большинства страниц и CSS/JS файлов. &laquo;Проблема&raquo; состоит в следующем:<strong> классы и идентификаторы</strong>, использующиеся в HTML, в &laquo;культурных&raquo; проектах <strong>имеют</strong> хотя бы отдаленно <strong>человекочитаемые названия</strong>, что почти всегда означает их длинность, что, учитывая их частую повторяемость в коде, в свою очередь негативно влияет на <em>итоговый размер HTML/JS/CSS документов</em>. Теоретическое решение лежит на поверхности: использовать &laquo;длинные&raquo; идентификаторы и классы в HTML при разработке, а при развертывании на публику <strong>переименовывать их в &laquo;короткие&raquo;</strong>: <em>a, b, c, &#8230;, aa, ab, ac, &#8230;</em></p>
<p>На практике же все несколько сложнее: есть масса проблем с префиксностью и суффиксностью, в JS классы иногда неотличимы от других строк с точками (зависят от контекста), некоторые классы и идентификаторы генерируются динамически&nbsp;&mdash; на них прийдется либо забивать, либо обрабатывать индивидуально, и это далеко не все&#8230;</p>
<p>Если у кого-то возникнет желание тоже сделать что-то подобное средствами <em>Jinja2</em>, то советую &laquo;вмешиваться&raquo; в обработку JS/CSS посредством дополнительного фильтра в <em>Webassets</em>, а сами шаблоны редактировать как и в предыдущих разделах при считывании их с диска.</p>
<h3>Спрайты и обработка изображений</h3>
<p>Создание <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D1%80%D0%B0%D0%B9%D1%82_(%D0%BA%D0%BE%D0%BC%D0%BF%D1%8C%D1%8E%D1%82%D0%B5%D1%80%D0%BD%D0%B0%D1%8F_%D0%B3%D1%80%D0%B0%D1%84%D0%B8%D0%BA%D0%B0)"  target="_blank">спрайтов</a> как таковых не предусмотрено, так как по сути это не по части шаблонизатора. Но есть вариант подключить их к Webassets, например через интеграцию <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto//tag/ruby/" target="_blank">Ruby</a>-проектом <a href="https://github.com/chriseppstein/compass"  target="_blank">compass</a> (у которого есть плагин-генератор спрайтов).</p>
<p>Если говорить просто о уменьшении размеров изображений, то это легко делается средствами самого <a href="/tag/python/" target="_blank">Python</a> и с шаблонизатором практически не взаимодействует: достаточно обрабатывать изображения при загрузке их пользователями и держать статические изображения &laquo;в форме&raquo;.</p>
<h3>Идеи для фильтров и глобальных функций</h3>
<p>Список встроенных в Jinja2 фильтров, функций и проверок, хоть и обширен, но того, что нужно, там зачастую не оказывается. Вот несколько примеров, чего в нем нет:</p>
<ul>
<li>Форматирования даты/времени по шаблону</li>
<li>Фильтрации HTML с белым списком тегов</li>
<li>Получения атрибута объекта с неизвестным заранее именем (getattr)</li>
<li>Вывода строки в режиме &laquo;первая&nbsp;&mdash; заглавная, остальные&nbsp;&mdash; прописные</li>
<li>Генерации часто используемых HTML-тегов, например <strong>&lt;a href=&raquo;&#8230;&#187;&gt;&lt;/a&gt;</strong></li>
</ul>
<p>Не смотря на то, что реализация каждого из вышеизложенных пунктов занимает буквально чуть-чуть строк кода, меня всегда удивляло отсутствие подобных достаточно примитивных вещей &laquo;в комплекте&raquo;.</p>
<h2>Подводим итоги</h2>
<p><strong>Jinja2</strong>&nbsp;&mdash; отличный инструмент для тех проектов, которым важно не просто донести какую-то информацию до пользователей, а сделать это приятно и быстро, как для себя, так и для пользователя. Гибкость и расширяемость этого движка шаблонов позволяет адаптировать его под нужды любого проекта, а также воспользоваться в полной мере всеми возможностями современной <em>клиентской оптимизации</em>.</p>
<p>С удовольствием бы опубликовал упоминавшиеся в статье куски кода в opensource, но для этого нужно взять себя в руки и состряпать из них что-то &laquo;отчуждаемое&raquo; от тех проектов, для которых оно писалось.</p>
<p><em>В комментариях предлагаю обсудить Jinja2 в сравнении с другими шаблонизаторами: кто какими альтернативами пользуется, в чем видит сильные и слабые стороны, какой фактор оказывается решающим при выборе движка для конкретного проекта?</em></p>
<p><strong>Спасибо за внимание, <a href="/tag/feed/" target="_blank">подписавшись на Insight IT</a> можно узнавать о новых материалах одним из первых <img src='http://www.insight-it.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </strong></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=I71GgA8XKZc:A5ElXyGGFgA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=I71GgA8XKZc:A5ElXyGGFgA:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=I71GgA8XKZc:A5ElXyGGFgA:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=I71GgA8XKZc:A5ElXyGGFgA:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=I71GgA8XKZc:A5ElXyGGFgA:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/I71GgA8XKZc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/python/jinja2/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/programmirovanie/python/jinja2/</feedburner:origLink></item>
		<item>
		<title>Как работает epoll?</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/HUpufE3siC8/</link>
		<comments>http://www.insight-it.ru/tekhnologii/kak-rabotaet-epoll/#comments</comments>
		<pubDate>Fri, 17 Feb 2012 12:27:13 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Технологии]]></category>
		<category><![CDATA[epoll]]></category>
		<category><![CDATA[kqueue]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[sockets]]></category>
		<category><![CDATA[разработка]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1562</guid>
		<description><![CDATA[Слово epoll сейчас определенно на слуху, в первую очередь благодаря росту популярности неблокирующих HTTP-серверов. При этом мало кто пытается разобраться в том, что, собственно, за ним стоит и почему использующие этот механизм продукты, среди которых достойное место занимают, например, nginx, node.js и Tornado, так значительно выигрывают в производительности у ближайших альтернатив. Хотите копнуть глубже? О [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/gOR7Vx9v6cCQpJLLpNb_bXBZL4U/0/da"><img src="http://feedads.g.doubleclick.net/~a/gOR7Vx9v6cCQpJLLpNb_bXBZL4U/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/gOR7Vx9v6cCQpJLLpNb_bXBZL4U/1/da"><img src="http://feedads.g.doubleclick.net/~a/gOR7Vx9v6cCQpJLLpNb_bXBZL4U/1/di" border="0" ismap="true"></img></a></p><p>Слово epoll сейчас определенно на слуху, в первую очередь благодаря росту популярности неблокирующих HTTP-серверов. При этом мало кто пытается разобраться в том, что, собственно, за ним стоит и почему использующие этот механизм продукты, среди которых достойное место занимают, например, <a href="/tag/nginx/" target="_blank">nginx</a>, <a href="/tag/node-js/" target="_blank">node.js</a> и <a href="/tag/tornado/" target="_blank">Tornado</a>, так значительно выигрывают в производительности у ближайших альтернатив. Хотите копнуть глубже?<br />
<span id="more-1562"></span></p>
<h2>О чем пойдет речь?</h2>
<ul>
<li><strong>epoll </strong>является масштабируемой <em>неблокирующей</em> системой уведомления о собятиях ввода-вывода в <a href="/tag/linux/" target="_blank">Linux</a>. В отличии от более старых механизмов, у epoll время срабатывания не зависит от количества открытых файловых дескрипторов.</li>
<li><strong>epoll</strong> используется для обработки событий <em>неблокирующих</em> TCP-сокетов, операционная система оповещает приложение когда один из сокетов &laquo;под наблюдением&raquo; готов получить или отправить сообщение. В традиционном же подходе на каждый сокет выделяется поток выполнения (thread), который блокируется до возвращения обращения к соответствующему сокету.</li>
</ul>
<p><div class="frame">Сразу хочу предупредить, хоть на практике <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto//tag/epoll/" target="_blank">epoll</a> и используется чаще, существуют и альтернативные реализации схожего подхода, например <a href="/tag/kqueue/" target="_blank">kqueue</a> в BSD системах. Конечные продукты обычно используют библиотеку, абстрагирующуе низкоуровневые вызовы, наиболее распросраненные - <a href="/goto/http://software.schmorp.de/pkg/libev.html"  rel="nofollow" target="_blank">libev</a> и <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://libevent.org/"  target="_blank">libevent</a>.</div><!-- .frame (end) --></p>
<h2>Что это дает?</h2>
<ul>
<li>Не нужно впустую тратить <em>системные ресурсы</em> на создание, уничтожение и поддержания пула потоков выполнения.</li>
<li>Один системный процесс может поддерживать существенно большее <em>количество TCP-соединений.</em></li>
<li><em>Длительные соединения</em>, по которым редко поступают сообщения, не держат заблокированный поток и потребляют минимум системных ресурсов.</li>
<li>Отсутствие проблем с <em>синхронизацией</em> пула потоков и доступом к общей памяти.</li>
<li>Возможность (но не необходимость) без дополнительных сложностей держать в памяти процесса какое-то общее состояние, если приложение того требует.</li>
</ul>
<h2>Обратная сторона медали</h2>
<ul>
<li>Потоки выполнения в блокирующей модели имеют относительно короткий жизненный цикл и рано или поздно освобождают выделенную им память, процесс обработки неблокирующих соединений живет существенно дольше и намного более уязвим для <em>утечек памяти</em>.</li>
<li>Использование одного системного процесса без пула потоков выполнения ограничивает приложение<em> использованием лишь одного процессорного ядра</em>, что делает такой подход менее пригодным для приложений, в значительной мере использующих вычислительные ресурсы. В большинстве же случаев приемлемым решением является запуск нескольких одинаковых копий приложения на одном сервере по количеству процессорных ядер.</li>
<li><em>Ошибки в коде</em> могут негативно повлиять на работу всего процесса приложения, в то время как в блокирующей модели потоки выполнения обычно достаточно изолированы друг от друга.</li>
</ul>
<h2>На пальцах</h2>
<p>Вернемся к изначальному вопросу статьи: <em>Как работает epoll?</em> Давайте попробуем разобрать на простом примере.</p>
<p>Представьте себе <strong>пиццерию </strong><em>(физический сервер)</em>. <strong>Вы</strong> <em>(приложение или HTTP-сервер)</em> получаете <strong>заказы</strong> <em>(обращения на сокет, например HTTP-запрос)</em> на выпечку <strong>пиццы</strong> <em>(ответы на обращение, например HTML-документы). </em>Есть два сценария, по которым можно их обрабатывать.</p>
<h3>Блокирующий (традиционный)</h3>
<p>Вы принимаете заказ, ставите пиццу в <strong>печь</strong><em> (системные ресурсы, в.т.ч. оперативная память, необходимые для обработки запроса) </em>и непрерыано наблюдаете за тем как пицца печется. Как только пицца готова&nbsp;&mdash; вы берете её и отдаете в руки <strong>заказчику </strong>(источник заказа, например браузер), после чего принимаете следующий заказ. При необходимости можно нанять <strong>помощников</strong> <em>(потоки выполнения, threads)</em>, чтобы следить за выпеканием пицц.</p>
<p>Вы ограничены как количеством печей, так и количеством помощников, которые могут поместиться в вашей пиццерии.</p>
<h3>Неблокирующий (epoll и аналоги)</h3>
<p>Вы принимаете заказ, ставит пиццу в печь и ставите <strong>таймер</strong> <em>(операционная система посредством epoll)</em>, чтобы узнать когда пицца испечется. После чего Вы возвращаетесь к приему заказов. Как только прозвенел таймер&nbsp;&mdash; Вы идете к соответствующей печи, достаете пиццу и отдаете заказчику, после чего снова возвращаетесь к приему заказов.</p>
<p>При таком подходе Вы ограничены лишь количеством печей и не нуждаетесь в помощниках, хотя если срабатывает несколько таймеров одновременно могут появлятся дополнительные задержки. В качестве бонуса легко готовить пиццы, требующие длительного времени выпекания.</p>
<h2>Заключение</h2>
<p>Как Вы уже догадались, цель этого поста не научить читателя работать с <strong>epoll</strong> напрямую или посредством распространенных библиотек (для большинства веб-разработчиков это не нужно), а дать общее представление о блокирующих и неблокирующих сокетах, принципах их работы и основных отличиях. При выборе ключевых технологий и проектировании архитектуры интернет-проекта эти вопросы определенно стоит иметь ввиду.</p>
<p>Буду рад услышать дополнения и поправки в комментариях, <a href="/feed/" target="_blank">до новых встреч</a>!</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=HUpufE3siC8:w_jFS_a5Hh0:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=HUpufE3siC8:w_jFS_a5Hh0:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=HUpufE3siC8:w_jFS_a5Hh0:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=HUpufE3siC8:w_jFS_a5Hh0:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=HUpufE3siC8:w_jFS_a5Hh0:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/HUpufE3siC8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/tekhnologii/kak-rabotaet-epoll/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/tekhnologii/kak-rabotaet-epoll/</feedburner:origLink></item>
		<item>
		<title>В поисках источников аудитории</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/NqGWajwHK1k/</link>
		<comments>http://www.insight-it.ru/life/wordpress/v-poiskakh-istochnikov-auditorii/#comments</comments>
		<pubDate>Thu, 16 Feb 2012 14:59:15 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[WordPress]]></category>
		<category><![CDATA[Habrahabr]]></category>
		<category><![CDATA[Wikipedia]]></category>
		<category><![CDATA[аудитория]]></category>
		<category><![CDATA[трафик]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1555</guid>
		<description><![CDATA[Эта заметка является небольшим &#171;криком души&#187;, прошу совета у постоянных читателей. Только что вот получил письмо о том, что некий модератор Википедии под псевдонимом bezik решил удалить все упоминания Insight IT с этого небезизвестного интернет-ресурса, долгие годы находившиеся в топ3 источников новых читателей. Чуть меньше года назад аналогичная история приключилась и с Хабром, где у меня [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/ST_CUGNvbfvOlufddM5QmcCXhPA/0/da"><img src="http://feedads.g.doubleclick.net/~a/ST_CUGNvbfvOlufddM5QmcCXhPA/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/ST_CUGNvbfvOlufddM5QmcCXhPA/1/da"><img src="http://feedads.g.doubleclick.net/~a/ST_CUGNvbfvOlufddM5QmcCXhPA/1/di" border="0" ismap="true"></img></a></p><p>Эта заметка является небольшим &laquo;криком души&raquo;, прошу совета у постоянных читателей. Только что вот получил письмо о том, что некий модератор Википедии под псевдонимом <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://ru.wikipedia.org/wiki/%D0%A3%D1%87%D0%B0%D1%81%D1%82%D0%BD%D0%B8%D0%BA:Bezik"  target="_blank">bezik</a> решил удалить все упоминания <strong>Insight IT</strong> с этого небезизвестного интернет-ресурса, долгие годы находившиеся в топ3 источников новых читателей.</p>
<p>Чуть меньше года назад аналогичная история приключилась и с Хабром, где у <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://m11.habrahabr.ru"  target="_blank">меня</a> при сильно положительной карме отправили &laquo;навечно в черновики&raquo; все посты и на несколько месяцев отправили в бан. На хабре модераторы анонимны, так что так и не выяснил кого за это все благодарить.</p>
<p>В итоге при довольно солидной базе подписчиков я теряю основные источники новых читателей&#8230; Казалось бы пишешь стоящий контент, люди интересуются, благодарят, а достаточно лишь одного человека, которому неоправданно выдали полномочия и которому не захотелось разбираться в значимости материала, и на ресурсе-источнике можно смело ставить крест. Как думаете, какие есть варианты решения или предотвращения подобных ситуаций?</p>
<p><em><strong>Хочется верить, что не все так печально и в социальных сетях и поисковых системах меня не забанят в обозримом будущем, но все же ими едиными сыт не будешь. Буду благодарен за любую помощь в поиске новых источников аудитории или, возможно, в решении ситуаций с Хабром и Википедией. Спасибо!</strong></em></p>
<p><em>UPDATE: В результате дискуссии в комментариях у Insight IT появилась своя страничка на Google+, <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/https://plus.google.com/b/108259869254766963941/" >добро пожаловать</a>!</em></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=NqGWajwHK1k:vKzZuMdBBgA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=NqGWajwHK1k:vKzZuMdBBgA:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=NqGWajwHK1k:vKzZuMdBBgA:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=NqGWajwHK1k:vKzZuMdBBgA:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=NqGWajwHK1k:vKzZuMdBBgA:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/NqGWajwHK1k" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/life/wordpress/v-poiskakh-istochnikov-auditorii/feed/</wfw:commentRss>
		<slash:comments>30</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/life/wordpress/v-poiskakh-istochnikov-auditorii/</feedburner:origLink></item>
		<item>
		<title>Redis: подробный обзор</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/zuBU33xXCEM/</link>
		<comments>http://www.insight-it.ru/tekhnologii/subd/redis-podrobnyjj-obzor/#comments</comments>
		<pubDate>Thu, 16 Feb 2012 11:49:21 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[СУБД]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Redis]]></category>
		<category><![CDATA[приемы]]></category>
		<category><![CDATA[разработка]]></category>
		<category><![CDATA[репликация]]></category>
		<category><![CDATA[технологии]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1539</guid>
		<description><![CDATA[На сегодняшний день ассортимент решений для хранения данных очень широк: от встраиваемых СУБД до кластерных распределенных систем. SQL перестал быть стандартом де-факто для доступа к данным, а альтернативные решения давно переросли примитивные хранилища пар ключ-значение. Сегодня я хочу вкратце рассказать об одном из таких решений, продукте, который нашел свое место во многих моих проектах за [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/oACYwbRAHD7OqYXPNBu-WeyNsuE/0/da"><img src="http://feedads.g.doubleclick.net/~a/oACYwbRAHD7OqYXPNBu-WeyNsuE/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/oACYwbRAHD7OqYXPNBu-WeyNsuE/1/da"><img src="http://feedads.g.doubleclick.net/~a/oACYwbRAHD7OqYXPNBu-WeyNsuE/1/di" border="0" ismap="true"></img></a></p><p>На сегодняшний день ассортимент решений для хранения данных очень широк: от встраиваемых СУБД до кластерных распределенных систем. SQL перестал быть стандартом де-факто для доступа к данным, а альтернативные решения давно переросли примитивные хранилища пар ключ-значение. Сегодня я хочу вкратце рассказать об одном из таких решений, продукте, который нашел свое место во многих моих проектах за последние годы, <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io"  target="_blank">Redis</a>.<br />
<span id="more-1539"></span></p>
<h2> О чем пойдет речь?</h2>
<p>Прежде чем вдаваться в детали, давайте разберемся чем по сути является главный герой этого поста?</p>
<ul>
<li><strong>Redis</strong> предлагает пять фиксированных примитивных структур данных, но <strong>не</strong> дает возможности создавать свои (по принципу таблиц или букетов).</li>
<li><strong>Redis</strong> держит все данные в памяти, но имеет гибко настраиваемый механизм сохранения их копии на диск.</li>
<li><strong>Redis</strong> реализован в виде однопоточного демона на <a href="/tag/c/" target="_blank">C</a>, общающегося по собственному текстово-бинарному TCP-протоколу, но без намеков на многопоточность и кластеризацию.</li>
</ul>
<p>Этих трех тезисов еще не достаточно, чтобы хорошо прочувствовать особенности и основные варианты использования Redis, но общая картинка уже начинает складываться. Интересно? Двигаемся дальше!</p>
<h2>Модель данных</h2>
<p>Модель данных у <a href="/tag/redis/" target="_blank">Redis</a> основана на принципе <em>пар ключ-значение</em>, с той лишь разницей, что значение может быть сложной структурой с дополнительными операциями. Пройдемся по каждой:</p>
<ul>
<li><strong>Строка: </strong>в самом случае значением может быть просто последовательность каких-то байт, о содержании которых Redis ничего не знает и знать не должен. По тому же принципу работает, скажем, <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto//unix-way/obzor-memcached/" target="_blank">memcached</a> и набор основных операций тот же: <a href="http://redis.io/commands/get"  target="_blank">get</a>/<a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/set"  target="_blank">set</a>/<a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/del"  target="_blank">del</a> и их групповые аналоги <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/mget"  target="_blank">mget</a>/<a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/mset"  target="_blank">mset</a>. Здесь же стоит упомянуть про возможность атомарного <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/incr"  target="_blank">инкремента</a> и получения <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/keys" >списка имеющихся в системе ключей</a>.</li>
<li><strong><strong><strong>Словарь/хэш: </strong></strong></strong>аналогичная структура данных есть во многих языках программирования, так что принцип наверняка знаком&nbsp;&mdash; за идентификатором (в языках программирования&nbsp;&mdash; <em>переменной</em>, а в Redis&nbsp;&mdash; <em>ключом</em>) стоит не одно значение, а подмножество пар ключ-значение с механизмом доступа, аналогичным основному множеству ключей в Redis. По сути команды, работающие со словарями отличаются от работающих со строками наличием первого параметра с идентификатором словаря и буквой h в начале, например <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/hget"  target="_blank">hget</a>, <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/hset"  target="_blank">hset</a>. Если воспринимать словарь как подмножество пар ключ-значений тяжело, то можно рассматривать их как <em>строки с полями</em>, то есть если в словарях использовать одинаковый набор внутренних ключей, то можно добиться абстракции на подобии таблиц в РСУБД.</li>
<li><strong><strong>Набор: </strong></strong>используется для хранения <em>множества</em> неотсортированных уникальных значений, предоставляя механизмы для работы с этими данными (команды на букву s), в частности стоит обратить внимание <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/sinter"  target="_blank">пересечение</a> и <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/sunion" >объединение</a> множеств. На практике же неотсортированные наборы удобно использовать для проверки уникальности каких-то данных, скажем e-mail&#39;ов зарегистрированных пользователей, время <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/sismember"  target="_blank">операции проверки наличия значения в наборе</a> не зависит от общего количества значений в наборе, аналогичным свойством обладает и <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/scard"  target="_blank">операция подсчета количества значений</a> в наборе&#8230;</li>
<li><strong>Отсортированный набор: </strong>сортировка в наборах осуществляется путем присваивания каждому значению условного ранга (score) в виде целого числа; значения в наборе будут автоматически храниться в порядке от минимального к максимального ранга. Доступные операции (на букву z) позволяют делать выборки значений <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/zrangebyscore"  target="_blank">с определеным диапазоном рангов</a> или <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/zrange"  target="_blank">по индексу</a> (если представить отсортированный набор в виде массива или списка), узнавать <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/zscore"  target="_blank">ранг</a> и <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/zrank"  target="_blank">индекс</a> по значению. Выглядит замысловато, но на практике довольно удобно. Их можно использовать в роли индекса в терминах РСУБД: например, в отсортированном наборе можно хранить идентификаторы пользователей, назначая в качестве ранга их возраст&nbsp;&mdash; в результате можно очень быстро делать выборки пользователей по возрасту, после чего по идентификаторам получать более подробную информацию о каждом, скажем командой <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/mget"  target="_blank">mget</a> или как-то еще.</li>
<li><strong>Список:</strong> в отличии от отсортированных наборов в списках значения хранятся просто по порядку. Операции (на букву l) позволяют эффективно добавлять и получать значения в начало, конец и по индексу (порядковому номеру), а также работать с последовательными участками списка. В результате на программном уровне можно реализовать любые абстракции работы с последовательностями, в том числе стек и любые очереди.</li>
</ul>
<p>При описании структур данных я использовал ссылки на <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/documentation"  target="_blank">официальную документацию</a>, где можно ознакомиться с <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands"  target="_blank">полным списком команд</a> и <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/clients"  target="_blank">доступными готовыми реализациями протокола</a> на различных языках программирования. У каждой команды есть подробное описание, в частности стоит обратить внимание на оценку времени выполнения по О-нотации.</p>
<p>В кажом сервере Redis может содержаться до 16 независимых пространств ключей, которые принято называть базами данных.</p>
<h2>Сохранность данных</h2>
<p>В Redis доступны четыре основных стратегии для сохраненния полученных:</p>
<ul>
<li><strong>Хранить только в памяти: </strong>по сути Redis из персистентной базы данных превращается в кэширующий сервер. Производительность в таком режиме максимальна и сравнима с часто используемым в этой роли <a href="/unix-way/obzor-memcached/" target="_blank">memcached</a>, в некоторых ситуациях его даже превосходит; плюс не забываем про более продвинутую структуру данных. Минус с высоким риском потери данных&nbsp;&mdash; очевиден, за производительность надо осознанно платить.</li>
<li><strong>Периодически сохранять на диск:</strong> <em>(по-умолчанию) </em>момент, когда Redis решает сделать очередную копию (snapshot) данных на диске, зависит от времени создания предыдущей и количества измененных ключей. При настройках по-умолчанию это случается раз в несколько минут (1-15).</li>
<li><strong>Лог транзакций: </strong>если потеря нескольких минут изменений данных неприемлема для приложения, есть опция синхронной записи каждого изменения в специальный append-only файл (лог).</li>
<li><strong>Репликация:</strong> в Redis поддержка репликации достаточно примитивна&nbsp;&mdash; каждому серверу можно указать мастера (командой <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/slaveof"  target="_blank">slaveof</a> или одноименной строкой в конфигурации), все изменения на мастере будут воспроизводиться и на подчиненном. Это можно использовать для построения более сложных схем сохранности данных, например: мастер работает в режиме &laquo;только в памяти&raquo;, а подчиненный&nbsp;&mdash; в режиме &laquo;лог транзакций&raquo;. У каждого сервера может быть только один мастер, но неограниченное количество подчиненных.</li>
</ul>
<div>Здесь же стоит упомянуть, что копия данных Redis на диске представляет собой бинарный файл (по-умолчанию dump.rdb), резервную копию которого можно сделать любыми средствами операционной системы, закинуть &laquo;в облако&raquo; наподобии Amazon S3 и.т.п. Восстановление происходит аналогичным образом скармливанием серверу файла в таком формате.</div>
<h2>Масштабируемость</h2>
<p>Как следует из третьего тезиса в начале этой статьи, ситуация печальна, как таковой поддержки масштабируемости в Redis нет, а клиенты умеют подключаться только к одному серверу. В сети давно ходят слухи о неком проекте <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/presentation/Redis_Cluster.pdf"  target="_blank">Redis Cluster</a>, который призван решить эту проблему, но пока еще не дорос до чего-то пригодного для использования в боевых условиях.</p>
<p>Но все не так плохо как кажется, этот проект предоставляет достаточно возможностей, чтобы масштабировать систему <em>своими руками</em> именно так, как представляется нужным конкретному проекту. Сразу хочется сказать, что не стоит бросаться воплощать все нижеизложенное в жизнь, если даже по самым оптимистичным прогнозам суммарный объем данных в Redis в вашем проекте не будет превышать 50-100 гигабайт. Какой-либо универсальной реализации нижеизложенного не существует, так как большая часть манипуляций осуществляется на стороне клиента, что потребовало бы реализации на всех возможных языках программирования.</p>
<p>Давайте пройдемся по основным приемам, которые можно реализовать на любом языке программирования с используем базового клиента Redis.</p>
<ul>
<li><strong>Много процессов Redis на одном физическом сервере: </strong>процессы Redis однопоточны и если ваш проект работает на физическом не антикварном оборудовании, то для использования всех ресурсов процессора потребуется запускать несколько процессов Redis на разных портах. Еще одна особенность, подталкивающая в пользу такого подхода&nbsp;&mdash; существенно большее потребление оперативной памяти 64-битной сборкой Redis, так как удваивается длина указателей, которых используется очень большое количество. 32-битная же сборка позволяет держать в памяти лишь примерно 3  гигабайта данных. <em>Альтернатива: много виртуальных машин с 1 ядром и 3 гигабайтами оперативной памяти.</em></li>
<li><strong>Распределение данных по ключам: </strong>так или иначе, у всех данных в Redis есть уникальный идентификатор, от которого можно взять хэш, а от него взять остаток от деления на константу&nbsp;&mdash; получим номер сервера в статичном списке <em>ip:port</em>, на котором нужную структуру данных можно найти. На клиенте держится по соединению на каждый используемый сервер Redis, выбирается нужный и отправляется запрос.</li>
<li><strong>Создание виртуальных баз данных (ВБД): </strong>у предыдущего подхода есть масса недостатков, связанных с перераспределением данных. Эту ситуацию можно решить введя понятие виртуальных БД, то есть брать в качестве константы для остатка не реальное число серверов, а достаточно большую степень двойки, скажем 65536 (будет удобно удваивать количество реальных серверов)&nbsp;&mdash; что далее будет считаться номером ВБД (который иногда удобно приписывать к ключу через разделитель). Виртуальные же базы необходимо по произвольному алгоритму распределить по запущенным процессам Redis и в простейшем случае указать это соотношение в конфигурационных файлах приложений-клиентов.</li>
<li><strong>Маршрутизатор по виртуальным базам данных:</strong> в предыдущем приеме подразумевалось, что распределение виртуальных БД по серверам фиксированное, но очень скоро захочется иметь механизм для перераспределения данных в кластере. Это может потребоваться при неравномерности нагрузки (к какой-то ВБД существенно больше запросов, чем к остальным) или, опять же, при расширении парка используемых серверов. Для реализации потребуется некое общее хранилище для информации о физическом расположении ВБД, это может быть как отдельный &laquo;особый&raquo; процесс Redis, так и какая-то встраиваемая СУБД, расположенная в общедоступном месте, есть и другие варианты. Добавление такой прослойки для определения расположения данных немного увеличит время отклика, но позволит более гибко перераспределять данные.</li>
<li><strong>Перераспределение данных: </strong>есть два подхода к переносу ВБД между двумя процессами Redis.</li>
<ul>
<li><em>В лоб:</em> некий процесс последовательно читает данные с сервера-источника и записывает в сервер-назначение, после окончания процесса отмечает в маршрутизаторе ВБД что данные теперь на новом сервере, удаляет данные на  сервере-источнике. Просто в реализации, не особо быстро при больших объемах.</li>
<li><em>Через репликацию: </em>существенно более хитрый вариант, о котором я услышал на одной из конференций от представителей команды Skype. Заключается в создании процесса, реализующего протокол репликации Redis с обоих сторон. То есть для сервера-источника он представляется подчиненным, а для сервера-назначения&nbsp;&mdash; мастером, при этом пропуская через себя только те ключи, которые относятся к нужной ВБД (требуется, чтобы её номер содержался в ключе). По окончании первичной репликации также отмечает в маршрутизаторе, что ВБД в новом месте. В протоколе репликация есть опция передачи начальной части данных бинарным файлом&nbsp;&mdash; этот вариант будет максимально быстрым, но потребуется разобраться с его форматом. Ребята из Skype обещали опубликовать в opensource свою реализацию этой схемы, но я её пока не видел&#8230;</li>
</ul>
<li><strong>Использование репликации:</strong> помимо увеличения максимального объема данных, стоит задумываться и о надежности системы, а также сохранности данных. Как работает репликация мы уже обсуждали, как добавить её к одной из вышеизложенных схем&nbsp;&mdash; дело техники. Самый простой вариант&nbsp;&mdash; хранить в маршрутизаторе или конфигурационном файле два физических адреса для каждой ВБД, основной и запасной. Запасной же настраивать как подчиненного с логом транзакций, а основной как мастера в режиме кэша или с очень редким созданием копии на диске. Сложные варианты репликации можно &laquo;позаимствовать&raquo; у изначально-распределенных СУБД вроде <a href="/tag/cassandra/" target="_blank">Cassandra</a> или <a href="/tag/riak/" target="_blank">Riak</a>, например можно хранить первую копию по указанному в маршрутизаторе адресу, вторую по адресе, который указан для следующей ВБД, а третью&nbsp;&mdash; для через-следующей, но в такой схеме нельзя будет использовать репликацию напрямую, потребуется &laquo;прослойка&raquo;, аналогичная описывавшейся в перераспределении данных через репликацию.</li>
</ul>
<h2>Бонусы</h2>
<p>В дополнение хотелось бы упомянуть три интересные особенности Redis, не совсем относящиеся к затрагивавшимся ранее вопросам, но определенно заслуживающих внимания:</p>
<ul>
<li><strong>Транзакции: </strong>клиент может отправить команду <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/multi"  target="_blank">multi</a>, что означает начало транзакции. Все последующие команды не изменят данные, пока сервер не получит команду <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/exec"  target="_blank">exec</a>, либо можно отменить транзакцию командой <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/discard"  target="_blank">discard</a>.</li>
<li><strong>Публикация и подписка на сообщения:</strong> помимо стандартного использование в роли хранилища данных, Redis может использоваться и как очередь сообщений. Клиент может включить <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/subscribe"  target="_blank">режим ожидания сообщений</a> по определенному ключу/каналу (команды доступа к данным в этом режиме недоступны), а также <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/publish" >отправлять произвольные сообщения</a> всем, кто подписан на определенный канал.</li>
<li><strong>Мониторинг: </strong>к каждому процессу Redis можно подключиться для <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/monitor" >просмотра всех поступающих команд</a> или только тех, которые <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://redis.io/commands/slowlog" >выполнялись больше N микросекунд</a>.</li>
</ul>
<h1>Заключение</h1>
<p><strong>Redis</strong>&nbsp;&mdash; определенно заслуживающий внимания opensource проект. Благодаря использованию нестандартных решений, он с одной стороны стал очень гибкой системой хранения данных, а с другой&nbsp;&mdash; ограничил круг проектов, где ему найдется место в стеке технологий.</p>
<p>Изложенные мной, <a href="http://www.insight-it.ru/author/"  target="_blank">Иваном Блинковым</a>, варианты использования и особенности Redis являются лишь небольшой частью того, что может быть реализовано с его помощью. Идеи по использованию реализованных в Redis структур данных в конкретных проектах порой оказываются очень неожиданными и практически нереализуемыми средствами большинства более консервативных СУБД.</p>
<p><i><b>Успехов в освоении современных технологий!</b></i></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=zuBU33xXCEM:p-M3P11mg0E:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=zuBU33xXCEM:p-M3P11mg0E:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=zuBU33xXCEM:p-M3P11mg0E:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=zuBU33xXCEM:p-M3P11mg0E:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=zuBU33xXCEM:p-M3P11mg0E:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/zuBU33xXCEM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/tekhnologii/subd/redis-podrobnyjj-obzor/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/tekhnologii/subd/redis-podrobnyjj-obzor/</feedburner:origLink></item>
		<item>
		<title>Вакансия: удаленный разработчик на Django</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/-8NpZEuyBtE/</link>
		<comments>http://www.insight-it.ru/life/vakansii/vakansiya-udalennyjj-razrabotchik-na-django/#comments</comments>
		<pubDate>Wed, 15 Feb 2012 13:09:31 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Вакансии]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[freelance]]></category>
		<category><![CDATA[вакансии]]></category>
		<category><![CDATA[разработчик]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1530</guid>
		<description><![CDATA[Для разработки новостных разделов и новой социальной сети, требуется удаленный разработчик Django. Основная цель&#160;&#8212; сделать качественный и интересный ресурс, с продвинутым интерфейсом. Требования Опыт работы с Django, не менее 2-х лет Опыт кастомизации административного интерфейса Django Опыт написания автоматизированных тестов Опыт работы с библиотекой jQuery не менее года Опыт проектирования  и работы с СУБД MySql [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/cfaBtZWyJs80x85RQPbENwGecCM/0/da"><img src="http://feedads.g.doubleclick.net/~a/cfaBtZWyJs80x85RQPbENwGecCM/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/cfaBtZWyJs80x85RQPbENwGecCM/1/da"><img src="http://feedads.g.doubleclick.net/~a/cfaBtZWyJs80x85RQPbENwGecCM/1/di" border="0" ismap="true"></img></a></p><p>Для разработки новостных разделов и новой социальной сети, требуется удаленный разработчик Django.<br />
Основная цель&nbsp;&mdash; сделать качественный и интересный ресурс, с продвинутым интерфейсом.<span id="more-1530"></span></p>
<h2>Требования</h2>
<ul>
<li>Опыт работы с Django, не менее 2-х лет</li>
<li>Опыт кастомизации административного интерфейса Django</li>
<li>Опыт написания автоматизированных тестов</li>
<li>Опыт работы с библиотекой jQuery не менее года</li>
<li>Опыт проектирования  и работы с СУБД MySql</li>
<li>Желание разбираться в чужом коде</li>
<li>Опыт использования SVN</li>
<li>Опыт использования Ajax</li>
</ul>
<h2>Задачи</h2>
<ul>
<li>Реализация серверной части</li>
<li>Разработка GUI на jQuery на основе готовых компонент</li>
<li>Разработка jQuery компонент</li>
<li>Расширение  и кастомизация Django Admin</li>
</ul>
<h2>Условия</h2>
<ul>
<li>Удаленная работа не менее 20 часов в неделю</li>
<li>Оплата от 400 рублей в час, зависит от уровня программиста</li>
<li>Оформления в штат нет, но проект долгосрочный</li>
</ul>
<h3><em>Резюме отправлять на <a href="mailto:vadim@vestlite.com">vadim@vestlite.com</a></em></h3>
<p><span style="font-size: x-small;">P.S.: В связи с возросшим спросом на размещение вакансий на Insight IT, стоимость <a href="http://www.insight-it.ru/razmeshhenie-vakansii-it-specialista/" >услуги</a> существенно возрасла, теперь подобые посты будут появляться реже.</span></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=-8NpZEuyBtE:bNh90WpYbdo:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=-8NpZEuyBtE:bNh90WpYbdo:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=-8NpZEuyBtE:bNh90WpYbdo:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=-8NpZEuyBtE:bNh90WpYbdo:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=-8NpZEuyBtE:bNh90WpYbdo:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/-8NpZEuyBtE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/life/vakansii/vakansiya-udalennyjj-razrabotchik-na-django/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/life/vakansii/vakansiya-udalennyjj-razrabotchik-na-django/</feedburner:origLink></item>
		<item>
		<title>Как дела у Plenty of Fish</title>
		<link>http://feedproxy.google.com/~r/insight-it/feed/~3/yHypBFi6mZY/</link>
		<comments>http://www.insight-it.ru/masshtabiruemost/kak-dela-u-plenty-of-fish/#comments</comments>
		<pubDate>Sun, 12 Feb 2012 14:04:21 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Масштабируемость]]></category>
		<category><![CDATA[Akamai]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[Plenty of Fish]]></category>
		<category><![CDATA[POF]]></category>
		<category><![CDATA[статистика]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=1520</guid>
		<description><![CDATA[Напомню, что Plenty of Fish&#160;&#8212; один из самых популярных в мире сайтов знакомств, созданный одним человеком и работающий на всего нескольких серверах и коммерческих технологиях. На Insight IT два года назад уже была подробная статья о технической реализации проекта, сегодня же я хочу  поделиться свежей информацией о том, как проект развивается. О проекте Долгое время, [...]]]></description>
			<content:encoded><![CDATA[
<p><a href="http://feedads.g.doubleclick.net/~a/lW291AbJYkP17Dm7EOnn07tUgAc/0/da"><img src="http://feedads.g.doubleclick.net/~a/lW291AbJYkP17Dm7EOnn07tUgAc/0/di" border="0" ismap="true"></img></a><br/>
<a href="http://feedads.g.doubleclick.net/~a/lW291AbJYkP17Dm7EOnn07tUgAc/1/da"><img src="http://feedads.g.doubleclick.net/~a/lW291AbJYkP17Dm7EOnn07tUgAc/1/di" border="0" ismap="true"></img></a></p><p>Напомню, что <a target="_blank" rel="nofollow" href="http://www.insight-it.ru/goto/http://www.pof.com/"  target="_blank">Plenty of Fish</a>&nbsp;&mdash; один из самых популярных в мире сайтов знакомств, созданный одним человеком и работающий на всего нескольких серверах и коммерческих технологиях. На<strong> Insight IT</strong> два года назад уже была <a href="http://www.insight-it.ru/masshtabiruemost/arkhitektura-plenty-of-fish/" >подробная статья о технической реализации проекта</a>, сегодня же я хочу  поделиться свежей информацией о том, как проект развивается.<br />
<span id="more-1520"></span></p>
<h2>О проекте</h2>
<ul>
<li>Долгое время, около 7 лет, проект полностью разрабатывался и поддерживался исключительно Маркусом, его создателем</li>
<li>Первый архитектор баз данных был нанят в июле 2011 года</li>
<li>На сегодняшний день команда составляет около 5 сотрудников</li>
<li>Используется стек технологий от Microsoft, а также Akamai в роли CDN</li>
<li>Расходы на техническое обеспечение проекта составляют порядка 70 тысяч долларов в месяц</li>
<li>Ежегодные доходы от рекламы измеряются миллионами долларов</li>
</ul>
<h2>Статистика на ноябрь 2011</h2>
<ul>
<li>6 миллиардов просмотров страниц</li>
<li>32 миллиарда просмотров изображений, пики до 22 тыс. в секунду, по ночам около 7 тыс.</li>
<li>30 миллиардов обращений к серверам мгновенного обмена сообщениями</li>
<li>Более 6 миллионов пользователей заходят на сайт каждое воскресенье</li>
<li>Количество веб-серверов увеличилось с 2 до 11, что обеспечивает практически двухкратный запас по используемым ресурсам</li>
</ul>
<h2>Выводы</h2>
<p>Чтобы проект был очень прибыльным для своих создателей, не всегда стоит торопиться стать большой компанией. Быть маленьким тоже выгодно:</p>
<ul>
<li>минимальные расходы на зарплаты, офис и техническое обеспечение,</li>
<li>востребованный продукт благодаря способности быстро подстраиваться под потребности рынка,</li>
<li>отсутствие необходимости делиться существенной частью прибыли с инвесторами,</li>
<li>достаточно простая архитектура и техническая реализация,</li>
<li>отсутствие потерь времени на бюрократию и совещания.</li>
</ul>
<div></div>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=yHypBFi6mZY:a_G2MSc8vJU:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=yHypBFi6mZY:a_G2MSc8vJU:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=yHypBFi6mZY:a_G2MSc8vJU:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/insight-it/feed?a=yHypBFi6mZY:a_G2MSc8vJU:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/insight-it/feed?i=yHypBFi6mZY:a_G2MSc8vJU:V_sGLiPBpWU" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/insight-it/feed/~4/yHypBFi6mZY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/masshtabiruemost/kak-dela-u-plenty-of-fish/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://www.insight-it.ru/masshtabiruemost/kak-dela-u-plenty-of-fish/</feedburner:origLink></item>
	</channel>
</rss><!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Served from: insight-it.ru @ 2012-05-17 19:33:11 -->

