<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	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/"
	>

<channel>
	<title>Блог Андрея Смирнова &#187; Twisted</title>
	<atom:link href="http://www.smira.ru/category/development/twisted-development/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.smira.ru</link>
	<description></description>
	<lastBuildDate>Wed, 24 Aug 2011 05:09:27 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>HighLoad-2010: Приемы разработки высоконагруженных приложений на Twisted/Python</title>
		<link>http://www.smira.ru/2010/10/28/highload-2010-twisted-python-development/</link>
		<comments>http://www.smira.ru/2010/10/28/highload-2010-twisted-python-development/#comments</comments>
		<pubDate>Thu, 28 Oct 2010 04:57:14 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Twisted]]></category>
		<category><![CDATA[Разработка]]></category>
		<category><![CDATA[highload]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[twisted]]></category>

		<guid isPermaLink="false">http://www.smira.ru/?p=593</guid>
		<description><![CDATA[25-26 октября состоялся HighLoad-2010, конференция получилось хорошей хотя бы потому, что было мало докладов ни о чем. Неплохой уровень, особенно было приятно увидеть &#171;профессоров&#187; PostgreSQL. Я выступал с докладом &#171;Приемы разработки высоконагруженных приложений на Twisted/Python&#187;. В докладе получилась (вполне сознательно) сборная солянка из советов и приемов о том, как писать приложения на Twisted (и похожих [...]]]></description>
			<content:encoded><![CDATA[<p>25-26 октября состоялся <a href="http://highload.ru/">HighLoad-2010</a>, конференция получилось хорошей хотя бы потому, что было мало докладов ни о чем. Неплохой уровень, особенно было приятно увидеть &laquo;профессоров&raquo; PostgreSQL.</p>

<p>Я выступал с докладом &laquo;Приемы разработки высоконагруженных приложений на Twisted/Python&raquo;. В докладе получилась (вполне сознательно) сборная солянка из советов и приемов о том, как писать приложения на Twisted (и похожих frameworkах). Из-за большого количества разных тем не получилось углубиться ни в одну, каюсь&#8230;</p>

<p>Тезисы:</p>

<ol>
<li>Запуск и шедулинг многих однопоточных процессов на одном сервере.</li>
<li>Key-value storage и приемы работы с ним.</li>
<li>Обслуживание сотен тысяч соединений на одном сервере.</li>
<li>HTTP-сервисы и балансировка нагрузки, локализация нагрузки.</li>
<li>Сбор статистики, интеграция с системой мониторинга.</li>
<li>Шина обмена сообщениями на примере AMQP.</li>
<li>Поиск и устранение memory leak.</li>
<li>Оптимизация по времени отклика и пропускной способности.</li>
<li>Мифы и правда о Python как языке разработки нагруженных приложений.</li>
</ol>

<p>Презентация:</p>

<div style="width:425px" id="__ss_5589368"><object id="__sse5589368" width="600" height="501"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=smirnov-twisted-python-101027232646-phpapp01&#038;stripped_title=smirnov-twistedpython&#038;userName=Smirnov.Andrey" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed name="__sse5589368" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=smirnov-twisted-python-101027232646-phpapp01&#038;stripped_title=smirnov-twistedpython&#038;userName=Smirnov.Andrey" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="600" height="501"></embed></object></div>

<p><br />
<br /></p>

<p>Утащить:</p>

<ul>
<li>В формате <a href="http://www.smira.ru/wp-content/uploads/2010/10/smirnov-twisted-python.pdf">PDF</a></li>
<li>Модуль <a href="http://github.com/smira/py-numa">py-numа</a>, который упоминался в докладе</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.smira.ru/2010/10/28/highload-2010-twisted-python-development/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Профайлинг Twisted-приложений</title>
		<link>http://www.smira.ru/2010/02/15/profiling-twisted-applications/</link>
		<comments>http://www.smira.ru/2010/02/15/profiling-twisted-applications/#comments</comments>
		<pubDate>Mon, 15 Feb 2010 20:11:56 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Twisted]]></category>
		<category><![CDATA[Разработка]]></category>
		<category><![CDATA[hotshot]]></category>
		<category><![CDATA[kcachegrind]]></category>
		<category><![CDATA[profiling]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[twisted]]></category>

		<guid isPermaLink="false">http://www.smira.ru/?p=525</guid>
		<description><![CDATA[Часто сам забываю, как профилировать легко и быстро Twisted-приложения (с некоторым изменениями подойдет для любых Python-приложений). Кроме Twisted нам понадобится еще KCachegrind Запускаем наше приложение с включенным профайлингом: twistd -n --savestats --profile=myprog.hotshot myprog Подаем нагрузку, профайл собирается. Теперь с помощью утилиты hotshot2cg из поставки KCachegrind превращаем hotshot-профайл в calltree-профайл, который уже умеет KCachegrind &#171;кушать&#187;. hotshot2cg [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.smira.ru/wp-content/uploads/2010/02/kcachegrind.png"><img src="http://www.smira.ru/wp-content/uploads/2010/02/kcachegrind-300x182.png" alt="" title="kcachegrind" width="300" height="182" class="alignleft size-medium wp-image-534" /></a> Часто сам забываю, как профилировать легко и быстро Twisted-приложения (с некоторым изменениями подойдет для любых Python-приложений). Кроме Twisted нам понадобится еще <a href="http://kcachegrind.sourceforge.net/">KCachegrind</a></p>

<p>Запускаем наше приложение с включенным профайлингом:</p>

<pre>
twistd -n --savestats --profile=myprog.hotshot myprog
</pre>

<p>Подаем нагрузку, профайл собирается. Теперь с помощью утилиты <code>hotshot2cg</code> из поставки KCachegrind превращаем hotshot-профайл в calltree-профайл, который уже умеет KCachegrind &laquo;кушать&raquo;.</p>

<pre>
hotshot2cg myprog.hotshot > myprog.calltree
</pre>

<p>Запускаем KCachegrind, открываем в нем полученный профайл:</p>

<pre>
kcachegrind myprog.calltree
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.smira.ru/2010/02/15/profiling-twisted-applications/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>HL++ (2009): Twisted Framework</title>
		<link>http://www.smira.ru/2009/10/13/hl-2009-twisted-framework/</link>
		<comments>http://www.smira.ru/2009/10/13/hl-2009-twisted-framework/#comments</comments>
		<pubDate>Tue, 13 Oct 2009 18:42:27 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Qik]]></category>
		<category><![CDATA[Twisted]]></category>
		<category><![CDATA[Разработка]]></category>
		<category><![CDATA[highload]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[twisted]]></category>

		<guid isPermaLink="false">http://www.smira.ru/?p=517</guid>
		<description><![CDATA[Сегодня выступал на HighLoad++ с докладом Twisted Framework &#8211; фреймворк для написания сетевых приложений в Python. Введение Последнее время в области web происходит смещение внимания с тяжелых application-серверов, которые тратят на обработку запроса сотни миллисекунд, а то и секунды, к более легковесным сервисам, передающим меньшие объемы данных с минимальной задержкой. Переход от генерации десятков и [...]]]></description>
			<content:encoded><![CDATA[<p>Сегодня выступал на <a href="http://highload.ru/">HighLoad++</a> с докладом <a href="http://www.highload.ru/papers2009/12261.html">Twisted Framework &#8211; фреймворк для написания сетевых приложений в Python</a>. </p>

<h2>Введение</h2>

<p>Последнее время в области web происходит смещение внимания с тяжелых application-серверов, которые тратят на обработку запроса сотни миллисекунд, а то и секунды, к более легковесным сервисам, передающим меньшие объемы данных с минимальной задержкой. Переход от генерации десятков и сотен килобайт HTML-кода в ответ на запрос к передаче изменений в данных, запакованных в JSON и измеряемых сотнями байт. В качестве примеров таких сервисов можно привести Gmail, FriendFeed, Twitter Live Search и т.п.</p>

<p>Для обеспечения минимальной задержки для пользователя необходимо либо поддерживать постоянное соединение (например, Adobe Flash, RTMP) или использовать технику HTTP long polling в сочетании с keep alive. Так или иначе на стороне сервера это приводит к появлению большого количества одновременных соединений (тысячи, десятки тысяч), по каждому из которых передается не такой большой объем данных. Эту ситуацию называют обычно проблемой <a href="http://www.kegel.com/c10k.html">C10k</a>.</p>

<p><span id="more-517"></span></p>

<p>Для обработки соединений архитектурный выбор на стороне сервера не такой большой: процесс на соединение, нить на соединение, комбинированный вариант процесс-нити или асинхронный ввод-вывод (возможно, в сочетании с дополнительными процессами или нитями). При наличии более 10 тысяч одновременных соединений с точки зрения расхода ресурсов совершенно невозможно представить создание 10 тысяч процессов; 10 тысяч нитей также вряд ли будет разумным решением. Необходимо дополнительно учесть, что при наличии такого большого числа соединений объем работы по каждому из них относительно невелик, большинство их них простаивают в ожидании поступления новых данных. Поэтому бóльшая часть процессов или нитей будет просто находиться в состоянии ожидания, расходуя впустую  системные ресурсы.
Асинхронный ввод-вывод позволяет осуществлять неблокирующийся сетевой ввод-вывод по тысячам открытых сокетов в рамках одной нити выполнения (одного процесса). Механизмы реализации в разных ОС разные, например: select(), poll(), epoll(), kqueue() и т.п. Примеры приложений, использующих асинхронный ввод-вывод:</p>

<ul>
<li>nginx (используются дополнительные процессы для обслуживания задач, требующих большего объема CPU);</li>
<li>haproxy;</li>
<li>memcached;</li>
<li>и другие.</li>
</ul>

<p>Тем не менее, асинхронный ввод-вывод не является универсальным решением: для сервера БД это вряд ли было бы хорошим способом организации обслуживания соединений, так как для обработки каждого запроса требуется большой объем дискового ввода-вывода и процессорного времени, что не позволяет это сделать в рамках одного процесса. </p>

<p><a href="http://www.twistedmatrix.com/">Twisted Framework</a> — это обширный набор классов и модулей для реализации асинхронных сетевых приложений. Twisted Framework — это:</p>

<ul>
<li>ядро, абстрагирующее все операции асинхронного ввода-вывода и использующее соответствующий механизм конкретной ОС;</li>
<li>концепция Deferred, которая позволяет реализовать в простой форме обслуживание запроса: асинхронные сетевые обращения (например, к БД, memcached), обработку ошибочных ситуаций; Deferred является аналогом обычных конструкций последовательного программирования для асинхронной модели программирования;</li>
<li>обширный набор уже реализованных сетевых протоколов: HTTP, DNS, SMTP, IMAP, memcached, Jabber, ICQ и т.д.; еще большее количество протоколов доступно в виде дополнительных модулей;</li>
<li>дополнительная инфраструктура: unit-testы с поддержкой Deferred, пулы нитей, процессов и т.д.; </li>
<li>качественная концепция разработки — полное покрытие unit-testами, строгий review любого изменения.</li>
</ul>

<p>Основная часть доклада будет посвящена конкретным примерам приложений, реализованными с помощью Twisted — с архитектурой, конкретными параметрами производительности, приемами оптимизации, преимуществами и недостатками Twisted для решения данной задачи:</p>

<ul>
<li><a href="http://www.smira.ru/2008/04/09/rit-2008/">RTMP-сервер pyFMS</a>, сервер вещаний сервиса Smotri.Com (сотни трансляций, десятки тысяч зрителей);</li>
<li>backend-сервер <a href="http://www.mdc.ru/">проекта MDC</a> &#8211; хранение и обработка истории общения пользователей, хранение настроек и т.п.;</li>
<li><a href="http://www.smira.ru/2009/07/12/qik-push-engine-api-private-beta/">Qik Push Engine</a> &#8211; сервер немедленной доставки изменений информации о видео, созданных пользователями сервиса, в том числе push-нотификация о появившихся live-стримах, масштабирование, обработка больших объемов информации.</li>
</ul>

<p>Дополнительная информация:</p>

<ul>
<li><a href="http://twistedmatrix.com/projects/core/documentation/howto/">Документация Twisted</a></li>
<li><a href="http://www.smira.ru/category/development/python-development/">О Python</a>, а также о <a href="http://www.smira.ru/category/development/twisted-development/">Twisted</a></li>
<li><a href="http://habrahabr.ru/blogs/twisted/">Блог на Хабрахабре про Twisted</a></li>
<li><a href="http://burus.org/2008/12/16/twisted-classic-examples/">Александр Бурцев о Twisted</a></li>
<li><a href="http://www.smira.ru/2009/02/10/deferred-async-programming/">Deferred в Twisted</a> и <a href="http://www.smira.ru/2009/02/24/more-about-deferred/">не только</a> .</li>
</ul>

<h2>Презентация</h2>

<div style="width:425px;text-align:left" id="__ss_2211313"><a style="font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;" href="http://www.slideshare.net/Smirnov.Andrey/twisted-framework-python-2211313" title="Twisted Framework - сетевые приложения в Python">Twisted Framework &#8211; сетевые приложения в Python</a><object style="margin:0px" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=smirnov-twisted-python-091013134034-phpapp02&#038;stripped_title=twisted-framework-python-2211313" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=smirnov-twisted-python-091013134034-phpapp02&#038;stripped_title=twisted-framework-python-2211313" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object><div style="font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;">View more <a style="text-decoration:underline;" href="http://www.slideshare.net/">documents</a> from <a style="text-decoration:underline;" href="http://www.slideshare.net/Smirnov.Andrey">Smirnov.Andrey</a>.</div></div>

<ul>
<li><a href='http://www.smira.ru/wp-content/uploads/2009/10/smirnov-twisted-python.pdf'>Скачать презентацию (PDF)</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.smira.ru/2009/10/13/hl-2009-twisted-framework/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>Qik Push Engine API: приглашаем разработчиков</title>
		<link>http://www.smira.ru/2009/07/12/qik-push-engine-api-private-beta/</link>
		<comments>http://www.smira.ru/2009/07/12/qik-push-engine-api-private-beta/#comments</comments>
		<pubDate>Sun, 12 Jul 2009 13:56:40 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Qik]]></category>
		<category><![CDATA[Twisted]]></category>
		<category><![CDATA[Разработка]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[engine]]></category>
		<category><![CDATA[push]]></category>
		<category><![CDATA[twisted]]></category>

		<guid isPermaLink="false">http://www.smira.ru/?p=461</guid>
		<description><![CDATA[Qik &#8211; это сервис стриминга (вещания) и загрузки видео с мобильных телефонов. Загруженное видео можно посмотреть на сайте или на его специальной версии с мобильного телефона. Доступна интеграция с другими сервисами, такими как Twitter, Facebook и другие. Клиенты для практически всех современных моделей телефонов: iPhone, Windows Mobile, Symbian, Android, Blackberry и другие. Qik Push Engine [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://qik.com/"><img src="http://www.smira.ru/wp-content/uploads/2009/07/qik_logo.png" alt="qik_logo" title="qik_logo" width="177" height="92" class="alignleft size-full wp-image-462" /></a> <a href="http://qik.com/">Qik</a> &#8211; это сервис стриминга (вещания) и загрузки видео с мобильных телефонов. Загруженное видео можно посмотреть на <a href="http://qik.com/">сайте</a> или на его <a href="http://m.qik.com/">специальной версии</a> с мобильного телефона. Доступна интеграция с другими сервисами, такими как Twitter, Facebook и другие. Клиенты для практически всех современных моделей телефонов: iPhone, Windows Mobile, Symbian, Android, Blackberry и другие.</p>

<p>Qik Push Engine &#8211; это механизм, который позволяет получать мгновенные оповещения о новых/изменившихся Qik-видео. Например, можно посмотреть постоянно обновляющийся список live-видео, все видео из района Новопеределкино или все видео со словом &laquo;кошка&raquo;. На основе Qik Push Engine API можно построить интересные приложения, интегрированные с Qik, или добавить функциональность в уже существующие. Можно написать собственную систему нотификации, desktop-widget 
или что-то еще.</p>

<p>Сегодня мы открываем API для работы c Qik Push Engine. Это первая ласточка в большом списке API, открывающих доступ к платформе стриминга Qik. Если вам интересно посмотреть Qik Push Engine в действии, заходите на одну из <a href="http://engine.qik.com/examples/all.videos.html">страниц примеров</a>.</p>

<p><span id="more-461"></span></p>

<h2>Основы работы с API</h2>

<p><a href="http://qikapi.pbworks.com/">Qik Push Engine API</a> доступно в виде набора удаленных процедур по протоколу JSON-RPC (over HTTP). В ближайшее время будет открыт REST-подобный интерфейс. Точкой входа для JSON-RPC является http://engine.qik.com/api/jsonrpc. Пока проект находится в закрытом бета-тестировании для доступа к API необходимо указать ключ разработчика, добавив параметр apikey: http://engine.qik.com/api/jsonrpc?apikey=xxxxxxx. Ключ разработчика можно получить, отправив письмо с запросом на <a href="mailto:api@qik.com">api@qik.com</a>.</p>

<p>Пример HTTP-сессии:</p>

<pre>
POST /api/jsonrpc
Host : engine.stage.qik.com
Content-Type:  application/json; charset=UTF-8
Content-Length: 46

{"method": "qik.session.create", "params": []}
</pre>

<p>Ответ:</p>

<pre>
Content-Type: text/json 

["a56c1603-1fbb-4140-8b35-8c36abbd8b27"]
</pre>

<h2>Пример подписки на поток событий</h2>

<p>Здесь и далее я буду использовать простую запись вызова команд, которая похожа на вызов обычных функций, опуская детали JSON-RPC взаимодействия. Итак, пусть мы хотим подписаться на список всех live-стримов, которые есть данный момент.</p>

<p>Первым делом создадим сессию:
<pre>
qik.session.create() -> "a56c1603-1fbb-4140-8b35-8c36abbd8b27"
</pre></p>

<p>Подписываемся в рамках сессии на view всех публичных live-стримов, при этом указываем максимальное количество элементов во view (limit). Элементы view будут упорядочены в порядке убывания даты начала стрима (самые новые первыми):
<pre>
qik.stream.subscribe&#95;public_live("a56c1603-1fbb-4140-8b35-8c36abbd8b27", 10) -> 
 [ "174/('PublicLiveStreamView', 'Stream', [('stoptime', None)], 'alllive;limit=10')", [ ... ] ]
</pre></p>

<p>В ответ на запрос мы получаем массив из двух элементов: первым является ключ подписки (длинная строка, начинающаяся с &laquo;174/..&raquo;). Не ищите смысла в ключе подписки, его необходимо просто сохранить, он потребуется в дальнейшем.</p>

<p>Вторым элементом является начальное состояние view, то есть текущий список live-стримов в нашем случае. Мы получим не больше 10 элементов (так как указали limit 10 при вызове метода <code>qik.stream.subscribe_public_live</code>). Состояние view выглядит примерно следующим образом:</p>


<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #009900;">&#91;</span>
   <span style="color: #009900;">&#123;</span><span style="color: #3366CC;">&quot;url&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;http://qik.com/video/2158468&quot;</span><span style="color: #339933;">,</span> 
    <span style="color: #3366CC;">&quot;live&quot;</span><span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">,</span> 
    <span style="color: #3366CC;">&quot;user_id&quot;</span><span style="color: #339933;">:</span> <span style="color: #CC0000;">340699</span><span style="color: #339933;">,</span> 
    <span style="color: #3366CC;">&quot;small_thumbnail_url&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;http://media.qik.com/media.thumbnails.128/c8ad8fe065ad4ad7ac8491874c043eac.jpg&quot;</span><span style="color: #339933;">,</span> 
    <span style="color: #3366CC;">&quot;title&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;Untitled&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;duration&quot;</span><span style="color: #339933;">:</span> <span style="color: #CC0000;">0</span><span style="color: #339933;">,</span>
    <span style="color: #3366CC;">&quot;created_at&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;2009-07-11 15:56:03&quot;</span><span style="color: #339933;">,</span> 
    <span style="color: #3366CC;">&quot;views&quot;</span><span style="color: #339933;">:</span> <span style="color: #CC0000;">0</span><span style="color: #339933;">,</span> 
    <span style="color: #3366CC;">&quot;id&quot;</span><span style="color: #339933;">:</span> <span style="color: #CC0000;">2158468</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> 
&nbsp;
   <span style="color: #009900;">&#123;</span><span style="color: #3366CC;">&quot;url&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;http://qik.com/video/2158466&quot;</span><span style="color: #339933;">,</span> 
    <span style="color: #3366CC;">&quot;live&quot;</span><span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">,</span> 
    <span style="color: #3366CC;">&quot;user_id&quot;</span><span style="color: #339933;">:</span> <span style="color: #CC0000;">340119</span><span style="color: #339933;">,</span> 
    <span style="color: #3366CC;">&quot;small_thumbnail_url&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;http://media.qik.com/media.thumbnails.128/984c33e1ead441038f315e1fff109fc5.jpg&quot;</span><span style="color: #339933;">,</span> 
     <span style="color: #3366CC;">&quot;title&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;Testando!&quot;</span><span style="color: #339933;">,</span> 
     <span style="color: #3366CC;">&quot;duration&quot;</span><span style="color: #339933;">:</span> <span style="color: #CC0000;">0</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;created_at&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;2009-07-11 15:55:34&quot;</span><span style="color: #339933;">,</span> 
     <span style="color: #3366CC;">&quot;views&quot;</span><span style="color: #339933;">:</span> <span style="color: #CC0000;">0</span><span style="color: #339933;">,</span> 
     <span style="color: #3366CC;">&quot;id&quot;</span><span style="color: #339933;">:</span> <span style="color: #CC0000;">2158466</span><span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#93;</span></pre></div></div>


<p>Смысл большинства полей очевиден, приведу лишь некоторые комментарии:</p>

<ul>
<li>все даты приведены в UTC;</li>
<li>поле <code>duration</code> хранит длительность видео в секундах.</li>
</ul>

<p>Так как мы подписались на live-стримы, у всех видео <code>live == true</code>.</p>

<p>Теперь нам необходимо получать новые события для тех view, на которые мы подписались. Для этого используются HTTP long polling-запросы. Клиент отправляет запрос серверу, а сервер отправляет ответ, когда появляются события или когда истечет таймаут, если событий не появилось. </p>

<p>Для реализации long polling в цикле вызываем метод <code>qik.session.get_events</code>, указывая желаемый таймаут (не рекомендуется использовать таймаут более 90 секунд).</p>

<pre>
qik.session.get_events("a56c1603-1fbb-4140-8b35-8c36abbd8b27", 60) -> 
  [ {...} ,{...} ]
</pre>

<p>В ответ мы можем получить пустой массив событий, если истек таймаут, или некоторый набор событий следующего вида:</p>


<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #009900;">&#91;</span>
   <span style="color: #009900;">&#123;</span>
     <span style="color: #3366CC;">&quot;action&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;update&quot;</span><span style="color: #339933;">,</span> 
     <span style="color: #3366CC;">&quot;old&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#123;</span><span style="color: #3366CC;">&quot;id&quot;</span><span style="color: #339933;">:</span> <span style="color: #CC0000;">104252</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;title&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;Untitled&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;views&quot;</span><span style="color: #339933;">:</span> <span style="color: #CC0000;">0</span><span style="color: #339933;">,</span> ...<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> 
     <span style="color: #3366CC;">&quot;obj&quot;</span><span style="color: #339933;">:</span> <span style="color: #009900;">&#123;</span><span style="color: #3366CC;">&quot;id&quot;</span><span style="color: #339933;">:</span> <span style="color: #CC0000;">104252</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;title&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;driving to key largo&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;views&quot;</span><span style="color: #339933;">:</span> <span style="color: #CC0000;">5</span><span style="color: #339933;">,</span> ...<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> 
     <span style="color: #3366CC;">&quot;key&quot;</span><span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;175/('PublicRecentStreamView', 'Stream', [], 'allrecent;limit=50')&quot;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
<span style="color: #009900;">&#93;</span></pre></div></div>


<p>В каждом событии обязательно передается следующая инормация:</p>

<ul>
<li><code>key</code> &#8211; ключ подписки, это то же самое значение, которое мы получили при подписке на view, ключ позволяет отличать события для разных view в рамках одной сессии;</li>
<li><code>action</code> &#8211; действие (изменение), произошедшее с view:

<ul>
<li><code>ping</code> &#8211; ничего не произошло, просто view сообщает, что все хорошо <img src='http://www.smira.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </li>
<li><code>update</code> &#8211; элемент view изменился (как в примере выше), передается старое и новое состояние объекта в полях <code>old</code> и <code>obj</code></li>
<li><code>insert</code> &#8211; во view добавился новый элемент, его состояние будет записано в поле <code>obj</code></li>
<li><code>delete</code> &#8211; элемент исчез из view (может быть, видео было удалено, или просто перестало удовлетворять критериям view, или ушло за границу &laquo;limit&raquo;), последнее состояние объекта будет записано в поле <code>obj</code>.</li>
</ul></li>
</ul>

<p>В ответ на события мы можем обновить информацию на экране для пользователя, выполнить какие-либо еще действия. Но обязательно надо отправить новый запрос <code>qik.session.get_events</code>, чтобы получить новые события.</p>

<p>В конце работы не забываем &laquo;убраться&raquo; за собой, убиваем сессию:</p>

<pre>
qik.session.destroy("a56c1603-1fbb-4140-8b35-8c36abbd8b27") 
</pre>

<p>Замечания:</p>

<ul>
<li>В рамках одной сессии можно подписаться на произвольное количество view. </li>
<li>Если это необходимо, можно авторизовать сессию от имени пользователя с помощью <code>qik.session.authorize</code>. </li>
<li>Существует большое количество вспомогательных запросов, которым сессия вообще не требуется, например <code>qik.user.public_profile</code>.</li>
<li>Можно отписаться от view при помощи конмады<code>qik.session.unsubscribe</code>.</li>
</ul>

<h2>Reference JS-клиент</h2>

<p>Для демонстрации возможностей Qik Push Engine и проверки его работы был реализован легкий JavaScript-клиент. Его можно увидеть в работе на примерах, полный список которых приведен ниже.</p>

<p>Если вы запустите пример в Firefox с включенным Firebug, в консоли Firebug будет появляться подробная информация о выполняемых запросах к Qik Push Engine API, полученных ответах и т.п.</p>

<p>Исходный код клиента доступен в необфусцированном виде, со всеми комментариями. Код предоставляет по принципам public domain, то есть можете делать с ним все, что захотите &#8211; использовать в своих приложениях, модифицировать, переписывать на другие языкаи программирования и т.п.</p>

<p>Пробежимся быстро по компонентам JS-клиента.</p>

<h3><a href="http://engine.qik.com/examples/js/prototype.js">prototype.js</a></h3>

<p>Совершенно стандартный <a href="http://www.prototypejs.org/">Prototype</a>, ничего интересного.</p>

<h3><a href="http://engine.qik.com/examples/js/deferred.js">deferred.js</a></h3>

<p><code>Deferred</code> &#8211; это одна из основных концепций Twisted Framework (http://twistedmatrix.com). Deferred не является обязательным для взаимодействия с Qik Push Engine API, но позволяет сильно упростить сложные взаимодействия в асинхронном коде. </p>

<p>В качестве дополнительного материала о Deferred можно почитать про <a href="http://www.smira.ru/2009/02/10/deferred-async-programming/">основы работы с Deferred</a>, <a href="http://www.smira.ru/2009/02/24/more-about-deferred/">более сложные случаи использования Deferred</a>, <a href="http://www.smira.ru/2009/05/29/deferred-in-javascript-for-prototype/">данную реализацию Deferred для Prototype</a>, а также взглянуть на Deferred в других JS-фреймворках: <a href="http://mochikit.com/doc/html/MochiKit/Async.html">MochiKit</a>, 
 <a href="http://www.dojotoolkit.org/book/dojo-book-0-9/part-3-programmatic-dijit-and-dojo/miscellaneous/communication-between-threads-do">Dojo</a>.</p>

<p><code>DeferredManager</code> &#8211; простая конструкция на основе Deferred, позволяет запускать не более N асинхронных действий в один момент времени.</p>

<h3><a href="http://engine.qik.com/examples/js/api.js">core.js</a></h3>

<p>Ядро клиента, объявляется namespace <code>QikEngine</code>, отладочные функции, определение конфигурации, создания объекта для доступа к API и т.п.</p>

<h3><a href="http://engine.qik.com/examples/js/api.js">api.js</a></h3>

<p><code>QikEngine.API</code> &#8211; простая обертка JSON-RPC API Qik Push Engine, возвращающая <code>Deferred</code> в качестве результата любого обращения к API. Через <code>Deferred</code> же отправляются ошибки и результаты выполнения методов.</p>

<p><code>QikEngine.Session</code> &#8211; класс, оборачивающий понятие сессии, создание, уничтожение, авторизация в рамках сессии.</p>

<h3><a href="http://engine.qik.com/examples/js/events.js">events.js</a></h3>

<p><code>QikEngine.Events</code> &#8211; получение новых событий с помощью HTTP long polling, технология, похожая на BOSH/Comet. Распределение событий подписчикам (конкретным view). </p>

<h3><a href="http://engine.qik.com/examples/js/view.js">view.js</a></h3>

<p><code>QikEngine.View</code> &#8211; абстрактный класс, представляющий view на стороне клиента. Обслуживание обновлений, отображение в элементах HTML, обработка событий и т.п. Списки элементов хранятся прямо в DOM-контейнере в виде HTML-узлов.</p>

<p>Потомки <code>QikEngine.View</code> для реализации конкретных view:  <code>QikEngine.PublicUserStreamView</code>, <code>QikEngine.PublicRecentStreamsView</code> и т.д.</p>

<h3><a href="http://engine.qik.com/examples/js/updater.js">updater.js</a></h3>

<p>Очень простой интерфейс между данными, полученными из Qik Push Engine (например, информацией о стримах) и HTML-элементами. Адаптация данных, например, перевод дат из UTC в локальный формат, форматирование значений и т.п.</p>

<h3><a href="http://engine.qik.com/examples/js/usercache.js">usercache.js</a></h3>

<p><code>QikEngine.UserCache</code> &#8211; механизм загрузки информации о пользователях (по их ID). Использует <code>DeferredManager</code> для ограничения количества параллельных запросов.</p>

<h2>Что делать дальше?</h2>

<ul>
<li>смотрите <a href="http://qikapi.pbworks.com/">описание API</a>;</li>
<li>пишите письмо на api@qik.com, расскажите о своем проекте и получайте ключ для доступа к API.</li>
</ul>

<h2>Полный список примеров</h2>

<ul>
<li><a href="http://engine.qik.com/examples/all.videos.html">все последние видео на Qik/live-видео</a></li>
<li><a href="http://engine.qik.com/examples/profile.html">открытые стримы пользователя</a></li>
<li><a href="http://engine.qik.com/examples/user.videos.html">профайл пользователя с точки зрения другого авторизованного пользователя</a></li>
<li><a href="http://engine.qik.com/examples/user.home.html">домашняя страница пользователя: собственные стримы, followerы, стримы от following</a></li>
</ul>

<p>Во всех примерах списки видео обновляются автоматически при внесении изменений на сайте или с мобильного телефона, например, если открою доступ Васе к моему приватному видео, он тут же увидит это видео в списке моих стримов и т.п. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.smira.ru/2009/07/12/qik-push-engine-api-private-beta/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AMQP по-русски</title>
		<link>http://www.smira.ru/2009/06/08/amqp-in-russian/</link>
		<comments>http://www.smira.ru/2009/06/08/amqp-in-russian/#comments</comments>
		<pubDate>Mon, 08 Jun 2009 07:33:57 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[MDC]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Twisted]]></category>
		<category><![CDATA[Разработка]]></category>
		<category><![CDATA[СпамоБорец]]></category>
		<category><![CDATA[amqp]]></category>
		<category><![CDATA[message]]></category>
		<category><![CDATA[queue]]></category>
		<category><![CDATA[twisted]]></category>
		<category><![CDATA[txamqp]]></category>
		<category><![CDATA[разработка]]></category>

		<guid isPermaLink="false">http://www.smira.ru/?p=392</guid>
		<description><![CDATA[Сегодня довольно мало информации о протоколе AMQP (Advanced Message Queueing Protocol) и его применении, особенно русском языке. А вообще это &#8211; замечательный, уже достаточно широко поддерживаемый открытый протокол для передачи сообщений между компонентами системы с низкой задержкой и на высокой скорости. При этом семантика обмена сообщениями настраивается под нужды конкретного проекта. Такие решения существовали и [...]]]></description>
			<content:encoded><![CDATA[<p><img alt="AMQP logo" src="http://www.smira.ru/wp-content/uploads/2009/06/global.logo_.jpeg" title="AMQP logo" class="alignleft" width="71" height="71" /> Сегодня довольно мало информации о протоколе <a href="http://www.amqp.org/">AMQP</a> (Advanced Message Queueing Protocol) и его применении, особенно русском языке. А вообще это &#8211; замечательный, уже достаточно широко поддерживаемый открытый протокол для передачи сообщений между компонентами системы с низкой задержкой и на высокой скорости. При этом семантика обмена сообщениями настраивается под нужды конкретного проекта. Такие решения существовали и ранее, но это первый стандарт, для которого существует большое количество свободных реализаций.</p>

<p>Основная идея состоит в том, что отдельные подсистемы (или независимые приложения) могут обмениваться произвольным образом сообщениями через AMQP-брокер, который осуществляет маршрутизацию, возможно гарантирует доставку, распределение потоков данных, подписку на нужные типы сообщений. В качестве классических примеров обычно приводятся финансовые приложения, связанные, например, с доставкой потребителям информации о курсах ценных бумаг в режиме реального времени, также возможно RPC-взаимодействие двух подсистем, которые не имеют связи друг с другом (взаимодействие через общий протокол AMQP) и так далее и тому подобное.</p>

<p>Сегодня тема доставки информации в реальном времени является крайне актуальной (достаточно вспомнить хотя бы Twitter, Google Wave). И здесь системы передачи сообщений могут служить внутренним механизмом обмена данными, который обеспечивает доставку данных (изменений данных) клиентам.</p>

<p>Я не ставлю своей целью сегодня рассказать о том, как писать приложения для AMQP. Хочу лишь немного рассказать о том, что это совсем не страшно, не очень сложно, и действительно работает, хотя стандарт находится еще в развитии, выходят новые версии протокола, брокеров и т.п. Но это уже вполне production-quality. Расскажу лишь базовые советы, чтобы помочь &laquo;въехать&raquo; в протокол.</p>

<p><span id="more-392"></span></p>

<p>Для начала, маленькая коллекция ссылок (в основном, на английском): что такое вообще обмен сообщениями и почему AMQP такой (<a href="http://www.zeromq.org/whitepapers:amqp-analysis">Messaging in general and AMQP design</a>); сравнение различных реализаций обмена сообщениями, в частности основанных на AMQP (<a href="http://wiki.secondlife.com/wiki/Message_Queue_Evaluation_Notes">Message Queue Comparison</a>); клиентская библиотека AMQP для Twisted Framework (Python) с поддержкой Thrift (<a href="http://us.pycon.org/2009/conference/schedule/event/28/">Thrift, AMQP in Twisted</a>); руководство от Red Hat о том, что такое messaging и как работать с AMQP, описывает их &laquo;коробочный&raquo; продукт на основе AMQP, но подходит и для любых AMQP-брокеров (<a href="http://www.redhatpress.org/docs/en-US/Red_Hat_Enterprise_MRG/1.0/pdf/Messaging_Tutorial/Messaging_Tutorial.pdf">AMQP Programming Tutorial for C++, Java, and Python</a>); достаточно много документации, описаний архитектурных решений на сайте <a href="http://www.zeromq.org/">ZeroMQ</a>, который не совсем AMQP-брокер, но общая архитектура, детали реализации представляют отдельный интерес; обзорная статья от Duncan McGregor о txAMQP и AMQP в общем (<a href="http://feedproxy.google.com/~r/ElectricDuncan/~3/NKiCEOnzXe0/sinfonia-on-messaging-with-txamqp.html">A Simfonia on Messaging with txAMQP</a>, <a href="http://feedproxy.google.com/~r/ElectricDuncan/~3/f3QMwXP6OwI/sinfonia-on-messaging-with-txamqp-part.html">II</a>, <a href="http://feedproxy.google.com/~r/ElectricDuncan/~3/Xi5GBHBooNg/sinfonia-on-messaging-with-txamqp-part_18.html">III</a>)</p>

<p>Далее необходимо выбрать AMQP-брокер, который вы будете использовать. При выборе необходимо рассматривать как собственно характеристики сервера: скорость работы, надежность, легкость установки и поддержки, но также внимательно смотреть на версию AMQP-протокола, которая поддерживается брокером, &#8211; она должна совпадать с версией клиентской AMQP-библиотеки. Из брокеров я бы посоветовал <a href="http://www.rabbitmq.com/">RabbitMQ</a>, написанный на Erlang, и <a href="http://qpid.apache.org/">Qpid</a>, версии на C++ (AMQP 0-10) и Java (0-8, 0-9).</p>

<p>Сам протокол AMQP устроен достаточно интересно: на самом нижнем уровне определяется формат кодирования данных в бинарный вид для передачи по TCP-соединению, выше лежит формат передачи RPC-запросов между сервером и клиентом. Сама семантика работы с сообщениями, создания очередей и т.п. описывается в XML-спецификации, которая по сути задает RPC-интерфейс сервера и клиента (примеры таких XML-файлов для версий <a href="http://jira.amqp.org/confluence/download/attachments/720900/amqp0-8.xml?version=1">0-8</a> и <a href="http://jira.amqp.org/confluence/download/attachments/720900/amqp.0-10.xml?version=1">0-10</a>). Этот XML является последней и конечной спецификацией протокола. Более того, версии протокола 0-8 и 0-10 отличаются настолько сильно, что поддерживать их одновременно вряд ли возможно в одной программе. Что еще более интересно, иногда такие spec-файлы для разных брокеров AMQP, формально поддерживающих одну и ту же версию протокола, отличаются настолько, что не являются взаимозаменяемыми. Но это скорее небольшие технические проблемы.</p>

<p>AMQP основан на трех понятиях:</p>

<ol>
<li>Сообщение (message) &#8211; единица передаваемых данных, основная его часть (содержание) никак не интерпретируется сервером, к сообщению могут быть прицеплены структурированные заголовки.</li>
<li>Точка обмена (exchange) &#8211; в нее отправляются сообщения. Точка обмена распределяет сообщения в одну или несколько очередей. При этом в точке обмена сообщения не хранятся. Точки обмена бывают трех типов: fanout &#8211; сообщение передается во все прицепленные к ней очереди; direct &#8211; сообщение передается в очередь с именем, совпадающим с ключом маршрутизации (routing key) (ключ маршрутизации указывается при отправке сообщения); topic &#8211; нечто среднее между fanout и exchange, сообщение передается в очереди, для которых совпадает маска на ключ маршрутизации, например, <code>app.notification.sms.*</code> &#8211; в очередь будут доставлены все сообщения, отправленные с ключами, начинающимися на <code>app.notification.sms</code>.</li>
<li>Очередь (queue) &#8211; здесь хранятся сообщения до тех пор, пока не будет забраны клиентом. Клиент всегда забирает сообщения из одной или нескольких очередей.</li>
</ol>

<p>Меня в AMQP привлек эффективный бинарный протокол, ориентированность протокола на минимальные задержки и гибкость настройки. Я его использовал для рассылки сообщений всем серверам кластера (fanout exchange) и организации аналогов &laquo;почтовых ящиков&raquo;, в которые доставляются сообщения, адресованные определенным клиентам (direct exchange). Для получения сообщений нет необходимости опрашивать сервер, достаточно подписаться на сообщения из очереди, и сервер передаст их в тот момент, когда они появятся.</p>

<p>В качестве клиентской библиотеки я выбрал библиотеку txAMQP для Twisted Framework (Python). В общем и целом все работает, но где-то требуются небольшие &laquo;доделки&raquo; и &laquo;подкрутки&raquo;, которые я планирую опубликовать на launchpad. В AMQP и вокруг AMQP есть много интересного и перспективного. К примеру, брокер RabbitMQ умеет масштабироваться и работать в едином кластере. Мне кажется, это очень полезная и перспективная технология.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.smira.ru/2009/06/08/amqp-in-russian/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>FMSPy, релиз Alpha (0.1)</title>
		<link>http://www.smira.ru/2009/06/07/flash-media-server-in-python-alpha-release/</link>
		<comments>http://www.smira.ru/2009/06/07/flash-media-server-in-python-alpha-release/#comments</comments>
		<pubDate>Sun, 07 Jun 2009 18:30:58 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Twisted]]></category>
		<category><![CDATA[Разработка]]></category>
		<category><![CDATA[flash]]></category>
		<category><![CDATA[fmspy]]></category>
		<category><![CDATA[rtmp]]></category>
		<category><![CDATA[разработка]]></category>

		<guid isPermaLink="false">http://www.smira.ru/?p=415</guid>
		<description><![CDATA[Flash Media Server written in Python (FMSPy) &#8211; это еще один RTMP-сервер для приложений на Adobe Flash/Flex/Air. FMSPy является аналогом Adobe Flash Media Server, с гораздо меньшими возможностями, однако FMSPy &#8211; совершенно бесплатный проект с открытым исходным кодом. Проект находится на ранней стадии развития, но в активной разработке. Итак, что есть на сегодняшний день: Реализация [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.smira.ru/wp-content/uploads/2009/05/fmspy.png"><img src="http://www.smira.ru/wp-content/uploads/2009/05/fmspy.png" alt="FMSPy" title="FMSPy" width="96" height="96" class="alignleft size-full wp-image-419" /></a> <a href="http://fmspy.org/" title="FMSPy">Flash Media Server written in Python</a> (<a href="http://fmspy.org/" title="FMSPy">FMSPy</a>) &#8211; это <em>еще один</em> RTMP-сервер для приложений на Adobe Flash/Flex/Air. FMSPy является аналогом Adobe Flash Media Server, с гораздо меньшими возможностями, однако FMSPy  &#8211; совершенно бесплатный проект с открытым исходным кодом. Проект находится на ранней стадии развития, но в активной разработке.</p>

<p>Итак, что есть на сегодняшний день:</p>

<ul>
<li>Реализация <a href="http://ru.wikipedia.org/RTMP" title="RTMP">RTMP</a>-протокола: кодирование/декодирование пакетов, разрезание и склеивание из chunks и т.п.</li>
<li>Поддержка базового RPC (Invoke) клиент-сервер и сервер-клиент. То есть из Flash-приложения можно вызывать с помощью класса <a href="http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/net/NetConnection.html" title="NetConnection">NetConnection</a> методы приложения на стороне сервера, и наоборот со стороны сервера вызывать методы приложения.</li>
<li>Инфраструктура для написания приложений (в качестве плагинов к FMSPy) со своим API на <a href="http://www.python.org/" title="Python">Python</a>.</li>
</ul>

<p><span id="more-415"></span></p>

<p>В ближайших релизах:</p>

<ul>
<li>Стриминг (вещание) с вебкамеры, стриминг видео/аудио с сервера (FLV, MP4, MP3).</li>
<li>Поддержка серверных <a href="http://www.adobe.com/support/flash/action_scripts/actionscript_dictionary/actionscript_dictionary648.html" title="Shared Object">Shared Object</a>.</li>
<li>Анализ загрузки, полуавтоматическая кластеризация для распределения нагрузки.</li>
</ul>

<p><a href="http://fmspy.org/" title="FMSPy">FMSPy</a> написан на <a href="http://www.python.org/" title="Python">Python</a> с использованием <a href="http://twistedmatrix.com/" title="Twisted">Twisted Framework</a>, приложения на FMSPy реализуются также на Python и им доступны все возможности, которые есть в Twisted: асинхронная сетевая модель, соединения с БД, <a href="http://www.smira.ru/tag/memcached" title="Memcached">memcached</a>, различные сервисы и т.п.</p>

<h2>Запуск и установка</h2>

<p>Если у Вас уже установлен Python и setuptools (чаще всего на Unix/Linux это так), достаточно выполнить от имени root:</p>

<pre><code>easy_install fmspy
</code></pre>

<p>Easy_install автоматически установит все необходимые зависимости (если они еще не установлены). Более подробно об установке можно почитать в <a href="http://fmspy.org/manual/en/userguide.html#installation" title="FMSPy установка">документации</a>.</p>

<p>После установки запуск в отладочном режиме (на консоли) осуществляется следующим образом:</p>

<pre><code>twistd -n fmspy
</code></pre>

<p>Для окончания работы сервера достаточно нажать Ctrl+C.</p>

<h2>Примеры</h2>

<p>Вместе с FMSPy устанавливается два примера: эхотест и простенький чат. После запуска откройте страницу <a href="http://localhost:3000/examples/">http://localhost:3000/examples/</a> и выберите интересующий вас. </p>

<p><a href="http://www.smira.ru/wp-content/uploads/2009/06/screenshot.png" target="_blank"><img src="http://www.smira.ru/wp-content/uploads/2009/06/screenshot-263x300.png" alt="echotest" title="echotest" width="263" height="300" class="alignleft size-medium wp-image-454" /></a> <a href="http://www.smira.ru/wp-content/uploads/2009/06/screenshot1.png"  target="_blank"><img src="http://www.smira.ru/wp-content/uploads/2009/06/screenshot1-297x300.png" alt="chat" title="chat" width="297" height="300" class="alignleft size-medium wp-image-456" /></a></p>

<h2>Вместо заключения</h2>

<p>Пробуйте, тестируйте, присоединяйтесь к разработке. Любая помощь приветствуется: написание документации, патчи, идеи новых фич, графические материалы! Все это лучше всего отправить в <a href="http://fmspy.org/" title="FMSPy">трекер</a>.  </p>

<p>Впереди новые релизы, также в ближайшее время статья о написании приложений для FMSPy.</p>

<p>Ссылки:</p>

<ul>
<li><a href="http://fmspy.org/" title="FMSPy">Сайт проекта, трекер</a></li>
<li><a href="http://fmspy.org/wiki/Documentation">Документация</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.smira.ru/2009/06/07/flash-media-server-in-python-alpha-release/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>Deferred для JavaScript (Prototype)</title>
		<link>http://www.smira.ru/2009/05/29/deferred-in-javascript-for-prototype/</link>
		<comments>http://www.smira.ru/2009/05/29/deferred-in-javascript-for-prototype/#comments</comments>
		<pubDate>Fri, 29 May 2009 15:35:06 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Twisted]]></category>
		<category><![CDATA[Разработка]]></category>
		<category><![CDATA[deferred]]></category>
		<category><![CDATA[javasc]]></category>
		<category><![CDATA[prototype]]></category>
		<category><![CDATA[разработка]]></category>

		<guid isPermaLink="false">http://www.smira.ru/?p=384</guid>
		<description><![CDATA[Продолжая тему Deferred для JavaScript предлагаю еще одно переписывание Deferred, теперь в терминах Prototype. Подробнее о самом Deferred можно почитать в двух моих прошлых заметках: Асинхронное программирование: концепция Deferred и Deferred: все подробности. Если кратко, самое распространенное и полезное применение Deferred в JavaScript &#8211; это работа с AJAX или другими RPC-over-HTTP вызовами, когда необходимо совершить [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.smira.ru/wp-content/uploads/2009/05/prototype_twisted.png" alt="Prototype and Twisted" title="Prototype and Twisted" width="200" height="200" class="alignleft size-full wp-image-410" /></p>

<p><a href="http://www.smira.ru/2008/11/01/open-source-deferred-qooxdoo/">Продолжая</a> тему <a href="http://twistedmatrix.com/projects/core/documentation/howto/async.html">Deferred</a> для JavaScript предлагаю еще одно переписывание Deferred, теперь в терминах <a href="http://www.prototypejs.org/">Prototype</a>. Подробнее о самом Deferred можно почитать в двух моих прошлых заметках: <a href="http://www.smira.ru/2009/02/10/deferred-async-programming/">Асинхронное программирование: концепция Deferred</a> и <a href="http://www.smira.ru/2009/02/24/more-about-deferred/">Deferred: все подробности</a>. Если кратко, самое распространенное и полезное применение Deferred в JavaScript &#8211; это работа с AJAX или другими RPC-over-HTTP вызовами, когда необходимо совершить цепочку логически связанных вызовов, корректно обрабатывать возникающие ошибки и т.п. С моей точки зрения, Deferred крайне необходим в таких ситуациях.</p>

<p>Перейдем к примерам: обращение к некоторому JSON-RPC API на основе Prototype&#8217;овского <a href="http://www.prototypejs.org/api/ajax/request">Ajax.Request</a> можеть быть обернуто в Deferred следующим образом:</p>

<p><span id="more-384"></span></p>


<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;"><span style="color: #003366; font-weight: bold;">var</span> Api <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">Class</span>.<span style="color: #660066;">create</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span>
    initialize <span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>url<span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">url</span> <span style="color: #339933;">=</span> url<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
&nbsp;
    call <span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>method<span style="color: #339933;">,</span> params<span style="color: #009900;">&#41;</span>
    <span style="color: #009900;">&#123;</span>
        <span style="color: #003366; font-weight: bold;">var</span> requestBody <span style="color: #339933;">=</span> $H<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span> <span style="color: #3366CC;">'method'</span> <span style="color: #339933;">:</span> method<span style="color: #339933;">,</span> <span style="color: #3366CC;">'params'</span> <span style="color: #339933;">:</span> params <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">toJSON</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #003366; font-weight: bold;">var</span> d <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> Deferred<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #003366; font-weight: bold;">var</span> onSuccess <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>transport<span style="color: #009900;">&#41;</span>
        <span style="color: #009900;">&#123;</span>
            result <span style="color: #339933;">=</span> transport.<span style="color: #660066;">responseText</span>.<span style="color: #660066;">evalJSON</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'faultCode'</span> <span style="color: #000066; font-weight: bold;">in</span> result <span style="color: #339933;">&amp;&amp;</span> <span style="color: #3366CC;">'faultString'</span> <span style="color: #000066; font-weight: bold;">in</span> result<span style="color: #009900;">&#41;</span>
            <span style="color: #009900;">&#123;</span>
                <span style="color: #003366; font-weight: bold;">var</span> err <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> Error<span style="color: #009900;">&#40;</span>result.<span style="color: #660066;">faultString</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
                err.<span style="color: #660066;">faultCode</span> <span style="color: #339933;">=</span> result.<span style="color: #660066;">faultCode</span><span style="color: #339933;">;</span>
                err.<span style="color: #660066;">faultString</span> <span style="color: #339933;">=</span> result.<span style="color: #660066;">faultString</span><span style="color: #339933;">;</span>
                d.<span style="color: #660066;">errback</span><span style="color: #009900;">&#40;</span>err<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
            <span style="color: #000066; font-weight: bold;">else</span>
            <span style="color: #009900;">&#123;</span>
                result <span style="color: #339933;">=</span> result<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
                console.<span style="color: #660066;">log</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;Got result: &quot;</span><span style="color: #339933;">,</span> result<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
                d.<span style="color: #660066;">callback</span><span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #003366; font-weight: bold;">var</span> onFailure <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>transport<span style="color: #009900;">&#41;</span>
        <span style="color: #009900;">&#123;</span>
            d.<span style="color: #660066;">errback</span><span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">new</span> Error<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;API transport error: &quot;</span> <span style="color: #339933;">+</span> transport.<span style="color: #000066;">status</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>
        <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #003366; font-weight: bold;">var</span> onException <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>error<span style="color: #009900;">&#41;</span>
        <span style="color: #009900;">&#123;</span>
            d.<span style="color: #660066;">errback</span><span style="color: #009900;">&#40;</span>error<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        <span style="color: #003366; font-weight: bold;">new</span> Ajax.<span style="color: #660066;">Request</span><span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">url</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#123;</span>
                method <span style="color: #339933;">:</span> <span style="color: #3366CC;">'post'</span><span style="color: #339933;">,</span>
                postBody <span style="color: #339933;">:</span> requestBody<span style="color: #339933;">,</span>
                requestHeaders <span style="color: #339933;">:</span> <span style="color: #009900;">&#123;</span> <span style="color: #3366CC;">'Content-Type'</span> <span style="color: #339933;">:</span> <span style="color: #3366CC;">'application/json'</span> <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
                onSuccess <span style="color: #339933;">:</span> onSuccess<span style="color: #339933;">,</span>
                onFailure <span style="color: #339933;">:</span> onFailure<span style="color: #339933;">,</span>
                onException <span style="color: #339933;">:</span> onException<span style="color: #339933;">,</span>
            <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
        <span style="color: #000066; font-weight: bold;">return</span> d<span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>


<p>Здесь любое обращение к <code>Api.call</code> будет возвращать новый Deferred, который будет содержать результат удаленного вызова либо исключение (транспортное или от сервера, к которому мы обращаемся). Пусть есть RPC-вызовы <code>sum</code> и <code>mult</code>, которые, соответственно, складывают и перемножают свои аргументы. Тогда вычисление выражения <code>(2+3)*7</code> с использованием нашего класса <code>Api</code> будет выглядеть так:</p>


<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">  <span style="color: #003366; font-weight: bold;">var</span> api <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> Api<span style="color: #339933;">;</span>
  api.<span style="color: #660066;">call</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'sum'</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#91;</span><span style="color: #CC0000;">2</span><span style="color: #339933;">,</span> <span style="color: #CC0000;">3</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>
     .<span style="color: #660066;">addCallback</span><span style="color: #009900;">&#40;</span>
               <span style="color: #003366; font-weight: bold;">function</span> <span style="color: #009900;">&#40;</span>sum_of_2_and_3<span style="color: #009900;">&#41;</span> 
               <span style="color: #009900;">&#123;</span> 
                     <span style="color: #000066; font-weight: bold;">return</span> api.<span style="color: #660066;">call</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'mult'</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#91;</span>sum_of_2_and_3<span style="color: #339933;">,</span> <span style="color: #CC0000;">7</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> 
               <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>
     .<span style="color: #660066;">addCallback</span><span style="color: #009900;">&#40;</span>
               <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span>result<span style="color: #009900;">&#41;</span>
               <span style="color: #009900;">&#123;</span> 
                     <span style="color: #000066;">alert</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'(2+3)*7 = '</span> <span style="color: #339933;">+</span> result<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> 
               <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>
     .<span style="color: #660066;">addErrback</span><span style="color: #009900;">&#40;</span>
              <span style="color: #003366; font-weight: bold;">function</span> <span style="color: #009900;">&#40;</span>error<span style="color: #009900;">&#41;</span> 
              <span style="color: #009900;">&#123;</span> 
                     <span style="color: #000066;">alert</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'Mmm.. something wrong happened: '</span> <span style="color: #339933;">+</span> error<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> 
              <span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>


<p>Ну и самое главное: </p>

<ul>
<li>исходный код <a href='http://www.smira.ru/wp-content/uploads/2009/05/deferred.js'>Deferred.js</a>;</li>
<li>лицензия MIT, как и у самого <a href="http://mochikit.com/doc/html/MochiKit/Async.html">первого варианта Deferred для JavaScript</a>.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.smira.ru/2009/05/29/deferred-in-javascript-for-prototype/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>СпамоБорец: релиз 0.2.0</title>
		<link>http://www.smira.ru/2009/02/27/spamfighter-dot-two-release/</link>
		<comments>http://www.smira.ru/2009/02/27/spamfighter-dot-two-release/#comments</comments>
		<pubDate>Fri, 27 Feb 2009 10:13:11 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Twisted]]></category>
		<category><![CDATA[Разработка]]></category>
		<category><![CDATA[СпамоБорец]]></category>
		<category><![CDATA[open-source]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[разработка]]></category>
		<category><![CDATA[спамоборец]]></category>

		<guid isPermaLink="false">http://www.smira.ru/?p=327</guid>
		<description><![CDATA[СпамоБорец &#8211; веб-сервис, предоставляющий функции по классификации произвольных текстовых сообщений, и, в частности, выделения спама из общего потока сообщений. В качестве сообщений могут рассматриваться, например, следующие виды общения, которые сегодня есть в социальных сетях (и веб-сайтах, имеющих элементы социальной сети): личные сообщения; чаты; комментарии к произвольным объектам; девизы, сообщения &#171;о себе&#187; и т.п. на страницах [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://spam-fighter.ru/" title="СпамоБорец">СпамоБорец</a> &#8211; веб-сервис, предоставляющий функции по классификации произвольных текстовых сообщений, и, в частности, выделения спама из общего потока сообщений.</p>

<p><img src="http://www.smira.ru/wp-content/uploads/2009/02/security-guard.png" alt="СпамоБорец" title="СпамоБорец" width="256" height="256" class="alignnone size-full wp-image-328" /></p>

<p>В качестве сообщений могут рассматриваться, например, следующие виды общения, которые сегодня есть в социальных сетях (и веб-сайтах, имеющих элементы социальной сети):</p>

<ul>
<li>личные сообщения;</li>
<li>чаты;</li>
<li>комментарии к произвольным объектам;</li>
<li>девизы, сообщения &laquo;о себе&raquo; и т.п. на страницах профиля пользователя;</li>
<li>письма в службу поддержки. </li>
</ul>

<p>Фильтрация и классификация сообщений основывается на нескольких независимых алгоритмах; результатом классификации может являться классификация как самого сообщения (причём, возможно, по нескольким категориям: спам, флуд, проституция и т.п.), так и классификация отправителей сообщений (как авторизованных, так и неавторизованных, по тем же самым категориям: спамер, флудер и т.п.). Применение классификации к отправителям сообщений позволяет на раннем этапе пресекать попытки спам-рассылок и тому подобных массовых действий на сайте. </p>

<p><span id="more-327"></span></p>

<p>Данный релиз является первым публичным релизом проекта <a href="http://spam-fighter.ru/" title="СпамоБорец">СпамоБорец</a>. Качество и полнота функционала соответствует на сегодняшний день альфа-версии. </p>

<p>На сегодня доступен следующий функционал:</p>

<ul>
<li>веб-сервис с JSON-RPC и XML-RPC API (<a href="http://spam-fighter.ru/docs/api/index.html">описание API</a>);</li>
<li>создание и запуск из <a href="http://spam-fighter.ru/docs/guide/running.html">отдельного окружения</a>;</li>
<li>firewall из правил анализа сообщений;</li>
<li>гибкая настройка содержимого сообщений;</li>
<li>анализ текста сообщений на флуд;</li>
<li>анализ частоты сообщений по тексту и атрибутам автора: логин, IP и т.п.;</li>
<li>простая Байесовская модель анализа текста (тестовая);</li>
<li>валидация сообщений по длине, наличию атрибутов, регулярному выражению;</li>
<li>логирование сообщений, проходящих через сервер;</li>
<li>система администрирования и настройки (qooxdoo, см. <a href="http://spam-fighter.ru/wiki/rus/Screenshots">скриншоты</a>);</li>
<li>реализация клиентов API на PHP и тестовый модуль для WordPress.</li>
</ul>

<h2>Как установить</h2>

<p>Нужен python и setuptools:</p>

<pre><code>easy_install spamfighter
</code></pre>

<p>После этого выбираем каталог для окружения (например, <code>~/spamfighter</code>) и запускаем:</p>

<pre><code>spamfighter-create ~/spamfighter
</code></pre>

<p>Далее следуем инструкциям на экране. Более подробную информация об установке можно найти в <a href="http://spam-fighter.ru/docs/guide/setup.html">документации по СпамоБорцу</a>. </p>

<h2>Что дальше?</h2>

<p>Этот пост &#8211; уведомление о выходе новой версии, за ним последует более подробный рассказ &laquo;для чего это нужно&raquo; и &laquo;как это использовать&raquo;. Надеемся на позитивный feedback и участие в жизни <a href="http://spam-fighter.ru/" title="СпамоБорец">проекта</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.smira.ru/2009/02/27/spamfighter-dot-two-release/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Deferred: все подробности</title>
		<link>http://www.smira.ru/2009/02/24/more-about-deferred/</link>
		<comments>http://www.smira.ru/2009/02/24/more-about-deferred/#comments</comments>
		<pubDate>Tue, 24 Feb 2009 06:14:29 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Twisted]]></category>
		<category><![CDATA[Разработка]]></category>
		<category><![CDATA[deferred]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[twisted]]></category>
		<category><![CDATA[разработка]]></category>

		<guid isPermaLink="false">http://www.smira.ru/?p=315</guid>
		<description><![CDATA[В предыдущей статье были описаны основные принципы работы Deferred и его применение в асинхронном программировании. Сегодня мы постараемся рассмотреть в деталях функционирование Deferred и примеры его использования. Итак, Deferred &#8211; это отложенный результат, результат выполнения, который станет известен через некоторое время. Результатом, хранящимся в Deferred, может быть произвольное значение (успешное выполнение) или ошибка (исключение), которое [...]]]></description>
			<content:encoded><![CDATA[<p>В <a href="http://www.smira.ru/2009/02/10/deferred-async-programming/" title="Асинхронное программирование: концепция Deferred">предыдущей статье</a> были описаны основные принципы работы Deferred и его применение в асинхронном программировании.
Сегодня мы постараемся рассмотреть в деталях функционирование Deferred и примеры его использования.</p>

<p>Итак, Deferred &#8211; это отложенный результат, результат выполнения, который станет известен через некоторое время. 
Результатом, хранящимся в Deferred, может быть произвольное значение (успешное выполнение) или ошибка (исключение),
которое произошло в процессе выполнения асинхронной операции. Раз нас интересует результат операции и мы получили
от некоторой асинхронной функции Deferred, мы хотим выполнить действия в тот момент, когда результат выполнения будет
известен. Поэтому Deferred кроме результата хранит еще цепочку обработчиков: обработчиков результатов (callback) и
обработчиков ошибок (errback).</p>

<p>Рассмотрим поподробнее цепочку обработчиков:</p>

<p><img src="http://www.smira.ru/wp-content/uploads/2009/02/deferred1.png" alt="Deferred" title="Deferred" width="263" height="332" class="size-full wp-image-319" /></p>

<p><span id="more-315"></span></p>

<p>Обработчики располагаются по &laquo;слоям&raquo; или уровням, выполнение происходит четко по уровням сверху вниз. При этом на каждом
уровне расположены обработчики callback и errback, один из элементов может отсутствовать. На каждом уровне может
быть выполнен либо callback, либо errback, но не оба. Выполнение обработчиков происходит только один раз, повторного
входа быть не может. </p>

<p>Функции-обработчики callback являются функциями с одним аргументом &#8211; результатом выполнения:</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> callback<span style="color: black;">&#40;</span>result<span style="color: black;">&#41;</span>:
    ...</pre></div></div>


<p>Функции-обработчики errback принимают в качестве параметра исключение, &laquo;завернутое&raquo; в класс <a href="http://twistedmatrix.com/documents/current/api/twisted.python.failure.Failure.html" title="API reference Failure">Failure</a>:</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> errback<span style="color: black;">&#40;</span>failure<span style="color: black;">&#41;</span>:
    ...</pre></div></div>


<p>Выполнение Deferred начинается с того, что в Deferred появляется результат: успешное выполнение или исключение. 
В зависимости от результата выбирается соответствующая ветвь обработчиков: callback или errback. После этого происходит поиск
ближайшего уровня обработчиков, в котором существует соответствующий обработчик. В нашем примере на рисунке
был получен успешный результат выполнения и результат был передан обработчику <code>callback1</code>.</p>

<p>Дальнейшее выполнение приводит к вызову обработчиков на нижележащих уровнях. Если callback или errback завершается
возвратом значения, которое не является Failure, выполнение считается успешным и полученный результат отправляется
на вход callback-обработчику на следующем уровне. Если же в процессе выполнения обработчика было выкинуто исключение
или возвращено значение типа Failure, управление будет передано errback на следующем уровне, который получит
исключение в качестве параметра.</p>

<p>В нашем примере обработчик <code>callback1</code> выполнился успешно, его результат был передан обработчику <code>callback2</code>, в котором
было выкинуто исключение, которое привело к переходу к цепочке errback-обработчиков, на третьем уровне обработчик
errback отсутствовал и исключение было передано в <code>errback4</code>, который обработал исключение, вернул успешный результат
выполнения, который теперь является результатом Deferred, однако больше обработчиков нет. Если к Deferred будет
добавлен еще один уровень обработчиков, они смогут получить доступ к этому результату.</p>

<p>Как и все другие объекты Python, объект Deferred живет до тех пор, пока на него есть ссылки из других объектов. Обычно
объект, вернувший Deferred, сохраняет его, т.к. ему надо по завершении асинхронной операции передать в Deferred полученный
результат. Чаще всего другие участники (добавляющие обработчики событий) не сохраняют ссылки на Deferred, таким образом
объект Deferred будет уничтожен по окончании цепочки обработчиков. Если происходит уничтожение Deferred, в котором
осталось необработанное исключение (выполнение завершилось исключением и больше обработчиков нет), на экран печатается
отладочное сообщение с traceback исключения. Эта ситуация аналогична &laquo;выскакиванию&raquo; необработанного исключения на верхний
уровень в обычной синхронной программе.</p>

<h2>Deferred в квадрате</h2>

<p>Возвращаемым значением callback и errback может быть также другой Deferred, тогда выполнение цепочки обработчиков
текущего Deferred приостанавливается до окончания цепочки обработчиков вложенного Deferred.</p>

<p><img src="http://www.smira.ru/wp-content/uploads/2009/02/deferred2.png" alt="deferred-in-deferred" title="deferred-in-deferred" width="563" height="384" class="alignnone size-full wp-image-320" /></p>

<p>В приведенном на рисунке примере обработчик callback2 возвращает не обычный результат, а другой Deferred &#8211; Deferred2. При
этом выполнение текущего Deferred приостанавливается до получения результата выполнения Deferred2. Результат Deferred2 &#8211; 
успешный или исключение &#8211; становится результатом, передаваемым на следующий уровень обработчиков первого Deferred. В нашем примере
Deferred2 завершился с исключением, которое будет передано на вход обработчику <code>errback2</code> первого Deferred.</p>

<h2>Обработка исключений в errback</h2>

<p>Каждый обработчик исключений errback является аналогом блока try..except, а блок except обычно реагирует на некоторый тип
исключений, такое поведение очень просто воспроизвести с помощью <a href="http://twistedmatrix.com/documents/current/api/twisted.python.failure.Failure.html" title="API reference Failure">Failure</a>:</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> errback<span style="color: black;">&#40;</span>failure<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;
    @param failure: ошибка (исключение), завернутое в failure
    @type failure: C{Failure}
    &quot;&quot;&quot;</span>
    failure.<span style="color: black;">trap</span><span style="color: black;">&#40;</span><span style="color: #008000;">KeyError</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Got key error: %r&quot;</span> <span style="color: #66cc66;">%</span> failure.<span style="color: black;">value</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff4500;">0</span></pre></div></div>


<p>Метод <code>trap</code> класса <code>Failure</code> проверяет, является ли завернутое в него исключение наследником или непосредственно классом
<code>KeyError</code>. Если это не так, оригинальное исключение снова выбрасывается, прерывая выполнение текущего errback, что приведет
к передаче управления следующему errback в цепочке обработчиков, что имитирует поведение блока <code>except</code> при несоответствии
типа исключения (управление передается следующему блоку). В свойстве <code>value</code> хранится оригинальное исключение, которое
можно использовать для получения дополнительной информации об ошибке.</p>

<p>Необходимо обратить внимание, что обработчик errback должен завершиться одним из двух способов:</p>

<ol>
<li>Вернуть некоторое значение, которое станет входным значением следующего callback, что по смыслу означает, что
исключение было обработано.</li>
<li>Выкинуть оригинальное или новое исключение &#8211; исключение не было обработано или было перевыброшено новое исключение,
цепочка обработчиков errback продолжается.</li>
</ol>

<p>Существует и третий вариант &#8211; вернуть Deferred, тогда дальнейшее выполнение обработчиков будет зависеть от результата
Deferred.</p>

<p>В нашем примере мы исключение обработали и передали в качестве результата 0 (например, отсутствие некоторого ключа эквивалентно
его нулевому значению).</p>

<h2>Готовимся к асинхронности заранее</h2>

<p>Как только появляется асинхронность, то есть некоторая функция вместо непосредственного значения возвращает Deferred, асинхронность
начинает распространяться по дереву функций выше, заставляя возвращать Deferred из функций, которые раньше были синхронными (возвращали
результат непосредственно). Рассмотрим условный пример такого превращения:</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> f<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff4500;">33</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> g<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> f<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><span style="color: #66cc66;">*</span><span style="color: #ff4500;">2</span></pre></div></div>


<p>Если по каким-то причинам функция <code>f</code> не сможет вернуть результат сразу, она начнет возвращать Deferred:</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> f<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> Deferred<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">callback</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">33</span><span style="color: black;">&#41;</span></pre></div></div>


<p>Но теперь и функция <code>g</code> вынуждена возращать Deferred, зацепляясь за цепочку обработчиков:</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> g<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> f<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>.<span style="color: black;">addCallback</span><span style="color: black;">&#40;</span><span style="color: #ff7700;font-weight:bold;">lambda</span> result: result<span style="color: #66cc66;">*</span><span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span></pre></div></div>


<p>Аналогичная схема &laquo;превращения&raquo; происходит и с реальными функциями: мы получаем результаты в виде Deferred от нижележащих в дереве
вызовов функции, навешиваем на их Deferred свои обработчики callback, которые соответствуют старому, синхронному коду нашей функции, если у 
нас были обработчики исключений, добавляются и обработчики errback.</p>

<p>На практике лучше сначала выявить те места кода, которые будут асинхронными и будут использовать Deferred, чем переделывать синхронный
код в асинхронный. Асинхронный код начинается с тех вызовов, которые не могут построить результат непосредственно:</p>

<ul>
<li>сетевой ввод-вывод;</li>
<li>обращение к сетевым сервисам СУБД, memcached;</li>
<li>удаленные вызовы RPC;</li>
<li>операции, выполнение которых будет выделено в нить в модели Worker и т.п.</li>
</ul>

<p>В процессе написания приложения часто ясно, что в данной точке будет асинхронное обращение, но его еще нет (не реализован интерфейс с СУБД, 
например). В такой ситуации можно использовать функции <code>defer.success</code> или <code>defer.fail</code> для создания Deferred, в котором уже содержится
результат. Вот как можно короче переписать функцию <code>f</code>:</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> twisted.<span style="color: black;">internet</span> <span style="color: #ff7700;font-weight:bold;">import</span> defer
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> f<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> defer.<span style="color: black;">success</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">33</span><span style="color: black;">&#41;</span></pre></div></div>


<p>Если мы не знаем, будет ли вызываемая функция возвращать результат синхронно или вернет Deferred, и не хотим зависеть от её
поведения, мы можем завернуть обращение к ней в <code>defer.maybeDeferred</code>, которое любой вариант сделает эквивалентным вызову
Deferred:</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> twisted.<span style="color: black;">internet</span> <span style="color: #ff7700;font-weight:bold;">import</span> defer
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> g<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> defer.<span style="color: black;">maybeDeferred</span><span style="color: black;">&#40;</span>f<span style="color: black;">&#41;</span>.<span style="color: black;">addCallback</span><span style="color: black;">&#40;</span><span style="color: #ff7700;font-weight:bold;">lambda</span> result: result<span style="color: #66cc66;">*</span><span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span></pre></div></div>


<p>Такой вариант функции <code>g</code> будет работать как с синхронной, так и с асинхронной <code>f</code>.</p>

<h2>Вместо заключения</h2>

<p>Рассказывать о Deferred можно еще очень долго, в качестве дополнительного чтения могу опять порекомендовать
список материалов в конце <a href="http://www.smira.ru/2009/02/10/deferred-async-programming/" title="Асинхронное программирование: концепция Deferred">предыдущей статьи</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.smira.ru/2009/02/24/more-about-deferred/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Асинхронное программирование: концепция Deferred</title>
		<link>http://www.smira.ru/2009/02/10/deferred-async-programming/</link>
		<comments>http://www.smira.ru/2009/02/10/deferred-async-programming/#comments</comments>
		<pubDate>Tue, 10 Feb 2009 06:49:28 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Twisted]]></category>
		<category><![CDATA[Разработка]]></category>
		<category><![CDATA[callback]]></category>
		<category><![CDATA[deferred]]></category>
		<category><![CDATA[errback]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[twisted]]></category>
		<category><![CDATA[асинхронное]]></category>
		<category><![CDATA[програмирование]]></category>
		<category><![CDATA[разработка]]></category>

		<guid isPermaLink="false">http://www.smira.ru/?p=296</guid>
		<description><![CDATA[Асинхронная концепция программирования заключается в том, что результат выполнения функции доступен не сразу же, а через некоторое время в виде некоторого асинхронного (нарушающего обычный порядок выполнения) вызова. Зачем такое может быть полезно? Рассмотрим несколько примеров. Первый пример &#8211; сетевой сервер, веб-приложение. Чаще всего как таковых вычислений на процессоре такие приложения не выполняют. Большая часть времени [...]]]></description>
			<content:encoded><![CDATA[<p>Асинхронная концепция программирования заключается в том, что результат
выполнения функции доступен не сразу же, а через некоторое время в виде
некоторого асинхронного (нарушающего обычный порядок выполнения) вызова. Зачем
такое может быть полезно?  Рассмотрим несколько примеров.</p>

<p>Первый пример &#8211; сетевой сервер, веб-приложение. Чаще всего как таковых
вычислений на процессоре такие приложения не выполняют. Большая часть времени
(реального, не процессорного) тратится на ввод-вывод: чтение запроса от
клиента, обращение к диску за данными, сетевые обращение к другим подсистемам
(БД, кэширующие сервера, RPC и т.п.), запись ответа клиенту. Во время этих
операций ввода-вывода процессор простаивает, его можно загрузить обработкой
запросов других клиентов.  Возможны различные способы решить эту задачу: отдельный
процесс на каждое соединение (<a href="http://httpd.apache.org/">Apache</a> mpm&#95;prefork, <a href="http://www.postgresql.org">PostgreSQL</a>, <a href="http://www.php.net">PHP</a> FastCGI),
отдельный поток (нить) на каждое соединение или комбинированный вариант
процесс/нить (<a href="http://httpd.apache.org/">Apache</a> mpm&#95;worker, <a href="http://mysql.org">MySQL</a>). Подход с использованием процессов или
нитей перекладывает мультиплексирование процессора между обрабатываемыми
соединениями на ОС, при этом расходуется относительно много ресурсов (память,
переключения контекста и т.п.), такой вариант не подходит для обработки
большого количества одновременных соединений, но идеален для ситуации, когда
объем вычислений достаточно высок (например, в СУБД). К плюсам модели нитей и
процессов можно добавить потенциальное использование всех доступных процессоров
в многопроцессорной архитектуре.</p>

<p>Альтернативой является использование однопоточной модели с использованием
примитивов асинхронного ввода-вывода, предоставляемых ОС (select, poll, и
т.п.). При этом объем ресурсов на каждое новое обслуживаемое соединение не
такой большой (новый сокет, какие-то структуры в памяти приложения). Однако
программирование существенно усложняется, т.к. данные из сетевых сокетов
поступают некоторыми &laquo;отрывками&raquo;, причем за один цикл обработки данные поступают от разных
соединений, находящихся в разных состояниях, часть соединений могут быть
входящими от клиентов, часть &#8211; исходящими к внешним ресурсам (БД, другой сервер
и т.п.). Для упрощения разработки используются различные концепции: callback,
конечные автоматы и другие. Примеры сетевых серверов, использующих асинхронный
ввод-вывод: <a href="http://sysoev.ru/nginx/">nginx</a>, <a href="http://lighttpd.net/">lighttpd</a>, 
<a href="http://haproxy.1wt.eu/">HAProxy</a>, <a href="https://developer.skype.com/SkypeGarage/DbProjects/PgBouncer">pgBouncer</a>, и т.д. Именно при такой
однопоточной модели возникает необходимость в асинхронном программировании.
Например, мы хотим выполнить запрос в БД. С точки зрения программы выполнение
запроса &#8211; это сетевой ввод-вывод: соединение с сервером, отправка запроса,
ожидание ответа, чтение ответа сервера БД. Поэтому если мы вызываем
функцию &laquo;выполнить запрос БД&raquo;, то она сразу вернуть результат не сможет
(иначе она должна была бы заблокироваться), а вернет лишь нечто, что позволит
впоследствие получить результат запроса или, возможно, ошибку (нет соединения
с сервером, некорректный запрос и т.п.) Этим возвращаемым значением удобно
сделать именно Deferred.</p>

<p>Второй пример связан с разработкой обычных десктопных приложений. Предположим,
мы решили сделать аналог <a href="http://www.miranda-im.org/">Miranda</a> (<a href="http://qip.ru/">QIP</a>, <a href="http://mdc.ru/">MDC</a>, &#8230;), то есть свой мессенджер.
В интерфейсе программы есть контакт-лист, где можно удалить контакт. Когда
пользователь выбирает это действие, он ожидает что контакт исчезнет на экране 
и что он действительно удалится из контакт-листа. На самом деле операция
удаления из серверного контакт-листа опирается на сетевое взаимодействие
с сервером, при этом пользовательский интерфейс не должен быть заблокирован
на время выполнения этой операции, поэтому в любом случае после выполнения
операции потребуется некоторое асинхронное взаимодействие с результатом операции.
Можно использовать механизм сигналов-слотов, callback&#8217;ов или что-то еще,
но лучше всего подойдет Deferred: операция удаления из контакт-листа возвращает
Deferred, в котором обратно придет либо положительный результат (всё хорошо),
либо исключение (точная ошибка, которую надо сообщить пользователю): в случае
ошибки контакт надо восстановить контакт в контакт-листе.</p>

<p>Примеры можно приводить долго и много, теперь о том, что же такое Deferred.
Deferred &#8211; это сердце framework&#8217;а асинхронного сетевого программирования 
<a href="http://twistedmatrix.com/">Twisted</a> в Python. Это простая и стройная концепция, которая
позволяет перевести синхронное программирование в асинхронный код, не изобретая
велосипед для каждой ситуации и обеспечивая высокое качества кода.
Deferred &#8211; это просто возвращаемый результат функции, когда этот результат
неизвестен (не был получен, будет получен в другой нити и т.п.) Что мы можем
сделать с Deferred? Мы можем &laquo;подвеситься&raquo; в цепочку обработчиков, которые
будут вызваны, когда результат будет получен. При этом Deferred может нести не 
только положительный результат выполнения, но и исключения, сгенерированные
функцией или обработчиками, есть возможность исключения обработать, перевыкинуть
и т.д. Фактически, для синхронного кода есть более-менее однозначная параллель в
терминах Deferred. Для эффективной разработки с Deferred оказываются полезными
такие возможности языка программирования, как замыкания, лямбда-функци.</p>

<p><span id="more-296"></span></p>

<p>Приведем пример синхронного кода и его альтернативу в терминах Deferred:</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">try</span>:
    <span style="color: #808080; font-style: italic;"># Скачать по HTTP некоторую страницу</span>
    page = downloadPage<span style="color: black;">&#40;</span>url<span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># Распечатать содержимое</span>
    <span style="color: #ff7700;font-weight:bold;">print</span> page
<span style="color: #ff7700;font-weight:bold;">except</span> HTTPError, e:
    <span style="color: #808080; font-style: italic;"># Произошла ошибка</span>
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;An error occured: %s&quot;</span>, e</pre></div></div>


<p>В асинхронном варианте с Deferred он был бы записан следующим образом:</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> printContents<span style="color: black;">&#40;</span>contents<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;
    Callback, при успешном получении текста страницы,
    распечатываем её содержимое.
    &quot;&quot;&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">print</span> contents
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> handleError<span style="color: black;">&#40;</span>failure<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;
    Errback (обработчик ошибок), просто распечатываем текст ошибки.
    &quot;&quot;&quot;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># Мы готовы обработать только HTTPError, остальные исключения</span>
    <span style="color: #808080; font-style: italic;"># &quot;проваливаются&quot; ниже.</span>
    failure.<span style="color: black;">trap</span><span style="color: black;">&#40;</span>HTTPError<span style="color: black;">&#41;</span>
    <span style="color: #808080; font-style: italic;"># Распечатываем само исключение</span>
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;An error occured: %s&quot;</span>, failure
&nbsp;
<span style="color: #808080; font-style: italic;"># Теперь функция выполняется асинхронно и вместо непосредственного</span>
<span style="color: #808080; font-style: italic;"># результата мы получаем Deferred</span>
deferred = downloadPage<span style="color: black;">&#40;</span>url<span style="color: black;">&#41;</span>
<span style="color: #808080; font-style: italic;"># Навешиваем на Deferred-объект обработчики успешных результатов</span>
<span style="color: #808080; font-style: italic;"># и ошибок (callback, errback).</span>
deferred.<span style="color: black;">addCallback</span><span style="color: black;">&#40;</span>printContents<span style="color: black;">&#41;</span>
deferred.<span style="color: black;">addErrback</span><span style="color: black;">&#40;</span>handleError<span style="color: black;">&#41;</span></pre></div></div>


<p>На практике обычно мы возвращаем Deferred из функций, которые получают
Deferred в процессе своей работы, навешиваем большое количество обработчиков,
обрабатываем исключения, некоторые исключения возвращаем через Deferred (выбрасываем
наверх). В качестве более сложного примера приведем код в асинхронном
варианте для примера атомарного счетчика из статьи про <a href="http://www.smira.ru/2009/01/21/data-structures-in-memcached-memcachedb/#atomic-counter">структуры данных в memcached</a>,
здесь мы предполагаем, что доступ к memcached как сетевому сервису идет через Deferred, т.е.
методы класса Memcache возвращают Deferred (который вернет либо результат операции, либо ошибку):</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> MCCounter<span style="color: black;">&#40;</span>MemcacheObject<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, mc, name<span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;
        Конструктор.
&nbsp;
        @param name: имя счетчика
        @type name: C{str}
        &quot;&quot;&quot;</span>
        <span style="color: #008000;">super</span><span style="color: black;">&#40;</span>MCCounter, <span style="color: #008000;">self</span><span style="color: black;">&#41;</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">&#40;</span>mc<span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">key</span> = <span style="color: #483d8b;">'counter'</span> + name
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> increment<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, value=<span style="color: #ff4500;">1</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;
        Увеличить значение счетчика на указанное значение.
&nbsp;
        @param value: инкремент
        @type value: C{int}
        @return: Deferred, результат операции
        &quot;&quot;&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">def</span> tryAdd<span style="color: black;">&#40;</span>failure<span style="color: black;">&#41;</span>:
            <span style="color: #808080; font-style: italic;"># Обрабатываем только KeyError, всё остальное &quot;вывалится&quot;</span>
            <span style="color: #808080; font-style: italic;"># ниже</span>
            failure.<span style="color: black;">trap</span><span style="color: black;">&#40;</span><span style="color: #008000;">KeyError</span><span style="color: black;">&#41;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># Пытаемся создать ключ, если раз его еще нет</span>
            d = <span style="color: #008000;">self</span>.<span style="color: black;">mc</span>.<span style="color: black;">add</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">key</span>, value, <span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>
            <span style="color: #808080; font-style: italic;"># Если вдруг кто-то еще создаст ключ раньше нас,</span>
            <span style="color: #808080; font-style: italic;"># мы это обработаем</span>
            d.<span style="color: black;">addErrback</span><span style="color: black;">&#40;</span>tryIncr<span style="color: black;">&#41;</span>
            <span style="color: #808080; font-style: italic;"># Возвращаем Deferred, он &quot;вклеивается&quot; в цепочку</span>
            <span style="color: #808080; font-style: italic;"># Deferred, в контексте которого мы выполняемся</span>
            <span style="color: #ff7700;font-weight:bold;">return</span> d
&nbsp;
        <span style="color: #ff7700;font-weight:bold;">def</span> tryIncr<span style="color: black;">&#40;</span>failure<span style="color: black;">&#41;</span>:
            <span style="color: #808080; font-style: italic;"># Всё аналогично функции tryAdd</span>
            failure.<span style="color: black;">trap</span><span style="color: black;">&#40;</span><span style="color: #008000;">KeyError</span><span style="color: black;">&#41;</span>
&nbsp;
            d = <span style="color: #008000;">self</span>.<span style="color: black;">mc</span>.<span style="color: black;">incr</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">key</span>, value<span style="color: black;">&#41;</span>
            d.<span style="color: black;">addErrback</span><span style="color: black;">&#40;</span>tryAdd<span style="color: black;">&#41;</span>
            <span style="color: #ff7700;font-weight:bold;">return</span> d
&nbsp;
        <span style="color: #808080; font-style: italic;"># Пытаемся выполнить инкремент, получаем Deferred</span>
        d = <span style="color: #008000;">self</span>.<span style="color: black;">mc</span>.<span style="color: black;">incr</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">key</span>, value<span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># Обрабатываем ошибку</span>
        d.<span style="color: black;">addErrback</span><span style="color: black;">&#40;</span>tryAdd<span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># Возвращаем Deferred вызывающему коду, он может тем самым:</span>
        <span style="color: #808080; font-style: italic;">#  а) узнать, когда операция действительно завершится</span>
        <span style="color: #808080; font-style: italic;">#  б) обработать необработанные нами ошибки (например, разрыв соединения)</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> d
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">def</span> value<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot;
        Получить значение счетчика.
&nbsp;
        @return: текущее значение счетчика
        @rtype: C{int}
        @return: Deferred, значение счетчика
        &quot;&quot;&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">def</span> handleKeyError<span style="color: black;">&#40;</span>failure<span style="color: black;">&#41;</span>:
            <span style="color: #808080; font-style: italic;"># Обрабатываем только KeyError</span>
            failure.<span style="color: black;">trap</span><span style="color: black;">&#40;</span><span style="color: #008000;">KeyError</span><span style="color: black;">&#41;</span>
&nbsp;
            <span style="color: #808080; font-style: italic;"># Ключа нет - возвращаем 0, он станет результатом</span>
            <span style="color: #808080; font-style: italic;"># вышележащего Deferred</span>
            <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #ff4500;">0</span>
&nbsp;
        <span style="color: #808080; font-style: italic;"># Пытаемся получить значение ключа</span>
        d = <span style="color: #008000;">self</span>.<span style="color: black;">mc</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">key</span><span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># Будем обрабатывать ошибку отсутствия ключа</span>
        d.<span style="color: black;">addErrback</span><span style="color: black;">&#40;</span>handleKeyError<span style="color: black;">&#41;</span>
        <span style="color: #808080; font-style: italic;"># Возвращаем Deferred, наверх там можно будет повеситься</span>
        <span style="color: #808080; font-style: italic;"># на его callback и получить искомое значение счетчика</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> d</pre></div></div>


<p>На практике приведенный выше код можно написать &laquo;короче&raquo;, объединяя часто используемые операции, например:</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">mc</span>.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #008000;">self</span>.<span style="color: black;">key</span><span style="color: black;">&#41;</span>.<span style="color: black;">addErrback</span><span style="color: black;">&#40;</span>handleKeyError<span style="color: black;">&#41;</span></pre></div></div>


<p>Практически для каждой конструкции синхронного кода можно найти аналог в асинхронной концепции с Deferred:</p>

<ul>
<li>последовательности синхронных операторов соответствует цепочка callback с асинхронными вызовами;</li>
<li>вызову одной подпрграммы с вводом-выводом из другой соответствует возврат Deferred из Deferred (ветвление Deferred);</li>
<li>глубокой цепочки вложенности, распространению исключений по стеку соответствует цепочка функций, возвращающие друг другу
Deferred;</li>
<li>блокам try..except соответствуют обработчики ошибок (errback), которые могут &laquo;пробрасывать&raquo; исключения дальше,
любое исключение в callback переводит выполнение в errback;</li>
<li>для &laquo;параллельного&raquo; выполнения асинхронных операций есть <code>DeferredList</code>.</li>
</ul>

<p>Нити часто применяются в асинхронных программах для осуществления вычислительных процедур, осуществления блокирующегося 
ввода-вывода (когда не существует асинхронного аналога). Всё это легко моделируется в простой модели &#8216;worker&#8217;, тогда 
нет необходимости при грамотной архитектуре в явной синхронизации, при этом всё элегантно включается в общий поток
вычислений с помощью Deferred:</p>


<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> doCalculation<span style="color: black;">&#40;</span>a, b<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot;
    В этой функции осуществляются вычисления, синхронные операции ввода-вывода,
    не затрагивающие основной поток.
    &quot;&quot;&quot;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> a/b
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> printResult<span style="color: black;">&#40;</span>result<span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">print</span> result
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> handleDivisionByZero<span style="color: black;">&#40;</span>failure<span style="color: black;">&#41;</span>:
    failure.<span style="color: black;">trap</span><span style="color: black;">&#40;</span><span style="color: #008000;">ZeroDivisionError</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">&quot;Ooops! Division by zero!&quot;</span>
&nbsp;
deferToThread<span style="color: black;">&#40;</span>doCalculation, <span style="color: #ff4500;">3</span>, <span style="color: #ff4500;">2</span><span style="color: black;">&#41;</span>.<span style="color: black;">addCallback</span><span style="color: black;">&#40;</span>printResult<span style="color: black;">&#41;</span>.<span style="color: black;">addCallback</span><span style="color: black;">&#40;</span>
    <span style="color: #ff7700;font-weight:bold;">lambda</span> _: deferToThread<span style="color: black;">&#40;</span>doCalculation, <span style="color: #ff4500;">3</span>, <span style="color: #ff4500;">0</span><span style="color: black;">&#41;</span>.<span style="color: black;">addErrback</span><span style="color: black;">&#40;</span>handleDivisionByZero<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>


<p>В приведенном выше примере функция <code>deferToThread</code> переносит выполнение указанной функции в отдельную нить и возвращает
Deferred, через который будет асинхронно получен результат выполнения функции или исключение, если они будет выброшено.
Первое деление (3/2) выполняется в отдельной нити, затем распечатывается его результат на экран, а затем запускается
еще одно вычисление (3/0), которое генерирует исключение, обрабатываемое функцией <code>handleDivisionByZero</code>.</p>

<p>В одной статье не описать и части того, что хотелось бы сказать о Deferred, мне удалось не написать ни слова о том,
как же они работают. Если успел заинтересовать &#8211; читайте материалы ниже, а я обещаю написать еще.</p>

<h2>Дополнительные материалы</h2>

<ul>
<li>Документация Twisted Framework:

<ul>
<li><a href="http://twistedmatrix.com/projects/core/documentation/howto/async.html">Основы асинхронного программирования</a></li>
<li><a href="http://twistedmatrix.com/projects/core/documentation/howto/defer.html">Использование Deferred</a>, <a href="http://twistedmatrix.com/projects/core/documentation/howto/gendefer.html">Откуда берутся Deferred?</a>, <a href="http://twistedmatrix.com/projects/core/documentation/howto/deferredindepth.html">Детальное описание Deferred</a></li>
</ul></li>
<li>Deferred в других языках программирования:

<ul>
<li>JavaScript: <a href="http://javascript.ru/tutorial/async/deferred-deep">Использование Deferred в JavaScript</a>, <a href="http://code.netstream.ru/wiki/QooxdooTwistedDeferred">Deferred в qooxdoo</a>, <a href="http://ajaxian.com/archives/dojos-deferred-api">Deferred в Dojo</a>, <a href="http://mochikit.com/doc/html/MochiKit/Async.html#fn-deferred">Deferred в MochiKit</a></li>
<li>C++: <a href="http://twistedmatrix.com/pipermail/twisted-python/2009-January/019119.html">1</a>, <a href="http://www.twistedmatrix.com/pipermail/twisted-python/2008-October/018548.html">2</a>, <a href="http://web.me.com/namezys/main/Блог/Записи/2009/1/26_Я_дописал_sequence._То_есть_питоновские_деферы.html">3</a></li>
<li>&#8230;</li>
</ul></li>
<li><a href="http://burus.org/2008/12/16/twisted-classic-examples/">Александр Бурцев о Twisted</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.smira.ru/2009/02/10/deferred-async-programming/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>twisted.python.log и trial &#8211; веселимся вместе</title>
		<link>http://www.smira.ru/2008/10/24/twisted-log-and-trial-fun/</link>
		<comments>http://www.smira.ru/2008/10/24/twisted-log-and-trial-fun/#comments</comments>
		<pubDate>Fri, 24 Oct 2008 05:32:27 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Twisted]]></category>
		<category><![CDATA[Разработка]]></category>
		<category><![CDATA[log]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[trial]]></category>
		<category><![CDATA[twisted]]></category>

		<guid isPermaLink="false">http://www.smira.ru/?p=153</guid>
		<description><![CDATA[Следующий забавный случай взаимодействия частей Twisted Framework заставил меня потерять час времени, спешу рассказать, в надежде, что это спасет чьё-то время. Итак, в Twisted есть модуль логгинга, twisted.python.log, в котором есть удобный метод log.err(), который позволяет записать в лог информацию о текущем исключении. А trial &#8211; это framework для написания юнит-тестов из того же Twisted. [...]]]></description>
			<content:encoded><![CDATA[<p>Следующий забавный случай взаимодействия частей <a href="http://twistedmatrix.com/">Twisted Framework</a> заставил меня потерять час времени, спешу рассказать, в надежде,
что это спасет чьё-то время. Итак, в Twisted есть модуль логгинга, <code>twisted.python.log</code>, в котором есть удобный метод <code>log.err()</code>, который позволяет записать в лог информацию о текущем исключении. А <code>trial</code> &#8211; это framework для написания юнит-тестов из того же Twisted. Их сочетание иногда приводит к проблеме <img src='http://www.smira.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>

<p><span id="more-153"></span></p>

<p>Итак, у меня был блок кода, похожий на пример ниже:</p>


<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">from</span> twisted.<span style="color: black;">python</span> <span style="color: #ff7700;font-weight:bold;">import</span> log
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> catcher<span style="color: black;">&#40;</span>f<span style="color: black;">&#41;</span>:
  <span style="color: #ff7700;font-weight:bold;">try</span>:
    <span style="color: #ff7700;font-weight:bold;">return</span> f<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
  <span style="color: #ff7700;font-weight:bold;">except</span> GoodException:
    <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">'ERROR'</span>
  <span style="color: #ff7700;font-weight:bold;">except</span>:
    log.<span style="color: black;">err</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">raise</span> UnknownException</pre></td></tr></table></div>


<p>По смыслу он должен был логировать все неожиданные исключения и заменять их на более разумные. Я написал юнит-тест на такую функцию примерно следующего вида:</p>


<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
</pre></td><td class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">def</span> testCatcher<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
  <span style="color: #ff7700;font-weight:bold;">def</span> raiseBadException<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">assert</span> <span style="color: #008000;">False</span> 
  <span style="color: #008000;">self</span>.<span style="color: black;">assertEquals</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'ERROR'</span>, catcher, raiseBadException<span style="color: black;">&#41;</span></pre></td></tr></table></div>


<p>При запуске юнит-тест говорил, что он завершился с ошибкой, т.к. было выброшено исключение 
<code>exceptions.AssertionError</code>, т.е. то исключение, которое генерирует строка 3 в тесте. Но ведь оно было поймано <code>catcher</code>? Я долго бился с этой ситуацией (на самом деле код был сложнее, но суть его от этого не менялась), пока не понял, что если из-под <code>trial</code> (запускальщика юнит-тестов) сделать <code>twisted.python.log.err</code>, то вместо того, чтобы вывести в лог исключение, он помечает тест как ошибочный, при этом в качестве причины ошибки выводит текущее исключение! То есть тест отрабатывал штатно, но <code>log.err()</code> мой помечал его как ошибочный.</p>

<p>Вот такое вот не совсем очевидное взаимодействие&#8230;</p>

<p><b>Update</b>: на самом деле &laquo;правильный&raquo; способ это исправить &#8211; использовать <a href="http://twistedmatrix.com/documents/11.0.0/api/twisted.trial.unittest.TestCase.html#flushLoggedErrors">TestCase.flushLoggedErrors</a>. Спасибо <a href="http://twitter.com/#!/paaleksey">@paaleksey</a> за наводку.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.smira.ru/2008/10/24/twisted-log-and-trial-fun/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

