<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Andrey Smirnov's Blog (Qik)</title><link>http://www.smira.ru/</link><description></description><language>en</language><lastBuildDate>Sun, 11 Jan 2015 19:24:29 GMT</lastBuildDate><generator>http://getnikola.com/</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>UDF в MySQL, json или то, как забрать обновления данных из БД</title><link>http://www.smira.ru/en/posts/20101030mysql-udf-json-memcacheq.html</link><dc:creator>Andrey</dc:creator><description>&lt;p&gt;Иногда необходимо забирать данные из БД MySQL в режиме реального времени во внешнюю систему,
которая никак не связана с &lt;a class="reference external" href="http://www.mysql.com/"&gt;MySQL&lt;/a&gt;. Существует множество возможных решений, например,
можно реализовать "слейва" MySQL, который бы хранил полученные данные во внешней системе.&lt;/p&gt;
&lt;p&gt;Одно из возможных решений - сделать "выгрузку" данных из MySQL с помощью
&lt;a class="reference external" href="http://dev.mysql.com/doc/refman/5.1/en/adding-functions.html"&gt;UDF (User Defined Functions)&lt;/a&gt; и триггеров. Для этого необходимо
поставить слейв MySQL, на котором уже повесить на интересующие таблицы триггеры, которые с помощью UDF будут выгружать поток
изменений таблиц во внешнюю систему. Слейв необходим, т.к. если триггеры поставить на мастере, то в случае отката
транзакции действия, уже сделанные триггерами, откатить не получится, а на слейв попадают только зафиксированные транзакции.
Второе,чтобы триггеры работали на слейве, тип репликации должен быть выставлен на
&lt;a class="reference external" href="http://www.smira.ru/posts/20100215mysql-row-statement-mixed-replication-triggers.html"&gt;STATEMENT-based&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Порывшись в одном &lt;a class="reference external" href="http://www.mysqludf.org/"&gt;интересном архиве&lt;/a&gt; UDF для MySQL я нашел несколько функций, которые мне подошли:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;преобразование строки MySQL в json;&lt;/li&gt;
&lt;li&gt;интерфейс с memcached.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;В результате получился следующий план действий: данные модифицируются на мастере, реплицируются на слейв с
помощью STATEMENT-репликации. В процессе репликации на слейве запускаются триггеры, формируют с помощью UDF
пакет обновлений в JSON, и передают его во внешнюю очередь (&lt;a class="reference external" href="http://memcachedb.org/memcacheq/"&gt;memcacheq&lt;/a&gt;) по memcached-протоколу.
Конечно, это не единственный возможный способ, но все UDF уже были почти готовы. После доделывания напильником
UDF получился вполне стабильно работающий вариант.&lt;/p&gt;
&lt;p&gt;Триггеры выглядят примерно следующим образом:&lt;/p&gt;
&lt;pre class="code sql literal-block"&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;kick_photos&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;memc_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'queue_db'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'insert'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'photos'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json_members&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'data'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thumbnail&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;thumbnail&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;photos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;dummy&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;photos&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;dummy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;


&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="n"&gt;photos_INSERT&lt;/span&gt; &lt;span class="k"&gt;AFTER&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;photos&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;EACH&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;
    &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;dummy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memc_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'queue_db'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'insert'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'photos'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json_members&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'data'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thumbnail&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;thumbnail&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)))));&lt;/span&gt;


&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="n"&gt;photos_DELETE&lt;/span&gt; &lt;span class="k"&gt;BEFORE&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;photos&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;EACH&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;
 &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;dummy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memc_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'queue_db'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'delete'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'photos'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json_members&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'data'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thumbnail&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;thumbnail&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)))));&lt;/span&gt;


&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="n"&gt;photos_UPDATE&lt;/span&gt; &lt;span class="k"&gt;AFTER&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;photos&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;EACH&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
    &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thumbnail&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;thumbnail&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thumbnail&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;thumbnail&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
        &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;dummy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memc_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'queue_db'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'update'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'photos'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json_members&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'new'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thumbnail&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;thumbnail&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;json_members&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'old'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;parent_id&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thumbnail&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;thumbnail&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;OLD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="k"&gt;size&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)))));&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Комментарии:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;функция &lt;tt class="docutils literal"&gt;kick_photos&lt;/tt&gt; позволяет скопировать строчку таблицы в очередь как пакет обновления типа "вставка", может
использоваться для начального наполнения внешней системы;&lt;/li&gt;
&lt;li&gt;триггеры на удаление и вставку просто формируют соответствующие пакеты;&lt;/li&gt;
&lt;li&gt;триггер на обновление проверяет, действительно ли в пакете произошли изменения (например, мы можем использовать не все поля в пакете);&lt;/li&gt;
&lt;li&gt;необходимо учесть, что работе &lt;tt class="docutils literal"&gt;FOREIGN KEY CONSTRAINT&lt;/tt&gt; триггеры не вызываются (очередной прикол MySQL), т.е., например,
если при выполнении запроса на удаление из таблицы &lt;tt class="docutils literal"&gt;A&lt;/tt&gt; будут по &lt;tt class="docutils literal"&gt;FOREIGN KEY&lt;/tt&gt; удалятся записи из таблицы &lt;tt class="docutils literal"&gt;B&lt;/tt&gt;, то в триггере
на удаление из &lt;tt class="docutils literal"&gt;A&lt;/tt&gt; необходимо отработать этот случай, т.к. триггеры на таблице &lt;tt class="docutils literal"&gt;B&lt;/tt&gt; не будут вызваны.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Код UDF доступен на github, это - "подпиленный" код из репозитория UDF или собственные разработки:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://github.com/smira/lib_mysqludf_json"&gt;превращение строки в json&lt;/a&gt; - были грязно исправлены проблемы с buffer overrun;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://github.com/smira/memcached_functions_mysql"&gt;запись в memcacheq&lt;/a&gt; - при запуске сервера будет настроена на запись в
&lt;tt class="docutils literal"&gt;localhost:22201&lt;/tt&gt;, плюс исправления для многопоточного режима и работы из нити workerа репликации;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://github.com/smira/mysql_udf_unix_timestamp_ms"&gt;timestamp с миллисекундной точностью&lt;/a&gt; - полезно для проставления
временных меток и анализа производительности репликации.&lt;/li&gt;
&lt;/ul&gt;</description><guid>http://www.smira.ru/en/posts/20101030mysql-udf-json-memcacheq.html</guid><pubDate>Sat, 30 Oct 2010 18:26:54 GMT</pubDate></item><item><title>HL++ (2009): Twisted Framework</title><link>http://www.smira.ru/en/posts/20091013hl-2009-twisted-framework.html</link><dc:creator>Andrey</dc:creator><description>&lt;p&gt;&lt;/p&gt;&lt;p&gt;Сегодня выступал на &lt;a href="http://highload.ru/"&gt;HighLoad++&lt;/a&gt; с докладом &lt;a href="http://www.highload.ru/papers2009/12261.html"&gt;Twisted Framework - фреймворк для написания сетевых приложений в Python&lt;/a&gt;. &lt;/p&gt;
&lt;h3&gt;Введение&lt;/h3&gt;
&lt;p&gt;Последнее время в области web происходит смещение внимания с тяжелых application-серверов, которые тратят на обработку запроса сотни миллисекунд, а то и секунды, к более легковесным сервисам, передающим меньшие объемы данных с минимальной задержкой. Переход от генерации десятков и сотен килобайт HTML-кода в ответ на запрос к передаче изменений в данных, запакованных в JSON и измеряемых сотнями байт. В качестве примеров таких сервисов можно привести Gmail, FriendFeed, Twitter Live Search и т.п.&lt;/p&gt;
&lt;p&gt;Для обеспечения минимальной задержки для пользователя необходимо либо поддерживать постоянное соединение (например, Adobe Flash, RTMP) или использовать технику HTTP long polling в сочетании с keep alive. Так или иначе на стороне сервера это приводит к появлению большого количества одновременных соединений (тысячи, десятки тысяч), по каждому из которых передается не такой большой объем данных. Эту ситуацию называют обычно проблемой &lt;a href="http://www.kegel.com/c10k.html"&gt;C10k&lt;/a&gt;.&lt;/p&gt;
&lt;!--more--&gt;

&lt;p&gt;Для обработки соединений архитектурный выбор на стороне сервера не такой большой: процесс на соединение, нить на соединение, комбинированный вариант процесс-нити или асинхронный ввод-вывод (возможно, в сочетании с дополнительными процессами или нитями). При наличии более 10 тысяч одновременных соединений с точки зрения расхода ресурсов совершенно невозможно представить создание 10 тысяч процессов; 10 тысяч нитей также вряд ли будет разумным решением. Необходимо дополнительно учесть, что при наличии такого большого числа соединений объем работы по каждому из них относительно невелик, большинство их них простаивают в ожидании поступления новых данных. Поэтому бóльшая часть процессов или нитей будет просто находиться в состоянии ожидания, расходуя впустую  системные ресурсы.&lt;/p&gt;
&lt;p&gt;Асинхронный ввод-вывод позволяет осуществлять неблокирующийся сетевой ввод-вывод по тысячам открытых сокетов в рамках одной нити выполнения (одного процесса). Механизмы реализации в разных ОС разные, например: select(), poll(), epoll(), kqueue() и т.п. Примеры приложений, использующих асинхронный ввод-вывод:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;nginx (используются дополнительные процессы для обслуживания задач, требующих большего объема CPU);&lt;/li&gt;
&lt;li&gt;haproxy;&lt;/li&gt;
&lt;li&gt;memcached;&lt;/li&gt;
&lt;li&gt;и другие.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Тем не менее, асинхронный ввод-вывод не является универсальным решением: для сервера БД это вряд ли было бы хорошим способом организации обслуживания соединений, так как для обработки каждого запроса требуется большой объем дискового ввода-вывода и процессорного времени, что не позволяет это сделать в рамках одного процесса. &lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.twistedmatrix.com/"&gt;Twisted Framework&lt;/a&gt; — это обширный набор классов и модулей для реализации асинхронных сетевых приложений. Twisted Framework — это:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ядро, абстрагирующее все операции асинхронного ввода-вывода и использующее соответствующий механизм конкретной ОС;&lt;/li&gt;
&lt;li&gt;концепция Deferred, которая позволяет реализовать в простой форме обслуживание запроса: асинхронные сетевые обращения (например, к БД, memcached), обработку ошибочных ситуаций; Deferred является аналогом обычных конструкций последовательного программирования для асинхронной модели программирования;&lt;/li&gt;
&lt;li&gt;обширный набор уже реализованных сетевых протоколов: HTTP, DNS, SMTP, IMAP, memcached, Jabber, ICQ и т.д.; еще большее количество протоколов доступно в виде дополнительных модулей;&lt;/li&gt;
&lt;li&gt;дополнительная инфраструктура: unit-testы с поддержкой Deferred, пулы нитей, процессов и т.д.; &lt;/li&gt;
&lt;li&gt;качественная концепция разработки — полное покрытие unit-testами, строгий review любого изменения.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Основная часть доклада будет посвящена конкретным примерам приложений, реализованными с помощью Twisted — с архитектурой, конкретными параметрами производительности, приемами оптимизации, преимуществами и недостатками Twisted для решения данной задачи:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.smira.ru/2008/04/09/rit-2008/"&gt;RTMP-сервер pyFMS&lt;/a&gt;, сервер вещаний сервиса Smotri.Com (сотни трансляций, десятки тысяч зрителей);&lt;/li&gt;
&lt;li&gt;backend-сервер &lt;a href="http://www.mdc.ru/"&gt;проекта MDC&lt;/a&gt; - хранение и обработка истории общения пользователей, хранение настроек и т.п.;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.smira.ru/2009/07/12/qik-push-engine-api-private-beta/"&gt;Qik Push Engine&lt;/a&gt; - сервер немедленной доставки изменений информации о видео, созданных пользователями сервиса, в том числе push-нотификация о появившихся live-стримах, масштабирование, обработка больших объемов информации.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Дополнительная информация:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://twistedmatrix.com/projects/core/documentation/howto/"&gt;Документация Twisted&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.smira.ru/category/development/python-development/"&gt;О Python&lt;/a&gt;, а также о &lt;a href="http://www.smira.ru/category/development/twisted-development/"&gt;Twisted&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://habrahabr.ru/blogs/twisted/"&gt;Блог на Хабрахабре про Twisted&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://burus.org/2008/12/16/twisted-classic-examples/"&gt;Александр Бурцев о Twisted&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.smira.ru/2009/02/10/deferred-async-programming/"&gt;Deferred в Twisted&lt;/a&gt; и &lt;a href="http://www.smira.ru/2009/02/24/more-about-deferred/"&gt;не только&lt;/a&gt; .&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Презентация&lt;/h3&gt;
&lt;p&gt;&lt;/p&gt;&lt;div style="width:425px;text-align:left" id="__ss_2211313"&gt;&lt;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"&gt;Twisted Framework - сетевые приложения в Python&lt;/a&gt;&lt;object style="margin:0px" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=smirnov-twisted-python-091013134034-phpapp02&amp;amp;stripped_title=twisted-framework-python-2211313"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowScriptAccess" value="always"&gt;&lt;embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=smirnov-twisted-python-091013134034-phpapp02&amp;amp;stripped_title=twisted-framework-python-2211313" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;"&gt;View more &lt;a style="text-decoration:underline;" href="http://www.slideshare.net/"&gt;documents&lt;/a&gt; from &lt;a style="text-decoration:underline;" href="http://www.slideshare.net/Smirnov.Andrey"&gt;Smirnov.Andrey&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.smira.ru/wp-content/uploads/2009/10/smirnov-twisted-python.pdf"&gt;Скачать презентацию (PDF)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><guid>http://www.smira.ru/en/posts/20091013hl-2009-twisted-framework.html</guid><pubDate>Tue, 13 Oct 2009 17:42:27 GMT</pubDate></item><item><title>Mongrel vs. Phusion Passenger: выбор очевиден</title><link>http://www.smira.ru/en/posts/20091005mongrel-vs-phusion-passenger-obvious-choice.html</link><dc:creator>Andrey</dc:creator><description>&lt;p&gt;&lt;/p&gt;&lt;p&gt;Предыдущая конфигурация:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://sysoev.ru/nginx/"&gt;nginx&lt;/a&gt; (главный proxy), который раздает трафик в&lt;/li&gt;
&lt;li&gt;&lt;a href="http://haproxy.1wt.eu/&amp;lt;/p&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html"&gt;haproxy&lt;/a&gt; (ради возможности балансировать по нагрузке), который распределяет нагрузку по нескольким webapp-серверам&lt;/li&gt;
&lt;li&gt;с 16-ю &lt;a href="http://mongrel.rubyforge.org/"&gt;mongrelами&lt;/a&gt; на каждом&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Проблемы:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;"Утекающая" память, периодический out of memory на серверах, лечится только перезапуском &lt;a href="http://mongrel.rubyforge.org/"&gt;mongrelов&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Запросы, занимающие десятки секунд из-за неверной балансировки (в нагруженный &lt;a href="http://mongrel.rubyforge.org/"&gt;mongrel&lt;/a&gt; все-таки попадает несколько "тяжелых" запросов).&lt;/li&gt;
&lt;li&gt;Сложность управления кластером монгрелов - постоянные проблемы при перезапуске, "не стартующие" &lt;a href="http://mongrel.rubyforge.org/"&gt;mongrelы&lt;/a&gt; и т.п.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Новая конфигурация:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://sysoev.ru/nginx/"&gt;nginx&lt;/a&gt; (proxy) остался&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.modrails.com/"&gt;Phusion Passenger&lt;/a&gt; + &lt;a href="http://www.rubyenterpriseedition.com/"&gt;Ruby Enterprise Edition&lt;/a&gt; на каждой машине.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Результат:&lt;/p&gt;
&lt;p&gt;&lt;img src="http://www.smira.ru/wp-content/uploads/2009/10/webapp01-passenger-mongrel.png" alt="webapp01-passenger-mongrel" title="webapp01-passenger-mongrel" width="603" height="250" class="aligncenter size-full wp-image-508"&gt;&lt;/p&gt;
&lt;p&gt;Комментарий: переход на &lt;a href="http://www.modrails.com/"&gt;Phusion Passenger&lt;/a&gt; на Week 39, объем занятой памяти - это белая область на графике, растущая сверху вниз. До перехода на Passenger объем свободной памяти стремительно уменьшался, иногда доходя до нуля, после перехода остается более-менее стабильным. Использование CPU осталось на прежнем уровне (как и ожидалось).&lt;/p&gt;
&lt;p&gt;После перехода исчезли запросы, которые по непонятным причинам занимали десятки секунд - время выполнения коррелирует со сложностью запроса.&lt;/p&gt;
&lt;p&gt;Так что если вы еще не переключились, мы идем к вам :)&lt;/p&gt;
&lt;p&gt;P.S. Отдельное спасибо &lt;a href="http://github.com/glebpom"&gt;glebpom&lt;/a&gt; за подсказку.&lt;/p&gt;</description><guid>http://www.smira.ru/en/posts/20091005mongrel-vs-phusion-passenger-obvious-choice.html</guid><pubDate>Mon, 05 Oct 2009 18:41:00 GMT</pubDate></item><item><title>Qik Push Engine API: приглашаем разработчиков</title><link>http://www.smira.ru/en/posts/20090712qik-push-engine-api-private-beta.html</link><dc:creator>Andrey</dc:creator><description>&lt;p&gt;&lt;/p&gt;&lt;a href="http://qik.com/"&gt;&lt;img src="http://www.smira.ru/wp-content/uploads/2009/07/qik_logo.png" alt="qik_logo" title="qik_logo" width="177" height="92" class="alignleft size-full wp-image-462"&gt;&lt;/a&gt; &lt;a href="http://qik.com/"&gt;Qik&lt;/a&gt; - это сервис стриминга (вещания) и загрузки видео с мобильных телефонов. Загруженное видео можно посмотреть на &lt;a href="http://qik.com/"&gt;сайте&lt;/a&gt; или на его &lt;a href="http://m.qik.com/"&gt;специальной версии&lt;/a&gt; с мобильного телефона. Доступна интеграция с другими сервисами, такими как Twitter, Facebook и другие. Клиенты для практически всех современных моделей телефонов: iPhone, Windows Mobile, Symbian, Android, Blackberry и другие.
&lt;p&gt;Qik Push Engine - это механизм, который позволяет получать мгновенные оповещения о новых/изменившихся Qik-видео. Например, можно посмотреть постоянно обновляющийся список live-видео, все видео из района Новопеределкино или все видео со словом "кошка". На основе Qik Push Engine API можно построить интересные приложения, интегрированные с Qik, или добавить функциональность в уже существующие. Можно написать собственную систему нотификации, desktop-widget &lt;/p&gt;
&lt;p&gt;или что-то еще.&lt;/p&gt;
&lt;p&gt;Сегодня мы открываем API для работы c Qik Push Engine. Это первая ласточка в большом списке API, открывающих доступ к платформе стриминга Qik. Если вам интересно посмотреть Qik Push Engine в действии, заходите на одну из &lt;a href="http://engine.qik.com/examples/all.videos.html"&gt;страниц примеров&lt;/a&gt;.&lt;/p&gt;
&lt;!--more--&gt;

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

POST /api/jsonrpc

Host : engine.stage.qik.com

Content-Type:  application/json; charset=UTF-8

Content-Length: 46



{"method": "qik.session.create", "params": []}

&lt;/pre&gt;

&lt;p&gt;Ответ:&lt;/p&gt;
&lt;pre&gt;

Content-Type: text/json 



["a56c1603-1fbb-4140-8b35-8c36abbd8b27"]

&lt;/pre&gt;

&lt;h3&gt;Пример подписки на поток событий&lt;/h3&gt;
&lt;p&gt;Здесь и далее я буду использовать простую запись вызова команд, которая похожа на вызов обычных функций, опуская детали JSON-RPC взаимодействия. Итак, пусть мы хотим подписаться на список всех live-стримов, которые есть данный момент.&lt;/p&gt;
&lt;p&gt;Первым делом создадим сессию:&lt;/p&gt;
&lt;pre&gt;

qik.session.create() -&amp;gt; "a56c1603-1fbb-4140-8b35-8c36abbd8b27"

&lt;/pre&gt;

&lt;p&gt;Подписываемся в рамках сессии на view всех публичных live-стримов, при этом указываем максимальное количество элементов во view (limit). Элементы view будут упорядочены в порядке убывания даты начала стрима (самые новые первыми):&lt;/p&gt;
&lt;pre&gt;

qik.stream.subscribe\_public_live("a56c1603-1fbb-4140-8b35-8c36abbd8b27", 10) -&amp;gt; 
 [ "174/('PublicLiveStreamView', 'Stream', [('stoptime', None)], 'alllive;limit=10')", [ ... ] ]
&lt;/pre&gt;

&lt;p&gt;В ответ на запрос мы получаем массив из двух элементов: первым является ключ подписки (длинная строка, начинающаяся с "174/.."). Не ищите смысла в ключе подписки, его необходимо просто сохранить, он потребуется в дальнейшем.&lt;/p&gt;
&lt;p&gt;Вторым элементом является начальное состояние view, то есть текущий список live-стримов в нашем случае. Мы получим не больше 10 элементов (так как указали limit 10 при вызове метода &lt;code&gt;qik.stream.subscribe_public_live&lt;/code&gt;). Состояние view выглядит примерно следующим образом:&lt;/p&gt;
&lt;pre lang="javascript"&gt;

[
   {"url": "http://qik.com/video/2158468", 
    "live": true, 
    "user_id": 340699, 
    "small_thumbnail_url": "http://media.qik.com/media.thumbnails.128/c8ad8fe065ad4ad7ac8491874c043eac.jpg", 
    "title": "Untitled", "duration": 0,
    "created_at": "2009-07-11 15:56:03", 
    "views": 0, 
    "id": 2158468}, 

   {"url": "http://qik.com/video/2158466", 
    "live": true, 
    "user_id": 340119, 
    "small_thumbnail_url": "http://media.qik.com/media.thumbnails.128/984c33e1ead441038f315e1fff109fc5.jpg", 
     "title": "Testando!", 
     "duration": 0, "created_at": "2009-07-11 15:55:34", 
     "views": 0, 
     "id": 2158466}
]

&lt;/pre&gt;

&lt;p&gt;Смысл большинства полей очевиден, приведу лишь некоторые комментарии:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;все даты приведены в UTC;&lt;/li&gt;
&lt;li&gt;поле &lt;code&gt;duration&lt;/code&gt; хранит длительность видео в секундах.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Так как мы подписались на live-стримы, у всех видео &lt;code&gt;live == true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Теперь нам необходимо получать новые события для тех view, на которые мы подписались. Для этого используются HTTP long polling-запросы. Клиент отправляет запрос серверу, а сервер отправляет ответ, когда появляются события или когда истечет таймаут, если событий не появилось. &lt;/p&gt;
&lt;p&gt;Для реализации long polling в цикле вызываем метод &lt;code&gt;qik.session.get_events&lt;/code&gt;, указывая желаемый таймаут (не рекомендуется использовать таймаут более 90 секунд).&lt;/p&gt;
&lt;pre&gt;

qik.session.get_events("a56c1603-1fbb-4140-8b35-8c36abbd8b27", 60) -&amp;gt; 
  [ {...} ,{...} ]
&lt;/pre&gt;

&lt;p&gt;В ответ мы можем получить пустой массив событий, если истек таймаут, или некоторый набор событий следующего вида:&lt;/p&gt;
&lt;pre lang="javascript"&gt;

[
   {
     "action": "update", 
     "old": {"id": 104252, "title": "Untitled", "views": 0, ...}, 
     "obj": {"id": 104252, "title": "driving to key largo", "views": 5, ...}, 
     "key": "175/('PublicRecentStreamView', 'Stream', [], 'allrecent;limit=50')"
    },
] 

&lt;/pre&gt;

&lt;p&gt;В каждом событии обязательно передается следующая инормация:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;key&lt;/code&gt; - ключ подписки, это то же самое значение, которое мы получили при подписке на view, ключ позволяет отличать события для разных view в рамках одной сессии;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;action&lt;/code&gt; - действие (изменение), произошедшее с view:&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ping&lt;/code&gt; - ничего не произошло, просто view сообщает, что все хорошо :)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;update&lt;/code&gt; - элемент view изменился (как в примере выше), передается старое и новое состояние объекта в полях &lt;code&gt;old&lt;/code&gt; и &lt;code&gt;obj&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;insert&lt;/code&gt; - во view добавился новый элемент, его состояние будет записано в поле &lt;code&gt;obj&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;delete&lt;/code&gt; - элемент исчез из view (может быть, видео было удалено, или просто перестало удовлетворять критериям view, или ушло за границу "limit"), последнее состояние объекта будет записано в поле &lt;code&gt;obj&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;В ответ на события мы можем обновить информацию на экране для пользователя, выполнить какие-либо еще действия. Но обязательно надо отправить новый запрос &lt;code&gt;qik.session.get_events&lt;/code&gt;, чтобы получить новые события.&lt;/p&gt;
&lt;p&gt;В конце работы не забываем "убраться" за собой, убиваем сессию:&lt;/p&gt;
&lt;pre&gt;

qik.session.destroy("a56c1603-1fbb-4140-8b35-8c36abbd8b27") 

&lt;/pre&gt;

&lt;p&gt;Замечания:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;В рамках одной сессии можно подписаться на произвольное количество view. &lt;/li&gt;
&lt;li&gt;Если это необходимо, можно авторизовать сессию от имени пользователя с помощью &lt;code&gt;qik.session.authorize&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Существует большое количество вспомогательных запросов, которым сессия вообще не требуется, например &lt;code&gt;qik.user.public_profile&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Можно отписаться от view при помощи конмады&lt;code&gt;qik.session.unsubscribe&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Reference JS-клиент&lt;/h3&gt;
&lt;p&gt;Для демонстрации возможностей Qik Push Engine и проверки его работы был реализован легкий JavaScript-клиент. Его можно увидеть в работе на примерах, полный список которых приведен ниже.&lt;/p&gt;
&lt;p&gt;Если вы запустите пример в Firefox с включенным Firebug, в консоли Firebug будет появляться подробная информация о выполняемых запросах к Qik Push Engine API, полученных ответах и т.п.&lt;/p&gt;
&lt;p&gt;Исходный код клиента доступен в необфусцированном виде, со всеми комментариями. Код предоставляет по принципам public domain, то есть можете делать с ним все, что захотите - использовать в своих приложениях, модифицировать, переписывать на другие языкаи программирования и т.п.&lt;/p&gt;
&lt;p&gt;Пробежимся быстро по компонентам JS-клиента.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://engine.qik.com/examples/js/prototype.js"&gt;prototype.js&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Совершенно стандартный &lt;a href="http://www.prototypejs.org/"&gt;Prototype&lt;/a&gt;, ничего интересного.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://engine.qik.com/examples/js/deferred.js"&gt;deferred.js&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Deferred&lt;/code&gt; - это одна из основных концепций Twisted Framework (http://twistedmatrix.com). Deferred не является обязательным для взаимодействия с Qik Push Engine API, но позволяет сильно упростить сложные взаимодействия в асинхронном коде. &lt;/p&gt;
&lt;p&gt;В качестве дополнительного материала о Deferred можно почитать про &lt;a href="http://www.smira.ru/2009/02/10/deferred-async-programming/"&gt;основы работы с Deferred&lt;/a&gt;, &lt;a href="http://www.smira.ru/2009/02/24/more-about-deferred/"&gt;более сложные случаи использования Deferred&lt;/a&gt;, &lt;a href="http://www.smira.ru/2009/05/29/deferred-in-javascript-for-prototype/"&gt;данную реализацию Deferred для Prototype&lt;/a&gt;, а также взглянуть на Deferred в других JS-фреймворках: &lt;a href="http://mochikit.com/doc/html/MochiKit/Async.html"&gt;MochiKit&lt;/a&gt;, 
 &lt;a href="http://www.dojotoolkit.org/book/dojo-book-0-9/part-3-programmatic-dijit-and-dojo/miscellaneous/communication-between-threads-do"&gt;Dojo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;DeferredManager&lt;/code&gt; - простая конструкция на основе Deferred, позволяет запускать не более N асинхронных действий в один момент времени.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://engine.qik.com/examples/js/api.js"&gt;core.js&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Ядро клиента, объявляется namespace &lt;code&gt;QikEngine&lt;/code&gt;, отладочные функции, определение конфигурации, создания объекта для доступа к API и т.п.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://engine.qik.com/examples/js/api.js"&gt;api.js&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;QikEngine.API&lt;/code&gt; - простая обертка JSON-RPC API Qik Push Engine, возвращающая &lt;code&gt;Deferred&lt;/code&gt; в качестве результата любого обращения к API. Через &lt;code&gt;Deferred&lt;/code&gt; же отправляются ошибки и результаты выполнения методов.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;QikEngine.Session&lt;/code&gt; - класс, оборачивающий понятие сессии, создание, уничтожение, авторизация в рамках сессии.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://engine.qik.com/examples/js/events.js"&gt;events.js&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;QikEngine.Events&lt;/code&gt; - получение новых событий с помощью HTTP long polling, технология, похожая на BOSH/Comet. Распределение событий подписчикам (конкретным view). &lt;/p&gt;
&lt;h4&gt;&lt;a href="http://engine.qik.com/examples/js/view.js"&gt;view.js&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;QikEngine.View&lt;/code&gt; - абстрактный класс, представляющий view на стороне клиента. Обслуживание обновлений, отображение в элементах HTML, обработка событий и т.п. Списки элементов хранятся прямо в DOM-контейнере в виде HTML-узлов.&lt;/p&gt;
&lt;p&gt;Потомки &lt;code&gt;QikEngine.View&lt;/code&gt; для реализации конкретных view:  &lt;code&gt;QikEngine.PublicUserStreamView&lt;/code&gt;, &lt;code&gt;QikEngine.PublicRecentStreamsView&lt;/code&gt; и т.д.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://engine.qik.com/examples/js/updater.js"&gt;updater.js&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Очень простой интерфейс между данными, полученными из Qik Push Engine (например, информацией о стримах) и HTML-элементами. Адаптация данных, например, перевод дат из UTC в локальный формат, форматирование значений и т.п.&lt;/p&gt;
&lt;h4&gt;&lt;a href="http://engine.qik.com/examples/js/usercache.js"&gt;usercache.js&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;QikEngine.UserCache&lt;/code&gt; - механизм загрузки информации о пользователях (по их ID). Использует &lt;code&gt;DeferredManager&lt;/code&gt; для ограничения количества параллельных запросов.&lt;/p&gt;
&lt;h3&gt;Что делать дальше?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;смотрите &lt;a href="http://qikapi.pbworks.com/"&gt;описание API&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;пишите письмо на api@qik.com, расскажите о своем проекте и получайте ключ для доступа к API.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Полный список примеров&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://engine.qik.com/examples/all.videos.html"&gt;все последние видео на Qik/live-видео&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://engine.qik.com/examples/profile.html"&gt;открытые стримы пользователя&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://engine.qik.com/examples/user.videos.html"&gt;профайл пользователя с точки зрения другого авторизованного пользователя&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://engine.qik.com/examples/user.home.html"&gt;домашняя страница пользователя: собственные стримы, followerы, стримы от following&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Во всех примерах списки видео обновляются автоматически при внесении изменений на сайте или с мобильного телефона, например, если открою доступ Васе к моему приватному видео, он тут же увидит это видео в списке моих стримов и т.п. &lt;/p&gt;</description><guid>http://www.smira.ru/en/posts/20090712qik-push-engine-api-private-beta.html</guid><pubDate>Sun, 12 Jul 2009 12:56:40 GMT</pubDate></item></channel></rss>