<?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>Insight IT &#187; PHP</title>
	<atom:link href="http://www.insight-it.ru/tag/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.insight-it.ru</link>
	<description>Информационные технологии</description>
	<lastBuildDate>Tue, 31 Jan 2012 09:34:08 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Архитектура Вконтакте</title>
		<link>http://www.insight-it.ru/masshtabiruemost/arkhitektura-vkontakte/</link>
		<comments>http://www.insight-it.ru/masshtabiruemost/arkhitektura-vkontakte/#comments</comments>
		<pubDate>Thu, 28 Oct 2010 17:12:52 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Масштабируемость]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Debian]]></category>
		<category><![CDATA[featured]]></category>
		<category><![CDATA[ffmpeg]]></category>
		<category><![CDATA[HAProxy]]></category>
		<category><![CDATA[highload]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Memcached]]></category>
		<category><![CDATA[mod_php]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[node.js]]></category>
		<category><![CDATA[openssl]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[XCache]]></category>
		<category><![CDATA[xfs]]></category>
		<category><![CDATA[Архитектура Вконтакте]]></category>
		<category><![CDATA[Вконтакте]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=641</guid>
		<description><![CDATA[Самая популярная социальная сеть в рунете пролила немного света на то, как же она работает. Представители проекта в лице Павла Дурова и Олега Илларионова на конференции HighLoad++ ответили на шквал вопросов по совершенно разным аспектам работы Вконтакте, в том числе и техническим. Спешу поделиться своим взглядом на архитектуру проекта по результатам данного выступления. Платформа Debian [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.insight-it.ru/wp-content/uploads/2010/10/vkontakte-512x512.png"><img class="alignleft size-medium wp-image-655" style="float: left; margin: 0 8px; border: 0!important;" title="Логотип Вконтакте" src="http://www.insight-it.ru/wp-content/uploads/2010/10/vkontakte-512x512-150x150.png" alt="Логотип Вконтакте" width="125" height="125" /></a><br />
Самая популярная социальная сеть в рунете пролила немного света на то, как же она работает. Представители проекта в лице Павла Дурова и Олега Илларионова на конференции <a href="http://www.insight-it.ru/life/highload-2010/" target="_blank">HighLoad++</a> ответили на шквал вопросов по совершенно разным аспектам работы <a href="http://www.vkontakte.ru" target="_blank">Вконтакте</a>, в том числе и техническим. Спешу поделиться своим взглядом на архитектуру проекта по результатам данного выступления.<span id="more-641"></span></p>
<h2>Платформа</h2>
<ul>
<li><a href="/tag/debian" target="_blank">Debian</a> <a href="/tag/linux" target="_blank">Linux</a> &#8212; основная операционная система</li>
<li><a href="/tag/nginx" target="_blank">nginx</a> &#8212; балансировка нагрузки</li>
<li><a href="/tag/php" target="_blank">PHP</a> + <a href="/tag/xcache" target="_blank">XCache</a></li>
<li><a href="/tag/apache" target="_blank">Apache</a> + <a href="/tag/mod_php" target="_blank">mod_php</a></li>
<li><a href="/tag/memcached" target="_blank">memcached</a></li>
<li><a href="/tag/mysql" target="_blank">MySQL</a></li>
<li>Собственная СУБД на <a href="/tag/c" target="_blank">C</a>, созданная &#171;лучшими умами&#187; России</li>
<li><a href="/tag/node-js" target="_blank">node.js</a> &#8212; прослойка для реализации XMPP, живет за <a href="/tag/haproxy" target="_blank">HAProxy</a></li>
<li>Изображения отдаются просто с файловой системы <a href="/tag/xfs">xfs</a></li>
<li><a href="/tag/ffmpeg">ffmpeg</a> &#8212; конвертирование видео</li>
</ul>
<h2>Статистика</h2>
<ul>
<li>95 миллионов учетных записей</li>
<li>40 миллионов активных пользователей во всем мире (сопоставимо с аудиторией интернета в России)</li>
<li>11 миллиардов запросов в день</li>
<li>200 миллионов личных сообщений в день</li>
<li>Видеопоток достигает 160Гбит/с</li>
<li>Более 10 тысяч серверов, из которых только 32 &#8212; фронтенды на <a href="/tag/nginx" target="_blank">nginx</a> (количество серверов с <a href="/tag/apache" target="_blank">Apache </a>неизвестно)</li>
<li>30-40 разработчиков, 2 дизайнера, 5 системных администраторов, много людей в датацентрах</li>
<li>Каждый день выходит из строя около 10 жестких дисков</li>
</ul>
<h2>Архитектура</h2>
<h3>Общие принципы</h3>
<ul>
<li>Cервера многофункциональны и используются одновременно в нескольких ролях:
<ul>
<li>Перебрасывание полуавтоматическое</li>
<li>Требуется перезапускать daemon&#8217;ы</li>
</ul>
</li>
<li>Генерация страниц с новостями (микроблоги) происходит очень похожим образом с <a href="/tag/facebook" target="_blank">Facebook</a> (см. <a href="/masshtabiruemost/arkhitektura-facebook/" target="_blank">Архитектура  Facebook</a>), основное отличие &#8212; использование собственной СУБД вместо MySQL</li>
<li>При балансировке нагрузки используются:
<ul>
<li>Взвешенный round robin внутри системы</li>
<li>Разные сервера для разных типов запросов</li>
<li>Балансировка на уровне ДНС на 32 IP-адреса</li>
</ul>
</li>
<li>Большая часть внутреннего софта написано самостоятельно, в том числе:
<ul>
<li>Собственная СУБД (см. ниже)</li>
<li>Мониторинг с уведомлением по СМС (Павел сам помогал верстать интерфейс <img src='http://www.insight-it.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  )</li>
<li>Автоматическая система тестирования кода</li>
<li>Анализаторы статистики и логов</li>
</ul>
</li>
<li>Мощные сервера:
<ul>
<li>8-ядерные процессоры Intel (по два на сервер, видимо)</li>
<li>64Гб оперативной памяти</li>
<li>8 жестких дисков (соответственно скорее всего корпуса 2-3U)</li>
<li>RAID не используется</li>
<li>Не брендированные, собирает компания ТехноОкта</li>
</ul>
</li>
<li>Вычислительные мощности серверов используются менее, чем на 20%</li>
<li>Сейчас проект расположен в 4 датацентрах в Санкт-Петербурге и Москве, причем:
<ul>
<li>Вся основная база данных располагается в одном датацентре в Санкт-Петербурге</li>
<li>В Московских датацентрах только аудио и видео</li>
<li>В планах сделать репликацию базы данных в другой датацентр в ленинградской области</li>
</ul>
</li>
<li><a href="/tag/cdn">CDN</a> на данный момент не используется, но в планах есть</li>
<li>Резервное копирование данных происходит ежедневно и инкрементально</li>
</ul>
<h3>Волшебная база данных на <a href="/tag/c" target="_blank">C</a></h3>
<p>Этому продукту, пожалуй, уделялось максимум внимания аудитории, но при этом почти никаких подробностей о том, что он собственно говоря собой представляет, так и не было обнародовано. Известно, что:</p>
<ul>
<li>Разработана &#171;лучшими умами&#187; России, победителями олимпиад и конкурсов топкодер; озвучили даже имена этих &#171;героев&#187; Вконтакте (писал на слух и возможно не всех успел, так что извиняйте):
<ul>
<li>Андрей Лопатин</li>
<li>Николай Дуров</li>
<li>Арсений Смирнов</li>
<li>Алексей Левин</li>
</ul>
</li>
<li>Используется в огромном количестве сервисов:
<ul>
<li>Личные сообщения</li>
<li>Сообщения на стенах</li>
<li>Статусы</li>
<li>Поиск</li>
<li>Приватность</li>
<li>Списки друзей</li>
</ul>
</li>
<li>Нереляционная модель данных</li>
<li>Большинство операций осуществляется в оперативной памяти</li>
<li>Интерфейс доступа представляет собой расширенный протокол <a href="/tag/memcached" target="_blank">memcached</a>, специальным образом составленные ключи возвращают результаты сложных запросов (чаще всего специфичных для конкретного сервиса)</li>
<li>Хотели бы сделать из данной системы универсальную СУБД и опубликовать под GPL, но пока не получается из-за высокой степени интеграции с остальными сервисами</li>
<li>Кластеризация осуществляется легко</li>
<li>Есть репликация</li>
<li>Если честно, я так и не понял зачем им <a href="/tag/mysql" target="_blank">MySQL </a>с такой штукой &#8212; возможно просто как legacy живет со старых времен</li>
</ul>
<h3>Аудио и видео</h3>
<p>Эти подпроекты являются побочными для социальной сети, на них особо не фокусируются. В основном это связанно с тем, что они редко коррелируют с основной целью использования социальной сети &#8212; <em>общением</em>, а также создают большое количество проблем: видеотраффик &#8212; основная статья расходов проекта, плюс всем известные проблемы с нелегальным контентом и претензиями правообладателей. Медиа-файлы банятся по хэшу при удалении по просьбе правообладателей, но это неэффективно и планируется усовершенствовать этот механизм.</p>
<p>1000-1500 серверов используется для перекодирования видео, на них же оно и хранится.</p>
<h3>XMPP</h3>
<p>Как известно, некоторое время назад появилась возможность общаться на Вконтакте через протокол Jabber (он же XMPP). Протокол совершенно открытый и существует масса opensource реализаций.</p>
<p>По ряду причин, среди которых проблемы с интеграцией с остальными сервисами, было решено за месяц создать собственный сервер, представляющий собой прослойку между внутренними сервисами Вконтакте и реализацией XMPP протокола. Основные особенности этого сервиса:</p>
<ul>
<li>Реализован на <a href="/tag/node-js">node.js</a> (выбор обусловлен тем, что JavaScript знают практически все разработчики проекта, а также хороший набор инструментов для реализации задачи)</li>
<li>Работа с большими контакт-листами &#8212; у многих пользователей количество друзей на вконтакте измеряется сотнями и тысячами</li>
<li>Высокая активность смены статусов &#8212; люди появляются и исчезают из онлайна чаще, чем в других аналогичных ситуациях</li>
<li>Аватарки передаются в base64</li>
<li>Тесная интеграция с внутренней системой обмена личными сообщениями Вконтакте</li>
<li>60-80 тысяч человек онлайн, в пике &#8212; 150 тысяч</li>
<li><a href="/tag/haproxy" target="_blank">HAProxy</a> обрабатывает входящие соединения и используется для балансировки нагрузки и развертывания новых версий</li>
<li>Данные хранятся в <a href="/tag/mysql" target="_blank">MySQL</a> (думали о MongoDB, но передумали)</li>
<li>Сервис работает на 5 серверах разной конфигурации, на каждом из них работает код на<a href="/tag/node-js" target="_blank"> node.js</a> (по 4 процесса на сервер), а на трех самых мощных &#8212; еще и <a href="/tag/mysql" target="_blank">MySQL</a></li>
<li>В <a href="/tag/node-js" target="_blank">node.js</a> большие проблемы с использованием <a href="/tag/openssl" target="_blank">OpenSSL</a>, а также течет память</li>
<li>Группы друзей в XMPP не связаны с группами друзей на сайте &#8212; сделано по просьбе пользователей, которые не хотели чтобы их друзья из-за плеча видели в какой группе они находятся</li>
</ul>
<h3>Интеграция со внешними ресурсами</h3>
<p>Во Вконтакте считают данное направление очень перспективным и осуществляют массу связанной с этим работы. Основные предпринятые шаги:</p>
<ul>
<li>Максимальная кроссбраузерность для виджетов на основе библиотек easyXDM и fastXDM</li>
<li>Кросс-постинг статусов в <a href="/tag/twitter">Twitter</a>, реализованный с помощью очередей запросов</li>
<li>Кнопка &#171;поделиться с друзьями&#187;, поддерживающая openGraph теги и автоматически подбирающая подходящую иллюстрацию (путем сравнивание содержимых тега &lt;title&gt; и атрибутов alt у изображений, чуть ли не побуквенно)</li>
<li>Возможность загрузки видео через сторонние видео-хостинги (YouTube, RuTube, Vimeo, и.т.д.), открыты к интеграции с другими</li>
</ul>
<h2>Интересные факты не по теме</h2>
<ul>
<li>Процесс разработки близок к Agile, с недельными итерациями</li>
<li>Ядро операционной системы модифицированно (на предмет работы с памятью), есть своя пакетная база для <a href="/tag/debian">Debian</a></li>
<li>Фотографии загружаются на два жестких диска одного сервера одновременно, после чего создается резервная копия на другом сервере</li>
<li>Есть много доработок над <a href="/tag/memcached" target="_blank">memcached</a>, в.т.ч. для более стабильного и длительного размещения объектов в памяти; есть даже persistent версия</li>
<li>Фотографии не удаляются для минимизации фрагментации</li>
<li>Решения о развитии проекта принимают Павел Дуров и Андрей Рогозов, ответственность за сервисы &#8212; на них и на реализовавшем его разработчике</li>
<li>Павел Дуров откладывал деньги на хостинг с 1 курса <img src='http://www.insight-it.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </li>
</ul>
<h2>Подводим итоги</h2>
<p>В целом Вконтакте развивается в сторону увеличения скорости распространения информацию внутри сети. Приоритеты поменялись в этом направлении достаточно недавно, этим обусловлено, напимер, перенос выхода почтового сервиса Вконтакте, о котором очень активно говорили когда появилась возможность забивать себе текстовые URL вроде vkontakte.ru/ivan.blinkov. Сейчас этот подпроект имеет низкий приоритет и ждет своего часа, когда они смогут предложить что-то более удобное и быстрое, чем Gmail.</p>
<p>Завеса тайны насчет технической реализации Вконтакте была немного развеяна, но много моментов все же остались секретом. Возможно в будущем появится более детальная информация о собственной СУБД Вконтакте, которая как оказалось является ключом к решению всех самых сложных моментов в масштабируемости системы.</p>
<p>Как я уже упоминал этот пост написан почти на память, на основе небольшого конспекта &#171;круглого стола Вконтакте&#187;, так что хочется сразу извиниться за возможные неточности и недопонимания. Я лишь структурировал хаотичную кучу ответов на вопросы. Буду рад уточнениям и дополнениям.</p>
<p>Если хотите быть в курсе новых веяний в сфере масштабируемости высоконагруженных интернет-проектов &#8212; по традиции рекомендую <a href="/feed" target="_blank">подписаться на RSS</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/masshtabiruemost/arkhitektura-vkontakte/feed/</wfw:commentRss>
		<slash:comments>105</slash:comments>
		</item>
		<item>
		<title>Архитектура Facebook</title>
		<link>http://www.insight-it.ru/masshtabiruemost/arkhitektura-facebook/</link>
		<comments>http://www.insight-it.ru/masshtabiruemost/arkhitektura-facebook/#comments</comments>
		<pubDate>Wed, 20 Oct 2010 09:02:27 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Масштабируемость]]></category>
		<category><![CDATA[CDN]]></category>
		<category><![CDATA[Facebook]]></category>
		<category><![CDATA[featured]]></category>
		<category><![CDATA[HipHop]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Memcached]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[ODS]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Scribe]]></category>
		<category><![CDATA[Thrift]]></category>
		<category><![CDATA[Архитектура Facebook]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=579</guid>
		<description><![CDATA[На сегодняшний день Facebook является пожалуй самым обсуждаемым интернет-проектом во всем мире. Не смотря на довольно низкий уровень проникновения Facebook в России, темпы захвата аудитории этим проектом мягко говоря поражают. Как же им удается управляться с таким огромным социальным графом и удовлетворять потребности в общении невероятно большого количества людей по всему миру? Платформа Linux &#8212; [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.insight-it.ru/wp-content/uploads/2010/10/facebook_logo.jpg"><img class="alignleft size-full wp-image-584" style="border: 0!important; float: left; margin: 0 8px;" title="Facebook Logo" src="http://www.insight-it.ru/wp-content/uploads/2010/10/facebook_logo.jpg" alt="Facebook Logo" width="200" height="75" /></a>На сегодняшний день <a href="http://www.facebook.com" target="_blank">Facebook</a> является пожалуй самым обсуждаемым интернет-проектом во всем мире. Не смотря на довольно низкий уровень проникновения Facebook в России, темпы захвата аудитории этим проектом мягко говоря поражают. Как же им удается управляться с таким огромным социальным графом и удовлетворять потребности в общении невероятно большого количества людей по всему миру?</p>
<p><span id="more-579"></span></p>
<h2>Платформа</h2>
<ul>
<li><a href="/tag/linux" target="_blank">Linux</a> &#8212; операционная система</li>
<li><a href="/tag/php" target="_blank">PHP</a> с <a href="/tag/hiphop" target="_blank">HipHop</a> &#8212; код на PHP компилируется в C++</li>
<li><a href="/tag/memcached" target="_blank">memcached</a> &#8212; агрессивное кэширование объектов</li>
<li><a href="/tag/mysql" target="_blank">MySQL</a> &#8212; используется как хранилище пар ключ-значение, никаких join&#8217;ов</li>
<li><a href="/tag/thrift" target="_blank">Thrift</a> &#8212; интерфейс взаимодействия между сервисами, написанными на разных языках программирования</li>
<li><a href="/tag/scribe" target="_blank">Scribe</a> &#8212; универсальная система сбора и агрегации данных с рабочих серверов</li>
</ul>
<h2>Статистика</h2>
<ul>
<li>Более 500 миллионов активных пользователей (месячная аудитория)</li>
<li>Более миллиарда социальных связей</li>
<li>Более 200 миллиардов просмотров страниц в месяц</li>
<li>Более 4 триллионов действий попадает в новостные ленты каждый день</li>
<li>Более 150 миллионов обращений к кэшу в секунду; 2 триллиона объектов в кэше</li>
<li>Более 8 миллиардов минут провели пользователи на Facebook&#8217;е ежедневно</li>
<li>Более 3 миллиардов фотографий загружается каждый месяц, до 1.2 миллиона фотографий в секунду</li>
<li>20 миллиардов фотографий в 4 разрешениях = 80 миллиардов фотографий, их бы хватило чтобы покрыть поверхность земли в 10 слоев; это больше, чем на всех других фото-ресурсах в месте взятых</li>
<li>О более чем 5 миллиардах единиц контента рассказывается друзьям еженедельно</li>
<li>Более миллиарда сообщений в чате каждый день</li>
<li>Более ста миллионов поисковых запросов в день</li>
<li>Более 250 приложений и 80 тысяч сторонних ресурсов на платформе Facebook Connect</li>
<li>Более 400 тысяч разработчиков сторонних приложений</li>
<li>Менее 500 разработчиков и системных администраторов в штате</li>
<li>Более миллиона активных пользователей на одного инженера</li>
<li>Десятки тысяч серверов, десятки гигабит трафика</li>
</ul>
<h2>Архитектура</h2>
<h3>Общие принципы</h3>
<ul>
<li>Балансировщик нагрузки выбирает веб-сервер для обработки запроса</li>
<li>PHP-код в веб-сервере подготавливает HTML, пользуясь данными из различных источников:
<ul>
<li>MySQL</li>
<li>memcached</li>
<li>Специализированные сервисы</li>
</ul>
</li>
<li>Если взглянуть с другой стороны, то получим трехуровневую архитектуру:
<ul>
<li>Вер-приложение</li>
<li>Распределенный индекс</li>
<li>Постоянное хранилище</li>
</ul>
</li>
<li>Использование открытых технологий там, где это возможно</li>
<li>Поиск возможностей оптимизации используемых продуктов</li>
<li>Философия Unix:
<ul>
<li>Старайтесь делать каждый компонент системы простым и производительным</li>
<li>Комбинируйте компоненты для решения задач</li>
<li>Концентрируйте внимание на хорошо обозначенных точках взаимодействия</li>
</ul>
</li>
<li>Все усилия направлены на масштабируемость</li>
<li>Попытки минимизации количества точек отказа</li>
<li>Простота, простота, простота!</li>
</ul>
<h3>PHP</h3>
<p><strong>Почему PHP?</strong></p>
<ul>
<li>Во многом &#171;так исторически сложилось&#187;</li>
<li>Хорошо подходит для веб-разработки</li>
<li>Легок в изучении: небольшой набор выражений и языковых конструкций</li>
<li>Легок в написании: нестрогая типизация и универсальный &#171;массив&#187;</li>
<li>Легок в чтении: синтаксис похож на C++ и Java</li>
<li>Прост в дебаггинге: нет необходимости в перекомпиляции</li>
<li>Большой ассортимент библиотек, актуальных для веб-проектов</li>
<li>Подходит для процесса разработки с короткими итерациями</li>
<li>Активное сообщество разработчиков по всему миру</li>
<li>Динамическая типизация, интерпретируемый язык для скриптов</li>
</ul>
<p><strong>Как оказалось на самом деле?</strong></p>
<ul>
<li>Высокий расход оперативной памяти и вычислительных ресурсов</li>
<li>Сложно работать, когда объем исходного кода очень велик: слабая типизация и ограниченные возможности для статичного анализа и оптимизации кода</li>
<li>Не особо оптимизирован для использования в крупных проектах</li>
<li>Линейный рост издержек при подключении файлов с исходным кодом</li>
<li>Механизм разработки расширений не очень удобен</li>
</ul>
<p><strong>Доработки:</strong></p>
<ul>
<li>Оптимизация байт-кода</li>
<li>Улучшения в APC (ленивая загрузка, оптимизация блокировок, &#171;подогрев&#187; кэша)</li>
<li>Свои расширения (клиент memcache, формат сериализации, логи, статистика, мониторинг, механизм асинхронной обработки событий)</li>
<li><strong><a href="http://github.com/facebook/hiphop-php" target="_blank">HipHop</a></strong> &#8212; трансформатор исходных кодов:
<ul>
<li>Разработчики пишут на PHP, который конвертируется в оптимизированный C++</li>
<li>Статический анализ, определение типов данных, генерация кода, и.т.д.</li>
<li>Облегчает разработку расширений</li>
<li>Существенно сокращает расходы оперативной памяти и вычислительных ресурсов</li>
<li>У команды из трех программистов ушло полтора года на разработку, переписаны большая часть интерпретатора и многие расширения языка</li>
<li>Опубликован под opensource лицензией в начале года, нет необходимости проходить этот же путь с нуля</li>
</ul>
</li>
</ul>
<h3>MySQL</h3>
<p><strong>Как используется MySQL?</strong></p>
<ul>
<li>Используется как хранилище пар ключ-значение</li>
<li>Большое количество логических узлов распределено между физическими машинами</li>
<li>Балансировка нагрузке на уровне физических серверов</li>
<li>Репликация для распределения операций чтения <strong>не</strong> используется</li>
<li>Большинство запросов касаются самой свежей информации: оптимизация таблиц для доступа к новым данным, архивация старых записей</li>
<li>В целом быстро и надежно</li>
</ul>
<p><strong>Как оказалось на самом деле?</strong></p>
<ul>
<li>Логическая миграция данных <em>очень</em> сложна</li>
<li>Создавать большое количество логических баз данных и перераспределять их между физическими узлами, балансируя таким образом нагрузку, намного удобнее</li>
<li>Никаких join&#8217;ов на рабочих серверах баз данных</li>
<li>Намного проще наращивать вычислительные мощности на веб-серверах, чем на серверах баз данных</li>
<li>Схемы, основанные на структуре данных, делают программистов счастливыми и создают большую головную боль администраторам</li>
<li>Никогда не храните не-статичные данные в централизованное базе данных</li>
</ul>
<p><strong>Доработки:</strong></p>
<ul>
<li>Практически никаких модификаций исходного кода MySQL</li>
<li>Своя схема партиционирования с глобально-уникальными идентификаторами</li>
<li>Своя схема архивирования, основанная на частоте доступа к данным относительно каждого пользователя</li>
<li>Расширенный движок запросов для репликации между датацентрами и поддержания консистенции кеша</li>
<li>Библиотеки для доступа к данным на основе графа:
<ul>
<li>Объекты (вершины графа) с ограниченными типами данных (целое число, строка ограниченно длины, текст)</li>
<li>Реплицированные связи (ребра графа)</li>
<li>Аналоги распределенных внешних ключей (foreign keys)</li>
<li>Большинство данных распределено случайно</li>
</ul>
</li>
</ul>
<h3>Memcache</h3>
<p><strong>Как используется memcached?</strong></p>
<ul>
<li>Высокопроизводительная распределенная хэш-таблица</li>
<li>Содержит &#171;горячие&#187; данные из MySQL</li>
<li>Снижает нагрузку на уровень баз данных</li>
<li>Основная форма кэширования</li>
<li>Используется более 25TB памяти на нескольких тысячах серверов</li>
<li>Среднее время отклика менее 250 микро-секунд</li>
<li>Кэшируются сериализованные структуры данных PHP</li>
<li>Отсутствие автоматического механизма проверки консистенции данных между memcached и MySQL &#8212; приходится делать это на уровне программного кода</li>
<li>Множество multi-get запросов для получения данных на другом конце ребер графа</li>
<li>Ограниченная модель данных, неэффективен для маленьких объектов</li>
</ul>
<p><strong>Доработки:</strong></p>
<ul>
<li>Порт на 64-битную архитектуру</li>
<li>Более эффективная сериализация</li>
<li>Многопоточность</li>
<li>Улучшенный протокол</li>
<li>Компрессия</li>
<li>Проксирование запросов</li>
<li>Доступ к memcache через UDP:
<ul>
<li>уменьшает расход памяти благодаря отсутствию тысяч буферов TCP соединений</li>
<li>управление ходом исполнения приложение (оптимизация для multi-get)</li>
</ul>
</li>
<li>Статистика о работе потоков по запросу &#8212; уменьшает блокировки</li>
<li>Ряд изменений в ядре Linux для оптимизации работы memcache:
<ul>
<li>распределение управления сетевыми прерывания по всем ядрам</li>
<li>оппортунистический опрос сетевых интерфейсов</li>
</ul>
</li>
<li> После вышеперечисленных модификаций memcached способен выполнять до 250 тысяч операций в секунду, по сравнению со стандартными 30-40 тысячами без данных изменений</li>
</ul>
<h3>Thrift</h3>
<p><strong>Что это?</strong></p>
<ul>
<li>Легкий механизм построения приложений с использованием нескольких языков программирования</li>
<li>Высокая цель: предоставить механизм прозрачного взаимодействия между языками программирования.</li>
<li>Предоставляет язык описания интерфейсов, статический генератор кода</li>
<li>Поддерживаемые языки: <a href="/tag/c++" target="_blank">C++</a>, <a href="/tag/php" target="_blank">PHP</a>, <a href="/tag/python" target="_blank">Python</a>, <a href="/tag/java" target="_blank">Java</a>, <a href="/tag/ruby" target="_blank">Ruby</a>, <a href="/tag/erlang" target="_blank">Erlang</a>, <a href="/tag/perl" target="_blank">Perl</a>, <a href="/tag/haskell" target="_blank">Haskell</a> и многие другие</li>
<li>Транспорты: простой интерфейс для ввода-вывода (сокеты, файлы, буферы в памяти)</li>
<li>Протоколы: стандарты сериализации (бинарный, JSON)</li>
<li>Серверы: неблокирующие, асинхронные, как однопоточные, так и многопоточные</li>
</ul>
<p><strong>Почему именно Thrift?</strong></p>
<ul>
<li>Альтернативные технологии: SOAP, CORBA, COM, Pillar, Protocol Buffers &#8212; но у всех есть свои существенные недостатки, что вынудило Facebook создать свою технологию</li>
<li>Он быстрый, очень быстрый</li>
<li>Меньше рабочего времени тратится каждым разработчиком на сетевые интерфейсы и протоколы</li>
<li>Разделение труда: работа над высокопроизводительными серверами ведется отдельно от работы над приложениями</li>
<li>Общий инструментарий, знакомый всем разработчикам</li>
</ul>
<h3>Scribe</h3>
<p><strong>Что это?</strong></p>
<ul>
<li>Масштабированный распределенный механизм ведения логов</li>
<li>Перемещает данные с серверов в центральный репозиторий</li>
<li>Широкая сфера применения:
<ul>
<li>Логи поисковых запросов</li>
<li>Публикации в новостных лентах</li>
<li>Данные по A/B тестированиям</li>
</ul>
</li>
<li>Более надежен, чем традиционные системы логгирования, но недостаточно надежен для транзакций баз данных</li>
<li>Простая модель данных</li>
<li>Построен на основе Thrift</li>
</ul>
<h3>Хранение фотографий</h3>
<p><strong>Сначала сделали это просто:</strong></p>
<ul>
<li>Загрузка на сервер: приложение принимает изображение, создает миниатюры в нужных разрешениях, сохраняет в NFS</li>
<li>Загрузка с сервера: изображения отдаются из NFS через HTTP</li>
<li>NFS построена на коммерческих продуктах</li>
<li>Это было необходимо, чтобы сначала проверить, что продукт востребован пользователями и они правда будут активно загружать фотографии</li>
<li>На самом деле оказалось, что:
<ul>
<li> Файловые системы непригодны для работы с большим количеством небольших файлов</li>
<li>Метаданные не помещаются в оперативную память, что приводит к дополнительным обращениям к дисковой подсистеме</li>
<li>Ограничивающим фактором является ввод-вывод, а не плотность хранения</li>
</ul>
</li>
</ul>
<p><strong>Потом начали оптимизировать:</strong></p>
<ul>
<li>Кэширование более часто используемых миниатюр изображений в памяти на оригинальных серверах для масштабируемости, надежности и производительности</li>
<li>Распределение их по <a href="/tag/cdn">CDN</a> для уменьшения сетевых задержек</li>
<li>Возможно сделать еще лучше:
<ul>
<li>Хранение изображений в больших бинарных файлах (blob)</li>
<li>Сервис, отвечающий за фотографии имеет информацию о том, в каком файле и с каким отступом от начала расположена каждая фотография (по ее идентификатору)</li>
<li>Этот сервис в Facebook называется Haystack и он оказался в 10 раз эффективнее &#171;простого&#187; подхода и в 3 раза эффективнее &#171;оптимизированного&#187;</li>
</ul>
</li>
</ul>
<h3>Другие сервисы</h3>
<ul>
<li><strong>SMC</strong>: консоль управления сервисами &#8212; централизованная конфигурация, определение на какой физической машине работает логический сервис</li>
<li><strong>ODS</strong>:  инструмент для визуализации изменений любых статистических данных, имеющихся в системе; удобен для мониторинга и оповещений</li>
<li><strong>Gatekeeper:</strong> разделение развертывания и запуска, A/B тестирования, таргетированный запуск, постепенный запуск</li>
<li>И еще около 50 других сервисов&#8230;</li>
</ul>
<h2>Как это работает все вместе?</h2>
<h3>Новые альбомы друзей</h3>
<p style="text-align: center;"><a style="width: 100%; text-align: center;" href="http://www.insight-it.ru/wp-content/uploads/2010/10/fb.png"><img class="size-medium wp-image-583 aligncenter" style="border: 0pt none  ! important;" title="Facebook" src="http://www.insight-it.ru/wp-content/uploads/2010/10/fb-300x190.png" alt="Facebook Screenshot" width="300" height="190" /></a></p>
<ol>
<li>Получаем профиль по идентификатору пользователя (скорее всего из кэша, но потенциально возможно обращение к базе данных)</li>
<li>Получаем список друзей (опять же на основе идентификатора пользователя из кэша или из базы данных в случае промаха)</li>
<li>Параллельно запрашиваем идентификаторы последних 10 альбомов для каждого из друзей (multi-get, каждый промах мимо кэша индивидуально вытаскивается из MySQL)</li>
<li>Параллельно получаем данные о всех альбомах (на основе идентификаторов альбомов из предыдущего шага)</li>
<li>Все данные получены, выполняем логику отрисовки конкретной страницы на PHP</li>
<li>Отправляем HTML в браузер, пользователь счастлив <img src='http://www.insight-it.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </li>
</ol>
<h3>Новостная лента</h3>
<p style="text-align: center;"><a href="http://www.insight-it.ru/wp-content/uploads/2010/10/fb2.png"><img class="size-medium wp-image-588 aligncenter" style="border: 0pt none  ! important;" title="News Feed" src="http://www.insight-it.ru/wp-content/uploads/2010/10/fb2-300x199.png" alt="News Feed Screenshot" width="300" height="199" /></a></p>
<p style="text-align: center;"><a href="http://www.insight-it.ru/wp-content/uploads/2010/10/fb3.png"><img class="size-medium wp-image-589 aligncenter" style="border: 0pt none  ! important;" title="News Feed" src="http://www.insight-it.ru/wp-content/uploads/2010/10/fb3-300x180.png" alt="News Feed Scheme" width="300" height="180" /></a></p>
<h3>Поиск</h3>
<p style="text-align: center;"><a href="http://www.insight-it.ru/wp-content/uploads/2010/10/fb4.png"><img class="size-medium wp-image-590 aligncenter" style="border: 0pt none  ! important;" title="Search" src="http://www.insight-it.ru/wp-content/uploads/2010/10/fb4-300x199.png" alt="Search Screenshot" width="300" height="199" /></a></p>
<p style="text-align: center;"><a href="http://www.insight-it.ru/wp-content/uploads/2010/10/fb5.png"><img class="size-medium wp-image-591 aligncenter" style="border: 0pt none  ! important;" title="Search" src="http://www.insight-it.ru/wp-content/uploads/2010/10/fb5-300x184.png" alt="Search Scheme" width="300" height="184" /></a></p>
<h2>Подводим итоги</h2>
<p>LAMP не идеален</p>
<ul>
<li>PHP+MySQL+Memcache решает большинство задач, но не может решить совсем все:
<ul>
<li>PHP не может хранить состояния</li>
<li>PHP не самый производительный язык</li>
<li>Все данные находятся удаленно</li>
</ul>
</li>
<li>Facebook разрабатывает собственные внутренние сервисы, чтобы:
<ul>
<li>Располагать исполняемый код ближе к данным</li>
<li>Скомпилированное окружение более эффективно</li>
<li>Некоторая функциональность присутствует только в других языках программирования</li>
</ul>
</li>
<li>Философия сервисов:
<ul>
<li>Создание сервисов только при необходимости (минимизация издержек по развертке, поддержке и ведению отдельной кодовой базы; потенциальная дополнительная точка сбоя)</li>
<li>Создание общего набора инструментов для создания сервисов (Thrift, Scribe, ODS, средства мониторинга и уведомлений)</li>
<li><em>Использование правильных языка программирования, библиотек и инструментов для решения задачи</em></li>
</ul>
</li>
<li>Возвращение инноваций общественности &#8212; важный аспект разработки в Facebook:
<ul>
<li>Опубликованные свои проекты:
<ul>
<li>Thrift</li>
<li>Scribe</li>
<li>Tornado</li>
<li>Cassandra</li>
<li>Varnish</li>
<li>Hive</li>
<li>xhprof</li>
</ul>
</li>
<li>Доработки популярных решений:
<ul>
<li>PHP</li>
<li>MySQL</li>
<li>memcached</li>
</ul>
</li>
<li>Информация о взаимодействии Facebook с opensource-сообществом, этих и других проектах расположена на <a href="http://developers.facebook.com/opensource/">странице, посвященной opensource</a>.</li>
</ul>
</li>
<li>Ключевые моменты культуры разработки в Facebook:
<ul>
<li><strong>Двигайся быстро</strong> и не бойся ломать некоторые вещи</li>
<li><strong>Большое влияние</strong> маленьких команд</li>
<li><strong>Будь откровенным</strong> и инновационным</li>
</ul>
</li>
</ul>
<h2>Источники информации</h2>
<p>Данная статья не является переводом готовой статьи, в качестве источников информации послужили записи выступлений сотрудников Facebook на конференциях:</p>
<ul>
<li><a href="http://www.infoq.com/presentations/Facebook-Software-Stack" target="_blank">Facebook Architecture: Science and the Social Graph</a></li>
<li><a href="http://www.infoq.com/presentations/Facebook-Moving-Fast-at-Scale" target="_blank">Facebook: Moving Fast at Scale</a></li>
<li><a href="http://www.infoq.com/presentations/Scale-at-Facebook" target="_blank">Scale at Facebook</a></li>
</ul>
<p>Очень рекомендую посмотреть материалы в оригинале, так как естественно я осветил в статье далеко не все, да и неточности какие-либо неисключены. Помимо этого возможно многим будет интересно мероприятие <a href="http://styleru.timepad.ru/event/3571" target="_blank">&#171;Facebook: how we scaled to 500 000 000 users &#171;</a>, где Robert Johnson выступает 22 октября в Москве. Еще он числится в списке докладчиков <a href="http://www.highload.ru" target="_blank">Highload++</a> с аналогичным выступлением. Дополнительную информацию можно почерпнуть в <a href="http://facebook.com/eblog">блоге инженеров Facebook</a>.</p>
<p><strong>UPD:</strong> Обновил некоторые моменты после посещения вышеупомянутого выступления Роберта.</p>
<p>И по традиции напоминаю, что так как я пишу довольно редко &#8212; читать мой блог намного удобнее по <a href="/feed" target="_blank">RSS</a>. Спасибо за внимание <img src='http://www.insight-it.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/masshtabiruemost/arkhitektura-facebook/feed/</wfw:commentRss>
		<slash:comments>36</slash:comments>
		</item>
		<item>
		<title>memcached на пальцах</title>
		<link>http://www.insight-it.ru/programmirovanie/memcached-na-palcakh/</link>
		<comments>http://www.insight-it.ru/programmirovanie/memcached-na-palcakh/#comments</comments>
		<pubDate>Wed, 15 Jul 2009 12:09:06 +0000</pubDate>
		<dc:creator></dc:creator>
				<category><![CDATA[Программирование]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Memcached]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=117</guid>
		<description><![CDATA[Ранее уже была сделана публикация с обзором memcached. Давайте вернемся к данной теме и рассмотрим практику работы с memcached на примерах. К сожалению у меня по прежнему не доходят руки активно заниматься блогом, но наконец-то появился появился первый человек, откликнувшийся на мое предложение стать гостевым автором данного блога. Его имя Владислав Клименко и именно он [...]]]></description>
			<content:encoded><![CDATA[<p>Ранее уже была сделана публикация с обзором memcached. Давайте вернемся к данной теме и рассмотрим практику работы с memcached на примерах.<br />
<span id="more-117"></span></p>
<div style="color:#eeffee; background-color:#6aa800; padding:8pt 16pt;">
<p>К сожалению у меня по прежнему не доходят руки активно заниматься блогом, но наконец-то появился появился первый человек, откликнувшийся на <a style="color:#daffda !important" href="/guest-posts">мое предложение стать гостевым автором данного блога</a>. Его имя <em>Владислав Клименко</em> и именно он является автором данного поста, а я лишь выступаю в роли редактора. Может быть данный пример подтолкнет и других читателей поучаствовать в возвращении <strong>Insight IT</strong> к жизни.</p>
<p style="text-align:right">С уважением,<br />Иван Блинков</p>
</div>
<p>Итак, пара слов о предмете разговора. memcached &#8212; это распределенная система кэширования объектов в оперативной памяти. Разрабатывается фирмой <a href="http://danga.com/" rel="nofollow">Danga Interactive</a> (кстати, они являются авторами не только memcached, но и других интересных проектов). Но о них, возможно, в следующий раз. Обычно memcached используется приложениями для временного хранения данных, которые надо часто читать. Приложения не взаимодействуют (обычно) напрямую с сервером memcached, а работают при помощи клиентских библиотек. На настоящее время созданы библиотеки для многих языков программирования (а для некоторых еще и по нескольку альтернативных)  &#8212; полный список клиентских библиотек доступен на <a href="http://code.google.com/p/memcached/wiki/Clients" rel="nofollow">wiki проекта</a>. В целом, данная схема похожа на работу с БД, знакомую многим разработчикам.</p>
<p>Будем рассматривать установку и использование memcached для Linux. Так же при рассмотрении примеров на PHP и обзоре кэширования сессий потребуются PHP и Apache. Возможно, их придется установить, но мы не будем заострять внимание на вопросах установки.</p>
<h2>Сервер memcached</h2>
<p>Давайте приступим к установке memcached. Практически во всех дистрибутивах Linux memcached можно установить из репозитариев. Если есть желание собрать самую свежую версию, то можно заглянуть на <a href="http://danga.com/memcached/" rel="nofollow">сайт разработчика</a>  (на момент написания этих строк последняя версия &#8212; <a href="http://memcached.googlecode.com/files/memcached-1.4.0.tar.gz" rel="nofollow">1.4.0</a>).<br />
Также, возможно, понадобится установить libevent. Последняя стабильная версия &#8212; <a href="http://www.monkey.org/~provos/libevent-1.4.11-stable.tar.gz" rel="nofollow" rel="nofollow">1.4.11</a></p>
<p>Собираем, устанавливаем и запускаем memcached в режиме вывода сообщений. Интересно же посмотреть, что с ним происходит:</p>
<pre lang="bash">memcached -vv</pre>
<p>Процесс запускается и ждет подключений (по умолчанию на порту 11211). Серверная часть готова обрабатывать подключения клиентов и кэшировать полученные данные.</p>
<p>Но для разработчика приложений это только полпути. Необходимо поддержать работу с memcached в своем приложении. Для этого, рассмотрим некоторые существующие клиентские библиотеки memcached.</p>
<h2>Клиенты memcached</h2>
<p>Из всего многообразия клиентских библиотек рассмотрим две:</p>
<ul>
<li>libmemcached (для Си);</li>
<li>PECL extension для PHP (построенный на базе предыдущей библиотеки).</li>
</ul>
<h2>Си</h2>
<p>Библиотека libmemcached на данный момент активно развивается и представляется наиболее подходящим выбором при работе с Си и PHP. Также, в комплекте с самой клиентской библиотекой поставляются дополнительные утилиты для работы с memcached, позволяющие просматривать, устанавливать, удалять значения в кэше memcached. Кстати, удивляет, что набор утилит идет не с серверной частью, а с клиентской библиотекой.</p>
<p>Итак, приступим к установке libmemcached. На момент написания этих строк текущая версия libmemcached &#8212; <a href="http://download.tangent.org/libmemcached-0.31.tar.gz" rel="nofollow">0.31</a>. Компилируем, устанавливаем. Для начала, наслаждаемся чтением страниц man:</p>
<pre lang="bash">
man libmemcached
man libmemcached_examples
</pre>
<p>C библиотекой поставляются описание несложных примеров использования. За более интересными же способами применения имеет смысл заглянуть в исходные тексты утилит, благо все идет вместе.</p>
<p>Рекомендую обратить внимание на собранные утилиты. Наверняка многие из них станут верными помощниками при разработке приложений.</p>
<ul>
<li><b>memstat</b> &#8212; выдает информацию о сервере memcached</li>
<li><b>memcat</b> &#8212; выдает значение по ключу</li>
<li><b>memrm</b> &#8212; удаляет значение по ключу</li>
<li><b>memdump</b> &#8212; выдает список ключей</li>
</ul>
<p>Для начала посмотрим, что скажет сервер memcached, запущенный нами немного ранее в режиме выдачи сообщений. Запросим статистику сервера при помощи утилиты memstat:</p>
<pre lang="bash">
memstat --servers localhost

 Listing 1 Server
 Server: localhost (11211)
 pid: 14534
  uptime: 1950
 time: 1247390264
 version: 1.4.0
 pointer_size: 32
 rusage_user: 0.0
 rusage_system: 0.0
 curr_items: 0
 total_items: 0
 bytes: 0
 curr_connections: 10
 total_connections: 11
 connection_structures: 11
 cmd_get: 0
 cmd_set: 0
 get_hits: 0
 get_misses: 0
 evictions: 0
 bytes_read: 0
 bytes_written: 0
 limit_maxbytes: 67108864
 threads: 5
</pre>
<p>Получили статистику &#8212; следовательно memcached функционирует и откликается на запросы.</p>
<p>Итак, на настоящий момент готовы к использованию сервер memcached и клиентская библиотека. Осталось дело за малым &#8212; внедрить использование memcached в разрабатываемое приложение. Что касается приложения &#8212; все в руках разработчиков, а мы рассмотрим небольшой пример работы с базовыми функциями.</p>
<p>memcached предоставляет следующий набор основных функций (их, конечно, больше, но здесь приведены основные):</p>
<ul>
<li><b>set</b> - занести в кэш пару ключ-значение</li>
<li><b>add</b> - занести в кэш значение при условии, что значения с таким ключом в кэше еще нет</li>
<li><b>replace</b> &#8212; обновляет кэш при условии, что значение с таким ключом в кэше уже есть</li>
<li><b>get</b> &#8212; получает значение из кэша по указанному ключу</li>
<ul>
<h3>Пример программы на C</h3>
<pre><b>Файл mc.c</b></pre>
<pre lang="c">#include "stdio.h"
#include "string.h"
#include "memcached.h"

int main( void )
{
	char *key = "key";
	char *value = "value";
	uint32_t flags = 0;
	size_t length = 0;
	char *value2 = NULL;
	memcached_return rc;

	// 1. создать структуру для работы с кэшем
	memcached_st *memc = memcached_create(NULL);

	// 2. указать сервер с которым будем работать
	memcached_server_add(memc,"localhost",11211);

	// 3. занести пару ключ-значение в кэш
	rc = memcached_set(memc, key, strlen(key), value, strlen(value)+1, (time_t)0, flags);

	if (rc == MEMCACHED_SUCCESS) {
	} else {
		// обработать ошибку
	}

	// 4. получить значение
	value2 = memcached_get (memc, key, strlen(key),     &amp; length, &amp; flags, &amp; rc);
	if (rc == MEMCACHED_SUCCESS) {
		printf("%s\n", value2);
		free(value2);
	} else {
		// обработать ошибку
	}

	// 5. высвободить структуру
	memcached_free(memc);
	return 0;
}
</pre>
<p>Программа состоит из 5 основных операций и в особых комментариях не нуждается. Разве что можно отметить, что в пункте 2 можно добавлять много серверов, в случае использования распределенной системы.</p>
<p>Компилируем, возможно придется явно указать пути к библиотекам:</p>
<pre lang="bash">
gcc -Wall -o mc mc.c -I/usr/local/include/libmemcached/ -lmemcached
</pre>
<p>Запускаем:</p>
<pre lang="bash">
./mc
 value
</pre>
<p>Видим требуемое значение &#8212; должно быть, <em>заработало</em>!</p>
<p>Для уточнения деталей, смотрим сообщения на сервере memcached:</p>
<pre>
&lt;32 new auto-negotiating client connection
32: Client using the ascii protocol
32 STORED
32 sending key key
&gt;32 END
&lt;32 quit
&lt;32 connection closed.
</pre>
<p>В данном примере представлены следующие события: подключение клиента, установка пары ключ-значение, чтение данных по ключу и отключение клиента.</p>
<p>Посмотрим статистику на сервере:</p>
<pre lang="bash">
memstat --servers localhost
 Listing 1 Server
 Server: localhost (11211)
 pid: 14534
 uptime: 4659
 time: 1247392973
 version: 1.4.0
 pointer_size: 32
 rusage_user: 0.0
 rusage_system: 0.0
 curr_items: 1
 total_items: 1
 bytes: 58
 curr_connections: 10
 total_connections: 13
 connection_structures: 11
 cmd_get: 1
 cmd_set: 1
 get_hits: 1
 get_misses: 0
 evictions: 0
 bytes_read: 58
 bytes_written: 58
 limit_maxbytes: 67108864
 threads: 5
</pre>
<p>Следующие две строчки показывают, что в кэше появилось значение:</p>
<pre lang="bash">
curr_items: 1
total_items: 1
</pre>
<p>Посмотрим на данное значение:</p>
<pre lang="bash">
memcat --servers localhost key
 value
</pre>
<p>Итак, приложение, использующее memcached &#8212; готово.</p>
<h2>PHP</h2>
<p>Для начала установим PECL extension для PHP &#8212; memcached</p>
<pre lang="bash">pecl install memcached</pre>
<p>На этом этапе возможно появление сообщения об ошибке вида:</p>
<pre>ERROR: 'phpize' failed</pre>
<p>Это означает, что не установлен пакет php-dev или его аналог. Устанавливаем его и можно пробовать снова:</p>
<pre lang="bash">
pecl install memcached
 install ok: channel://pecl.php.net/memcached-1.0.0
 You should add "extension=memcached.so" to php.ini
</pre>
<p>Как нам и советуют, дописываем <b>extension=memcached.so</b> в php.ini и перезапускаем Apache.</p>
<p>Смотрим информацию об используемом PHP:</p>
<pre>
memcached support  enabled
Version  1.0.0
libmemcached version    0.31
Session support    yes
igbinary support   no
</pre>
<h3>Пример программы на PHP</h3>
<p>Можно смело использовать обращения к memcached из PHP. Как обычно, рассмотрим пример:</p>
<pre>
&lt;?php
$m = new Memcached();

$m-&gt;addServer('localhost', 11211);
$m-&gt;set('phpkey', 'phpvalue');
var_dump( $m-&gt;get('phpkey'));
?&gt;
</pre>
<p>Результат работы данного скрипта:</p>
<pre>
string(8)  "phpvalue"
</pre>
<p>Итак, PHP-приложение, использующее memcached &#8212; готово.</p>
<h2>Кэширование данных сессий</h2>
<p>Memcached можно использовать и как хранилище данных сессий для PHP. Такой подход часто используется в реальных приложениях. Давайте рассмотрим, что для этого надо сделать.</p>
<p>Вносим изменения в php.ini</p>
<pre>
;session.save_handler = files
session.save_handler = memcached

;session.save_path = /var/lib/php5
session.save_path = localhost:11211
</pre>
<p>Параметр session.save_handler указывает, что теперь данные будут храниться в memcached. Второй параметр &#8212; session.save_path указывает сервер memcached (их может быть указано несколько, через запятую) на котором будут сохранятся данные.</p>
<p>Перезапускаем Apache &#8212; и готово!</p>
<p>Теперь надо проверить, что теперь данные сессии реально хранятся не на диске, а в memcached.</p>
<p>Рассмотрим работу несложного скрипта, заносящего что-нибудь в сессию:</p>
<pre>
&lt;?php

session_start();
$_SESSION['intval'] = 123;
$_SESSION['strval'] = "qwe";
?&gt;
</pre>
<p>Запускаем скрипт, он заносит данные в сессию, после чего смотрим на кэш</p>
<pre lang="bash">
memdump --servers localhost
 key
 keyphp
 memc.sess.key.3ff8ccab14424082ff83a6dfbcf0941f
</pre>
<p>Итак &#8212; к нашим знакомым по предыдущим примерам ключам, добавился ключ с характерным именем <em>memc.sess.key.3ff8ccab14424082ff83a6dfbcf0941f</em>.</p>
<p>Хранение данных сессии перенесено в систему кэширования. Более подробную информацию по работе с memcached из PHP можно почитать <a href="http://ru2.php.net/manual/ru/book.memcached.php" rel="nofollow">на сайте PHP</a>.</p>
<h2>Заключение</h2>
<p>Мы рассмотрели утановку и примеры использования memcached. Следует особо подчеркнуть, что memcached &#8212; это не система хранения данных, поэтому на практике memcached почти всегда используется в паре с БД. Также следовало бы уделить внимание своевременной инвалидации данных в кэше и вопросам безопасности. В общем, тема интересная, и еще далека от закрытия.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/memcached-na-palcakh/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>Архитектура Digg</title>
		<link>http://www.insight-it.ru/masshtabiruemost/arkhitektura-digg/</link>
		<comments>http://www.insight-it.ru/masshtabiruemost/arkhitektura-digg/#comments</comments>
		<pubDate>Tue, 01 Apr 2008 17:49:21 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Масштабируемость]]></category>
		<category><![CDATA[APC]]></category>
		<category><![CDATA[Digg]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Lucene]]></category>
		<category><![CDATA[Memcached]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[online]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[архитектура]]></category>
		<category><![CDATA[архитектура Digg]]></category>
		<category><![CDATA[интернет]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/net/scalability/arkhitektura-digg/</guid>
		<description><![CDATA[Трафик, генерируемый более чем 1.2 миллионами пользователей Digg, знаменитых своей жаждой информации, способен загнать любой невинный сайт за рамки его вычислительных ресурсов и пропускной способности канала. Как же сам Digg справляется с такой нагрузкой? Источники информации Этот текст &#8212; перевод статьи, автор &#8212; Todd Hoff. Как Digg.com использует LAMP для масштабирования Масштабируемость и производительность PHP [...]]]></description>
			<content:encoded><![CDATA[<p>Трафик,  генерируемый более чем 1.2 миллионами пользователей <a href="http://www.digg.com" rel="nofollow" target="_blank">Digg</a>, знаменитых своей жаждой информации, способен загнать любой невинный сайт за рамки его вычислительных ресурсов и пропускной способности канала. Как же сам Digg справляется с такой нагрузкой?<br />
<span id="more-61"></span></p>
<h3>Источники информации</h3>
<p><em>Этот текст &#8212; перевод <a href="http://highscalability.com/digg-architecture" target="_blank" rel="nofollow">статьи</a>, автор &#8212; <a href="http://highscalability.com/user/todd-hoff" target="_blank" rel="nofollow">Todd Hoff</a>.</em></p>
<ul>
<li><a href="http://www.computerworld.com/action/article.do?command=viewArticleBasic&#038;articleId=9017778" target="_blank" rel="nofollow">Как Digg.com использует LAMP для масштабирования</a></li>
<li><a href="http://www.oreillynet.com/onlamp/blog/2006/04/digg_phps_scalability_and_perf.html" target="_blank" rel="nofollow">Масштабируемость и производительность PHP в Digg</a></li>
</ul>
<h3>Платформа</h3>
<ul>
<li><a href="/tag/mysql" target="_blank">MySQL</a></li>
<li><a href="/tag/linux" target="_blank">Linux</a></li>
<li><a href="/tag/php" target="_blank">PHP</a></li>
<li><a href="/tag/lucene" target="_blank">Lucene</a></li>
<li><a href="/tag/apc" target="_blank">APC PHP Accelerator</a></li>
<li><a href="/tag/memcached" target="_blank">Memcached</a></li>
</ul>
<h3>Статистика</h3>
<ul>
<li>Проект стартовал в конце 2004 года на одном сервере под управлением <a href="/tag/linux" target="_blank">Linux</a> с использованием <a href="/tag/apache" target="_blank">Apache 1.3</a>, <a href="/tag/php" target="_blank">PHP 4</a> и <a href="/tag/mysql" target="_blank">MySQL 4.0</a> (со стандартной системой хранения данных &#8212; MyISAM).</li>
<li>Более 1.2 миллиона пользователей.</li>
<li>Более 200 миллионов просмотров страниц в месяц.</li>
<li>100 серверов расположены в нескольких датацентрах, из них:
<dl>
<dd>&ndash; 20 серверов баз данных;</dd>
<dd>&ndash; 30 веб-серверов;</dd>
<dd>&ndash; несколько поисковых серверов, использующих Lucene;</dd>
<dd>&ndash; остальные используются для обеспечения избыточности.</dd>
</dl>
</li>
<li>30 GB данных.</li>
<li>Ни одна из проблем, с которыми пришлось столкнуться проекту не была связана с <a href="/tag/php" target="_blank">PHP</a>, в основном они касались базы данных.</li>
<li>Легковесная природа <a href="/tag/php" target="_blank">PHP</a> позволила переместить вычислительные работы из базы данных в приложение для улучшения производительности.</li>
</ul>
<h3>Что внутри?</h3>
<ul>
<li>Балансировщик нагрузки равномерно распределяет запросы между <a href="/tag/php" target="_blank">PHP</a> серверами.</li>
<li>MySQL используется по принципу master-slave:
<dl>
<dd>&nbsp; Сервера, обрабатывающие большое количество транзакций, используют движок InnoDB.</dd>
<dd>&nbsp; Сервера, выполняющие аналитическую обработку данных в реальном времени, используют MyISAM.</dd>
<dd>&nbsp; Снижения производительности при переходе с <a href="/tag/mysql" target="_blank">MySQL</a> 4.1 на версию 5 замечено не было.</dd>
</dl>
</li>
<li>Для кэширования используется <a href="/tag/memcached" target="_blank">Memcached</a>.</li>
<li>Используется сегментирование баз данных.</li>
<li>Особенности использования Digg существенно облегчают процесс масштабирования. Большинство посетителей просто просматривают главную страницу и уходят. Это приводит к тому, что 98% запросов к базе данных являются операциями чтения. Такое соотношение операций чтения и записи позволяет не беспокоиться о комплексной работе по проектированию операций записи, что позволяет намного проще масштабировать проект.</li>
<li>Возникали проблемы, связанные с системой хранения данных, которые сообщали, что данные уже записаны на диск, когда на самом деле это было не так. Контроллеры делали это для создания впечатления более высокой производительности. Но на практике это приводило лишь к проблемам с целостностью данных. Это достаточно распространенная проблема, которую порой не так уж просто решить, правда все зависит от используемого оборудования.</li>
<li>Для облегчения нагрузки на базы данных используется кэшрование и <a href="/tag/apc" target="_blank">APC PHP Accelerator</a>.</li>
<li>С использованием рабочих потоков <a href="/tag/apache" target="_blank">Apache2</a>, FastCGI и <a href="/tag/php" target="_blank">PHP</a> акселератора возможно избежать необходимости каждый раз заново интерпретировать и компилировать PHP скрипты: скрипт компилируется только при первом обращении, что существенно ускоряет скорость его выполнения при последующих обращениях.</li>
</ul>
<h3>Подводим итоги</h3>
<ul>
<li>Используйте возможность выбора движка для <a href="/tag/mysql" target="_blank">MySQL</a>. Если Вам нужны транзакции &#8212; используйте InnoDB, если нет &#8212; MyISAM. Например, если на master сервере расположены транзакционные таблицы, то для slave серверов можно использовать и MyISAM.</li>
<li>В определенный момент рост стал невозможен путем добавления дополнительной оперативной памяти, пришлось продолжать рост путем изменения архитектуры.</li>
<li>Люди часто жалуются, что Digg медлителен. Скорее это вызвано их огромными <a href="/tag/javascript" target="_blank">JavaScript</a> библиотеками, чем работой их серверной системы.</li>
<li>Стоит тщательно выбирать какие именно приложения развертывать. Они приложили все усилия, чтобы не использовать приложения, требующие больших вычислительных мощностей. Очевидно, что Digg работает на совершенно стандартной <a href="/tag/lamp" target="_blank">LAMP</a> архитектуре, но тем не менее реализована она достаточно интересно. У инженеров часто возникает желание реализовать какой-либо дополнительный функционал, но всегда стоит иметь ввиду, что они могут разрушить инфраструктуру, если она не сможет расти теми же темпами. Так что с этим стоит повременить до тех пор пока система сможет выдерживать все необходимые нагрузки. Это приводит к планированию ресурсов, особенно большое внимание этому аспекту уделяет <a href="/tag/flickr" target="_blank">Flickr</a>.</li>
<li>Вам остается лишь догадываться, сможет ли <a href="/tag/digg" target="_blank">Digg</a> удержать свои позиции, если и дальше будет ограничивать добавление новых возможностей, или уступит более активно развивающимся сервисам социальных закладок? Возможно если бы была возможность увеличивать масштабы более простыми методами, более быстрое добавление новых функций и возможностей позволило бы более эффективно конкурировать на этом рынке? С другой стороны, просто добавление новых возможностей может и не поменять ситуацию кардинальным образом.</li>
<li>Основные проблемы с масштабируемостью и производительностью связаны с обработкой данных и в большинстве случаев они не зависят от используемого языка программирования. Вы столкнетесь с ними при работе с <a href="/tag/java" target="_blank">Java</a>, <a href="/tag/php" target="_blank">PHP</a>, <a href="/tag/ruby" target="_blank">Ruby</a>, или подставьте сюда Ваш любимый язык программирования.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/masshtabiruemost/arkhitektura-digg/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Архитектура Wikimedia</title>
		<link>http://www.insight-it.ru/masshtabiruemost/arkhitektura-wikimedia/</link>
		<comments>http://www.insight-it.ru/masshtabiruemost/arkhitektura-wikimedia/#comments</comments>
		<pubDate>Fri, 28 Mar 2008 12:32:49 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Масштабируемость]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[lighttpd]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Lucene]]></category>
		<category><![CDATA[LVS]]></category>
		<category><![CDATA[Memcached]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Squid]]></category>
		<category><![CDATA[архитектура]]></category>
		<category><![CDATA[архитектура Wikimedia]]></category>
		<category><![CDATA[геораспределение]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/net/scalability/arkhitektura-wikimedia/</guid>
		<description><![CDATA[Wikimedia является платформой для Wikipedia, Wiktionary и еще семи менее крупных wiki-проектов. Этот документ очень пригодится новичкам, пытающимся довести свои проекты до масштабов гигантских вебсайтов. Здесь можно найти множество интересных деталей и инновационных идей, которые уже успели доказать свою работоспособность на самых посещаемых сайтах всего Интернета. Источники информации Перевод статьи. Автор &#8212; Todd Hoff. Архитектура [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://wikimedia.org" target="_blank" rel="nofollow">Wikimedia</a> является платформой для <a href="http://wikipedia.org" target="_blank" rel="nofollow">Wikipedia</a>, <a href="http://wiktionary.org" target="_blank" rel="nofollow">Wiktionary</a> и еще семи менее крупных wiki-проектов. Этот документ очень пригодится новичкам, пытающимся довести свои проекты до масштабов гигантских вебсайтов. Здесь можно найти множество интересных деталей и инновационных идей, которые уже успели доказать свою работоспособность на самых посещаемых сайтах всего Интернета.<br />
<span id="more-59"></span></p>
<h3>Источники информации</h3>
<p><em>Перевод <a href="http://highscalability.com/wikimedia-architecture" target="_blank" rel="nofollow">статьи</a>. Автор &#8212; <a href="http://highscalability.com/user/todd-hoff" target="_blank" rel="nofollow">Todd Hoff</a>.</em></p>
<ul>
<li><a href="http://www.nedworks.org/~mark/presentations/san/Wikimedia%20architecture.pdf" target="_blank" rel="nofollow">Архитектура Wikimedia</a></li>
<li><a href="http://meta.wikimedia.org/wiki/Wikimedia_servers" target="_blank" rel="nofollow">Серверы Wikimedia</a></li>
<li><a href="http://oracle2mysql.wordpress.com/2007/08/22/12/" target="_blank" rel="nofollow">scale-out vs scale-up</a> из блога &#171;Oracle to MySQL&#187;</li>
</ul>
<h3>Платформа</h3>
<ul>
<li><a href="/tag/apache" target="_blank">Apache</a></li>
<li><a href="/tag/linux" target="_blank">Linux</a></li>
<li><a href="/tag/mysql" target="_blank">MySQL</a></li>
<li><a href="/tag/php" target="_blank">PHP</a></li>
<li><a href="/tag/squid" target="_blank">Squid</a></li>
<li><a href="/tag/LVS" target="_blank">LVS</a></li>
<li><a href="/tag/lucene" target="_blank">Lucene</a> для поиска</li>
<li><a href="/tag/memcached" target="_blank">Memcached</a> для распределенного кэширования объектов</li>
<li><a href="/tag/lighttpd" target="_blank">lighttpd</a> для работы с изображениями</li>
</ul>
<h3>Статитстика</h3>
<ul>
<li>8 миллионов статей распределены по сотням языковых подпроектов (английские, голландские, &#8230;)</li>
<li>В десятке самых высоконагруженных проектов по данным <a href="http://alexa.com" target="_blank" rel="nofollow">Alexa</a></li>
<li>Экспоненциальный рост: в терминах посетителей, трафика и серверов удвоение происходит каждые 4-6 месяцев</li>
<li>30000 HTTP запросов в секунду в периоды пиковой нагрузки</li>
<li>3 GBps трафик данных</li>
<li>3 датацентра: Тампа, Амстердам, Сеул</li>
<li>350 серверов, конфигурации варьируются от однопроцессорных Pentium 4 с 512 MB оперативной памяти до двухпроцессорных Xeon Quad-Core с 16 GB RAM.</li>
<li>Управляется ~6 людьми</li>
<li>Три кластера на трех разных континентах</li>
</ul>
<h3>Архитектура</h3>
<ul>
<li>Географическая балансировка нагрузки, основываясь на IP клиента, перенаправляет их на ближайший кластер. Происходит статическое отображение множества IP адресов на множество стран, а затем и на множество кластеров.</li>
<li>Кэширование с помощью <a href="/tag/squid" target="_blank">Squid</a> группируется по типу контента: текст для wiki отдельно от изображений и больших статических файлов.</li>
<li>На данный момент функционирует 55 <a href="/tag/squid" target="_blank">Squid</a> серверов, плюс еще 20 подготавливается к запуску.</li>
<li>1000 HTTP запросов в секунду на каждый сервер, в периоды повышенной нагрузки эта цифра может достигать 2500.</li>
<li>~ 100-250 MBps на сервер.</li>
<li>~ 14000-32000 открытых соединений на каждом сервере.</li>
<li>До 40 GB дискового кэша на каждом Squid сервере.</li>
<li>До 4 дисков в каждом сервере (1U серверы).</li>
<li>8 GB оперативной памяти, половину использует <a href="/tag/squid" target="_blank">Squid</a>.</li>
<li><a href="http://www.powerdns.com" target="_blank" rel="nofollow">PowerDNS</a> предоставляет геораспределение.</li>
<li>В основном и региональных датацентрах текстовые и медиа кластеры построены на <a href="/tag/lvs" target="_blank">LVS</a>, <abbr title="Common Address Reduncancy Protocol">CARP</abbr> <a href="/tag/squid" target="_blank">Squid</a>, кэш <a href="/tag/squid" target="_blank">Squid</a>. В основном датацентре также находится хранилище медиа-данных.</li>
<li>Для того, чтобы обеспечить предоставление только последних версий страниц, всем Squid-серверам отправляются инвалидационные запросы.</li>
<li>Централизованно управляемая и синхронизированная установка программного обеспечения для сотен серверов.</li>
<li>MediaWiki отлично масштабируется с несколькими процессорами, так что закупаются двухпроцессорный четырех ядерные серверы (8 ядер на сервер).</li>
<li>Одно и то же оборудование выполняет как задачи внешнего хранения данных, так и кэширования <a href="/tag/memcached" target="_blank">Memcached</a>.</li>
<li><a href="/tag/memcached" target="_blank">Memcached</a> используется для кэширования метаданных изображений, данных парсера, различий между ревизиями, пользователей, сессий. Метаданные, такие как история ревизий, отношений статей (ссылки, категории и так далее), учетные записи пользователей хранятся в основных базах данных</li>
<li>Сам текст находится во внешних хранилищах данных.</li>
<li>Статические (загруженные пользователями) файлы, например изображения, хранятся отдельно на сервере изображений, а метаданные (размер, тип и так далее) кэшируются в основной базе данных и объектном кэше.</li>
<li>Отдельная база данных для каждой вики (не отдельный сервер!).</li>
<li>Один master и много реплицированных slave серверов.</li>
<li>Операции чтения равномерно распределяются по slave серверам, операции записи направляются на master.</li>
<li>Иногда master используется и для операция чтения, когда репликация на slave еще не завершена.</li>
<li>Внешнее хранение данных:
<dl>
<dd>&ndash; Текст статей хранится на отдельных кластерах, которые представляют собой простой средство хранения данных с возможностью только дописывания новых данных. Такой подход позволяет сохранить дорогостоящее место в высоконагруженных основных базах данных от редкоиспользуемой информации.</dd>
<dd>&ndash; Благодаря этому появляются дополнительные неиспользованные ресурсы на серверах приложений (порой 250-500 GB на сервер).</dd>
<dd>&ndash; На данной момент используются реплицируемые кластеры из 3 <a href="/tag/mysql" target="_blank">MySQL</a> серверов, но в будущем это может измениться, так как требуется более удобное управление ими.</dd>
</dl>
</li>
</ul>
<h3>Подводим итоги</h3>
<ul>
<li>Сфокусируйтесь на архитектуре, а не на операциях или чем-то другом.</li>
<li>Иногда кэширование обходится дороже, чем повторные вычисление или поиск данных в исходном источнике.</li>
<li>Старайтесь избегать сложных алгоритмов, запросов к базе данных и тому подобного.</li>
<li>Кэшируйте каждый результат, который дорого вам обошелся и является относительно локальным.</li>
<li>Сфокусируйтесь на &#171;горячих точках&#187; в коде.</li>
<li>Масштабируйтесь разделением:
<dl>
<dd>&ndash; операций чтения и записи (master/slave);</dd>
<dd>&ndash; сложных операций и более частых и простых (группы запросов);</dd>
<dd>&ndash; больших, популярных вики и более мелких.</dd>
</dl>
</li>
<li>Улучшайте кэширование: временная и пространственная локализация данных, а также уменьшение набора данных на каждом сервере.</li>
<li>Выполняйте компрессию текстовых данных, храните только изменения в статьях.</li>
<li>Казалось бы простые вызовы библиотечных функций порой на практике могут занимать слишком много времени.</li>
<li>Скорость поиска данных на диске ограничена, так что чем больше дисков &#8212; тем лучше!</li>
<li>Масштабирование с использованием обычного оборудование не означает использование самых дешевых вещей, которые удастся найти. Серверы баз данных Wikipedia сегодня представляют собой 16GB RAM, двух- или четырех-ядерные серверы с 6 15000 rpm SCSI дисками, организованными в RAID 0. Возможно они бы и использовали более дешевые системы, но 16 GB как раз хватает для размещения основного объема данных, а остальное берут на себя жесткие диски, это вполне соответствует потребностям системы, которую они построили. Примерно по таким же причинам их веб-сервера имеют 8 ядер, так как это позволяет достичь неплохой производительности <a href="/tag/php" target="_blank">PHP</a> при достаточно простой организации балансировки нагрузки.</li>
<li>Для масштабирования требуется выполнение массы работы, но если заранее этого не предусмотреть &#8212; понадобится сделать еще больше. MediaWiki изначально была написана для одного master сервера баз данных. Затем добавилась поддержка slave. Затем добавилось распределение по языкам и проектам. Дизайн системы с тех пор прекрасно выдерживает все нагрузки, но без очистки от новых узких мест системы не обошлось.</li>
<li>Каждый, кто хочет разработать свою базу данных таким образом, чтобы она позволила недорого масштабироваться с уровня одного сервера до уровня десятки лучших сайтов Интернета, должен начать с обработки слегка устаревших данных на реплицированных slave серверах, при этом не забывать балансировать нагрузку операций чтения между slave серверами. Если это возможно &#8212; блоки данных (группы пользователей, учетных записей, или чего угодно) должны размещаться каждый на разных серверах. Можно делать это с самого начала используя виртуализацию, чтобы удостовериться в работоспособности архитектуры, когда вы еще &#171;маленькие&#187;. Это <strong>намного</strong> проще, чем когда вы делаете то же самое, но под ежемесячно удваивающейся нагрузкой.</li>
</ul>
<p>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/masshtabiruemost/arkhitektura-wikimedia/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Модификация алгоритма хэширования</title>
		<link>http://www.insight-it.ru/programmirovanie/php/modifikaciya-algoritma-khehshirovaniya/</link>
		<comments>http://www.insight-it.ru/programmirovanie/php/modifikaciya-algoritma-khehshirovaniya/#comments</comments>
		<pubDate>Fri, 15 Feb 2008 10:17:20 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[hash]]></category>
		<category><![CDATA[md5]]></category>
		<category><![CDATA[sha1]]></category>
		<category><![CDATA[код]]></category>
		<category><![CDATA[кодинг]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[технология]]></category>
		<category><![CDATA[хранение данных]]></category>
		<category><![CDATA[хэш]]></category>
		<category><![CDATA[хэширование]]></category>
		<category><![CDATA[шифрование]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/programming/php/modifikaciya-algoritma-khehshirovaniya/</guid>
		<description><![CDATA[Если Вы уже успели прочитать одну из моих предыдущих записей о хэшировании, то Вы уже имеете базовое представление о теме сегодняшнего разговора. Одним из возможных способов применения хэшей является хранение аутентификационных данных пользователей интернет-приложения, об особенностях реализации формирования и проверки хэшей при регистрации и авторизации пользователей средствами PHP я и хотел бы с Вами поговорить. [...]]]></description>
			<content:encoded><![CDATA[<p>Если Вы уже успели прочитать <a href="/science/kriptografiya/obratnogo-puti-net/" target="_blank">одну из моих предыдущих записей о хэшировании</a>, то Вы уже имеете базовое представление о теме сегодняшнего разговора.<br />
Одним из возможных способов применения хэшей является хранение аутентификационных данных пользователей интернет-приложения, об особенностях реализации формирования и проверки хэшей при регистрации и авторизации пользователей средствами <a href="/tag/php" target="_blank">PHP</a> я и хотел бы с Вами поговорить.<br />
<span id="more-42"></span><br />
Сомневаюсь, что Вы услышите что-то новое, если я скажу, что в <a href="/tag/php" target="_blank">PHP</a> даже в &#171;стандартной комплектации&#187; реализована масса алгоритмов хэширования, начиная с широкораспространенных <strong>md5();</strong> и <strong>sha1();</strong> и заканчивая модулями <strong>hash</strong> и <strong>mhash</strong>, в которых реализована еще целая масса алгоритмов. Все они давно уже стандартизованы и доступны для изучения всем желающим получить о них какую-либо информацию.</p>
<p>Допустим мы храним пароли пользователей в виде какого-то стандартного хэша, для примера &#8212; <strong>md5</strong>, в базе данных. Все было отлично, но в один прекрасный момент нашелся подлый злоумышленник, который неким хитрым способом получил доступ к базе данных логинов и паролей. Перед ним стоит цель &#8212; узнать изначальный пароль у максимального числа пользователей. Посмотрим на ситуацию с его стороны:</p>
<ul>
<li>Первым делом он бы попытался определить, какой именно хэш перед ним находится &#8212; чаще всего это делается либо просто взглянув на длину хэша, либо если приложение широко распространено (популярная CMS скажем) &#8212; покопавшись в ее исходниках, еще есть вариант найти свой собственный аккаунт &#8212; и зная пароль попробовать на нем разные алгоритмы, способов можно придумать множество &#8212; все ограничивается лишь воображением. Узнав ответ на свой вопрос ему лишь останется набрать в <a href="/tag/google" target="_blank">Google</a> фразу вроде <em>&#171;md5 decrypt&#187;</em>, а дальше уже дело техники.</li>
<li>Еще один вариант решения задачи &#8212; взглянуть на список хэшей на предмет наличия совпадений. С очень высокой степенью вероятности за значительной группой одинаковых хэшей будет скрываться какой-либо банальный пароль вроде <em>123456</em>.</li>
</ul>
<p>Задача же разработчика приложения максимально обезопасить систему от подобных ситуаций. Конечно же можно просто стараться минимизировать возможности реализации методов получения информации из базы данных, но предугадать все варианты невозможно: в любом из используемых компонентов системы может оказаться уязвимость в коде, на которую наверняка найдется умник, который напишет <em>exploit</em>, а значит полностью исключить такую вероятность не получится, в лучшем случае выйдет просто ее минимизировать.</p>
<p>Именно по этим причинам и стоит задуматься об усложнении задачи злоумышленника в случае возникновения описанной выше ситуации. Для исключения возможности просто расшифровывания хэшей по словарю (то есть первый случай, когда определяется тип хэша и соответствующий ему словарь <em>хэш =&gt; исходное значение</em>) достаточно исключить возможность идентификации алгоритма хэширования или наличия к нему заранее подготовленного словаря. Для этого достаточно лишь сделать шаг в сторону от стандартного алгоритма любым пришедшим в голову способом, например:</p>
<ul>
<li>хранить хэш не от самого пароля, а от <em>пароль + какая-либо фиксированная строка</em></li>
<li>поменять местами группы символов в получившемся стандартном хэше</li>
<li>сделать сдвиг символов в стандартном хэше (или можно даже не сами символы двигать, а с помощью битовых операций их значения)</li>
<li>комбинировать два стандартных алгоритма хэширования, или алгоритм хэширования с алгоритмом обратимого шифрования, которых доступно также множество</li>
</ul>
<p>Список этот можно было бы продолжать достаточно долго, это было лишь первое, что пришло мне в голову. Но ни один из приведенных способов не избавит от возможности второго варианта раскрывания исходного пароля. Основывается он на однозначности стандартных алгоритмов &#8212; одним и тем же исходным данным соответствует один и тот же хэш. Для отказа от этого свойства стандартных алгоритмов придется выполнить более сложную модификацию используемой для генерации хэша функции (которая конечно же тоже поможет и для борьбы с первым вариантом). Сразу приведу пример <a href="/tag/kod" target="_blank">кода</a>, реализующего этот механизм, а дальше попытаюсь его объяснить:</p>
<pre lang="PHP">
function generateHash($input,$salt = false)
{
  if(!$salt)$salt=randomString(2);
  $hash=md5($input.$salt);
  return $salt.substr($hash,2);
}</pre>
<p>Как не трудно заметить &#8212; используется самодельная функция <strong>randomString();</strong>, которая возвращает случайную строку, состоящую из указанного количества шестнадцатеричных цифр (надеюсь Вы в состоянии написать ее своими силами). Именно этот момент и гарантирует элемент случайности при каждой новой генерации хэша. В том месте, где я прочитал про этот механизм (ссылку, к сожалению, привести не могу &#8212; в bookmark&#8217;ах не нашел), этот случайный компонент назывался словом <strong>salt</strong>, смысл его заключается в том, что он приписывается ко входным данным, передаваемым стандартной функции хэширования, а затем им же подменяется какая-либо фиксированная часть полученного хэша.<br />
Наверняка у Вас возник вопрос: а как же потом понять, что пользователь ввел верные данные, ведь для тех же исходных данных получится другой хэш и возможности их сравнить не будет? Ответ достаточно прост, его можно было увидеть даже в коде: при повторной инициализации хэша из <a href="/tag/bd" target="_blank">базы данных</a> достается заранее известная часть хранящегося там хэша, соответствующего конкретному пользователю &#8212; тот самый <strong>salt</strong>, и передается нашей функции. В этом случае в механизме будет использоваться именно он, а не новое случайное значение, и, как следствие, в случае правильности введенных данных на выходе получатся совпадающие хэши. Вот такой вот простенький, но иногда достаточно полезный трюк.</p>
<p>Если Вам понравился этот пост &#8212; возможно Вам придутся по душе и <a href="/dzhentelmenskij-nabor-php-programmista/" target="_blank">остальные записи из этой серии статей</a>, а не пропустить публикацию новых записей Вам может помочь <a href="/feed" target="_blank">RSS feed</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/php/modifikaciya-algoritma-khehshirovaniya/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Архитектура Flickr</title>
		<link>http://www.insight-it.ru/masshtabiruemost/arkhitektura-flickr/</link>
		<comments>http://www.insight-it.ru/masshtabiruemost/arkhitektura-flickr/#comments</comments>
		<pubDate>Fri, 08 Feb 2008 19:41:28 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Масштабируемость]]></category>
		<category><![CDATA[Apache]]></category>
		<category><![CDATA[Cvsup]]></category>
		<category><![CDATA[flickr]]></category>
		<category><![CDATA[Ganglia]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Memcached]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[online]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[RedHat]]></category>
		<category><![CDATA[shard]]></category>
		<category><![CDATA[Smarty]]></category>
		<category><![CDATA[Squid]]></category>
		<category><![CDATA[Subcon]]></category>
		<category><![CDATA[SystemImager]]></category>
		<category><![CDATA[архитектура]]></category>
		<category><![CDATA[архитектура Flickr]]></category>
		<category><![CDATA[интернет]]></category>
		<category><![CDATA[кластер]]></category>
		<category><![CDATA[сервер]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/net/scalability/arkhitektura-flickr/</guid>
		<description><![CDATA[Flickr является мировым лидером среди сайтов размещения фотографий. Перед Flickr стоит впечатляющая задача, они должны контролировать обширное море ежесекундно обновляющегося контента, непрерывно пополняющиеся легионы пользователей, постоянный поток новых предоставляемых пользователям возможностей, а делается все это при постоянной поддержке отличной производительности. Как же они это делают? Источники информации Как и предыдущий пост &#171;Архитектура Google&#187;, этот тоже [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.flickr.com" rel="nofollow" target="_blank">Flickr</a> является мировым лидером среди сайтов размещения фотографий. Перед Flickr стоит впечатляющая задача, они должны контролировать обширное море ежесекундно обновляющегося контента, непрерывно пополняющиеся легионы пользователей, постоянный поток новых предоставляемых пользователям возможностей, а делается все это при постоянной поддержке  отличной производительности. Как же они это делают?<br />
<span id="more-37"></span></p>
<h3>Источники информации</h3>
<p><em>Как и предыдущий пост <a href="/net/scalability/arkhitektura-google" target="_blank">&#171;Архитектура Google&#187;</a>, этот тоже является переводом <a href="http://highscalability.com/flickr-architecture" target="_blank" rel="nofollow">статьи</a> от <a href="http://highscalability.com/user/todd-hoff" target="_blank" rel="nofollow">Todd&#8217;а Hoff&#8217;а</a>. Возможно читателям <a href="/tag/google" target="_blank">Google</a> был более интересен, но подход Flickr к масштабируемости тоже более чем заслуживает внимания. Далее привожу источники информации из оригинальной статьи:</em></p>
<ul>
<li><a href="http://www.niallkennedy.com/blog/uploads/flickr_php.pdf" rel="nofollow" target="_blank">Flickr и PHP</a> (ранний документ)</li>
<li>Планирование нагрузок на LAMP</li>
<li><a href="http://www.bytebot.net/blog/archives/2007/04/25/federation-at-flickr-a-tour-of-the-flickr-architecture" rel="nofollow" target="_blank">Федерация Flickr: Тур по архитектуре Flickr</a></li>
<li><a href="http://highscalability.com/book-building-scalable-web-sites" rel="nofollow" target="_blank">Построение масштабируемых веб-сайтов</a> от Call Handerson&#8217;а из Flickr</li>
<li>История войн баз данных #3: Tim O&#8217;Reilly о Flickr</li>
<li><a href="http://www.iamcal.com/talks/" rel="nofollow" target="_blank">Cal Henderson&#8217;s Talks</a> &#8212; много полезных презентаций</li>
</ul>
<h3>Платформа</h3>
<ul>
<li><a href="/tag/php" target="_blank">PHP</a></li>
<li><a href="/tag/sql" target="_blank">MySQL</a></li>
<li>Сегментирование <em>(прим.: разбиение системы на части, обслуживающие каждая свою группу пользователей; называть можно было по-разному, но давайте остановимся на этом варианте перевода слова &#171;Shards&#187;)</em></li>
<li><a href="/tag/memcached" target="_blank">Memcached</a> для кэширования</li>
<li><a href="/tag/squid" target="_blank">Squid</a> в качестве обратной-прокси для html и изображений</li>
<li><a href="/linux" target="_blank">Linux</a> (<a href="/tag/redhat" target="_blank">RedHat</a>)</li>
<li><a href="/tag/smarty" target="_blank">Smarty</a> в роли шаблонизатора</li>
<li><a href="/tag/perl" target="_blank">Perl</a></li>
<li>PEAR для парсинга e-mail и XML</li>
<li>ImageMagick для обработки изображений</li>
<li><a href="/tag/java" target="_blank">Java</a> для узлового сервиса</li>
<li><a href="/tag/apache">Apache</a></li>
<li><a href="/tag/systemimager" target="_blank">SystemImager</a> для развертывания систем</li>
<li><a href="/tag/ganglia" target="_blank">Ganglia</a> для мониторинга распределенных систем</li>
<li><a href="/tag/subcon" target="_blank">Subcon</a> хранит важные системные конфигурационные файлы в SVN-репозитории для легкого развертывания на машины в кластере.</li>
<li><a href="/tag/cvsup" target="_blank">Cvsup</a> для распространения и обновления коллекций файлов по сети</li>
</ul>
<h3>Статистика</h3>
<ul>
<li>Более четырех миллиардов запросов в день</li>
<li>Примерно 35 миллионов фотографий в кэше <a href="/tag/squid" target="_blank">Squid</a></li>
<li>Около двух миллионов фотографий в оперативной памяти <a href="/tag/squid" target="_blank">Squid</a></li>
<li>Всего приблизительно 470 миллионов изображений, каждое представлено в 4 или 5 размерах</li>
<li>38 тысяч запросов к <a href="/tag/memcached" target="_blank">memcached</a> (12 миллионов объектов)</li>
<li>2 петабайта дискового пространства</li>
<li>Более 400000 фотографий добавляются ежедневно</li>
</ul>
<h3>Архитектура</h3>
<p>Симпатичное изображение архитектуры Flickr можно увидеть на <a href="http://www.slideshare.net/techdude/scalable-web-architectures-common-patterns-and-approaches/138" target="_blank" rel="nofollow">этом слайде</a>. Краткое ее описание выглядит следующим образом:<br />
–– Два ServerIron<br />
–––– <a href="/tag/squid" target="_blank">Squid</a> кэши<br />
–––––– Системы хранения NetApp<br />
–––– Серверы <a href="/tag/php" target="_blank">PHP</a>-приложений<br />
–––––– Менеджер хранения данных<br />
–––––– Master-master сегменты<br />
–––––– Центральная база данных, структурированная по принципу Dual Tree<br />
–––––– <a href="/tag/memcached" target="_blank">Memcached</a> кластер<br />
–––––– Поисковая система</p>
<dl> </dl>
<dl>
<dd>– Структура Dual Tree является индивидуальным набором модификаций для <a href="/tag/sql" target="_blank">MySQL</a>, позволяющим масштабировать систему путем добавления новых мастер-серверов без использования кольцевой архитектуры. Эта система позволяет экономить на масштабировании, так как варианты мастер-мастер требовали бы удвоенных вложений в оборудование.</dd>
</dl>
<dl>
<dd>– Центральная база данных включает в себя таблицу пользователей, состоящую из основных ключей пользователей (несколько уникальных идентификационных номеров) и указатель на сегмент, на котором может быть найдена остальная информация о конкретном пользователе.</dd>
</dl>
<ul>
<li>Использование выделенных серверов для статического контента</li>
<li>Все, за исключением фотографий, хранится в базе данных</li>
<li>Отсутствие состояний заключается в том, что в случае необходимости они имеют возможность передать пользователей от сервера к серверу, что стало намного проще для них после создания своего API</li>
<li>В основе масштабируемости лежит репликация, но этот факт помогает лишь при обработке операций чтения</li>
<li>Для поиска по определенной части базы данных создается отдельная копия этого фрагмента</li>
<li>Использования горизонтального масштабирования для того чтобы можно было проще добавлять новые машины в систему</li>
<li>Обработка изображений, полученных от пользователей по электронной почте, происходит с помощью <a href="/tag/php" target="_blank">PHP</a></li>
<li>Раньше система страдала от задержек связанных с организацией по принципу мастер-слуга. При слишком большой нагрузке они имели одну точку, которая теоретически могла дать сбой.</li>
<li>Им было необходимо иметь возможность проводить технические работы во время непрерывной работы сайта, не прекращая его функционирование.</li>
<li>Были проведены отличные работы по планированию распределения дискового пространства, более подробную информацию можно найти по ссылкам в разделе &#171;Источники информации&#187;.</li>
<li>Для обеспечения возможности масштабирования в будущем, они пошли по федеративному пути развития:
<dl>
<dd>– <em>Сегменты системы:</em> Мои данные хранятся на моем сегменте, но запись о Вашем комментарии хранится на Вашем сегменте.</dd>
<dd>– <em>Глобальное кольцо:</em> Принцип работы схож с DNS, Вам необходимо знать куда Вы хотите пойти и кто контролирует то место, куда Вы собираетесь пойти.</dd>
<dd>– Логика на <a href="/tag/php" target="_blank">PHP</a> устанавливает соединение с сегментом и поддерживает целостность данных (10 строк кода с комментариями!)</dd>
</dl>
</li>
<li><strong>Сегменты:</strong>
<dl>
<dd>– Срез основной базы данных</dd>
<dd>– Активная репликация по принципу мастер-мастер: имеет несколько недостатков в <a href="/tag/sql" target="_blank">MySQL</a> 4.1. Автоматическое инкрементирование идентификационных номеров используется для поддержания системы в режиме одновременной активности обоих серверов в паре</dd>
<dd>– Привязывание новых учетных записей к сегментам системы происходит случайным образом</dd>
<dd>– Миграция пользователей проводится время от времени для того, чтобы избавиться от проблем, связанных с излишне активными пользователями. Необходима сбалансированность в этом процессе, особенно в случаях с большим количеством фотографий… 192 тысячи фотографий, 700 тысяч тэгов, может занять несколько минут. Миграция выполняется вручную.</dd>
</dl>
</li>
<li>Нажатие на <strong>Favorite</strong>:
<dl>
<dd>– Получается информация об учетной записи владельца из кэша для того, чтобы узнать к какому сегменту он привязан (допустим на shard-5)</dd>
<dd>– Получается информация о моей учетной записи из кэша, более конкретно &#8212;  мой сегмент (например shard-13) </dd>
<dd>– Начинается &#171;распределенная трансакция&#187; для определения ответов на вопросы: Кто добавил эту фотографию в избранное? Как изменился список избранных фотографий?</dd>
</dl>
</li>
<li>Подобные вопросы могут задаваться любому сегменту, информация на них абсолютно избыточна.</li>
<li>Для избавления от задержек, связанных с репликацией&#8230;
<dl>
<dd>– при каждой загрузке страницы, пользователю предоставляется список серверов</dd>
<dd>– если сервер не в состоянии ответить на запрос, запрос переходит к следующему серверу в списке; если список кончился &#8212; выводится сообщение об ошибке. При этом не используются постоянные соединения, каждый раз создаются и разрываются новые соединения.</dd>
</dl>
</li>
<li>Запросы на чтение и запись от каждого пользователя ограничиваются рамками одного сегмента. Задержки репликации исчезают из поля зрения пользователей.</li>
<li>Каждый сервер в рамках одного сегмента в обычном состоянии нагружен ровно на половину. Выключите половину серверов в каждом сегменте и система продолжит функционировать без изменений. Это значит, что один сервер внутри сегмента может взять на себя всю нагрузку второго, в то время как второй сервер может по каким либо причинам быть отключен от системы, например для проведения технических работ. Обновление оборудования производится очень просто: отключается половина сегмента, она же обновляется, подключается обратно, процесс повторяется для оставшейся половины.</li>
<li>Периоды пиковой нагрузки также нарушают правило 50% нагрузки. В такие моменты система получает 6-7 тысяч запросов в секунду, в то время как на данный момент система может работать на пятидесятипроцентном уровне нагрузки только при четырех тысячах запросов в секунду.</li>
<li>В среднем при загрузке одной страницы выполняется 27-35 SQL-запросов. Списки избранных фотографий обрабатываются в реальном времени, ровно как и доступ через API к базе данных. Все требования к нагрузке в реальном времени выполняются без каких-либо недостатков.</li>
<li>Более 36 тысяч запросов в секунду может выполняться не выходя за рамки возможностей системы, даже при резком росте трафика.</li>
<li>Каждый сегмент содержит данные о более чем 400 тысячах пользователей.
<dl>
<dd>– Многие данные хранятся в двух местах одновременно. Например, комментарий является частью между комментатором и автором комментируемого контента. Где его хранить? Как насчет обоих мест? Трансакции используются для предотвращения рассинхронизации данных: открывается первая трансакция, выполняется запись, открывается вторая трансакция, выполняется запись, подтверждается первая трансакция если все нормально, после чего вторая подтверждается только в случае  если первая прошла успешно.</dd>
</dl>
</li>
<li><strong>Поиск:</strong>
<dl>
<dd>- Используется два варианта поиска: поиск в рамках сегмента, поддерживающий до 35 тысяч запросов в секунду, а также проприетарный веб-поиск от Yahoo!</dd>
<dd>- В 90% случаев используется система от Yahoo!, за исключением поиска по тэгу фотографий одного пользователя и массовых изменений тэгов.</dd>
<dd>- Эту систему стоит рассматривать как аналог Lucene.</dd>
</dl>
</li>
<li><strong>Оборудование:</strong>
<dl>
<dd>- EMT64 под управлением RHEL 4 с 16 Gb оперативной памяти.</dd>
<dd>- 6 жестких дисков с 15000rpm, объединены в RAID-10.</dd>
<dd>- Размер для пользовательских метаданных достигает 12 терабайт (это не включает фотографии, для них цифры существенно больше).</dd>
<dd>- Используются 2U корпуса.</dd>
</dl>
</li>
<li><strong>Процедура резервного копирования данных:</strong>
<dl>
<dd>- ibbackup выполняется регулярно посредством cron daemon&#8217;а, на каждом сегменте настроен на разное время.</dd>
<dd>- Каждую ночь делается снимок со всего кластера баз данных.</dd>
<dd>- Запись или удаление нескольких больших файлов с резервными копиями одновременно на реплицирующую систему хранения может сильно сократить производительность системы вцелом на последующие несколько часов из-за процесса репликации. Выполнение этого на активно работающей системе хранения фотографий было бы не самой лучшей идеей.</dd>
<dd>- Содержание нескольких резервных копий всех Ваших данных требует существенных материальных затрат, но оно того стоит. Особенно это актуально для тех ситуаций, когда Вы понимаете, что что-то пошло не так только спустя несколько  дней после того как это случилось, в таких случаях неплохо иметь, например, резервные копии 1, 3, 10 и 30-дневной давности.</dd>
</dl>
</li>
<li>Фотографии хранятся в системе хранения данных. После загрузки изображения система выдает различные его размеры, на чем ее работа заканчивается. Метаданные и ссылки на файловые системы, где расположены фотографии, хранятся в базе данных.</li>
<li>Аггрегация данных проходит очень быстро, так как она ограничена пределами сегмента.</li>
<li>max_connections = 400 соединений на каждый сегмент, неплохой запас. Значение для кэша потоков установлено равным 45, так как не бывает ситуаций когда более 45 пользователей одновременно выполняют какие-либо действия с одним конкретным сегментом.</li>
<li><strong>Тэги:</strong>
<dl>
<dd>- Тэги плохо вписываются в традиционную нормализованную схему реляционной базы данных. Денормализация или активное кэширование &#8212; единственные способы сгенерировать облако меток для сотен миллионов тэгов в течении миллисекунд.</dd>
<dd>Некоторые данные обрабатываются отдельными вычислительными кластерами, которые сохраняют результаты своей работы в MySQL, так как иначе вычисление сложных отношений заняло бы все процессорное время основных серверов баз данных.</dd>
</dl>
</li>
<li>Направления для развития: ускорение работы с помощью создания организационного плана для непрерывной работы всей системы на уровне нескольких датацентров, таким образом чтобы все датацентры имели возможность получать запросы на общий уровень данных (как сами БД, так и memcache и прочее) все вместе одновременно. Если все части системы постоянно активны &#8212; время простоя оборудования будет сведено к минимуму.</li>
</ul>
<h3>Подводим итоги</h3>
<ul>
<li>Старайтесь думть о своем приложении как о чем-то большем, чем просто веб-приложении, тогда у Вас возможно появятся поддержка различных API, RSS и Atom ленты и многие другие возможности.</li>
<li>Отсутствие состояний системы позволяет более  легко выполнять модернизации не моргнув и глазом.</li>
<li>Реструктуризация базы данных &#8212; не самое лучшее занятие.</li>
<li>Планирование нагрузок должно проводиться уже на ранних этапах развития проекта</li>
<li>Начинайте медленно. Не покупайте сразу много оборудования просто из-за того, что Вы рады/боитесь, что ваш сайт взорвется.</li>
<li>Измеряйте реально, планирование нагрузок должно базироваться на реальных вещах, а не абстрактных.</li>
<li>Внедряйте ведение логов и индивидуальные измерения для оценки реальных показателей на основе серверной статистики, статистика использования не менее важна чем серверная.</li>
<li>Кэширование и оперативная память может стать ответом на все вопросы.</li>
<li>Создавайте четкие уровни абстракции между работой базы данных, бизнес-логикой, логикой страниц, разметкой страниц и презентационным уровнем. Это позволяет ускорить циклы итеративной разработки.</li>
<li>Разделение приложения на уровни позволяет каждому заниматься своим делом: разработчики могут строить логику страниц, в то время как дизайнеры работают с удобством работы для пользователей.</li>
<li>Делайте релизы как можно чаще, пускай даже это будет происходить каждые полчаса.</li>
<li>Забудьте о всех небольших эффективных вещах, предварительная оптимизация является корнем всего зла в примерно 97% всех случаев.</li>
<li>Тестируйте в работе. Постройте архитектурные механизмы (флаги конфигурации, балансировку нагрузки, и так далее), которые позволят Вам разворачивать новое оборудование в (и из) работу.</li>
<li>Забудьте об искусственных тестах, они годятся только для получения общего представления о нагрузках, но не для планирования. Искуственные тесты дают искусственные результаты, для настоящих тестов все же стоит пользоваться реальным временем выполнения задач.</li>
<li>Найдите максимальное значения для всех показателей:
<dl>
<dd>- Какой максимум чего-то, что может выполнять каждый сервер?</dd>
<dd>- Как близко параметр находится к максимуму и каковы тенденции?</dd>
<dd>- <a href="/tag/sql" target="_blank">MySQL</a> (дисковый ввод/вывод?)</dd>
<dd>- <a href="/tag/squid" target="_blank">Squid</a> (дисковый ввод/вывод? или процессорное время?)</dd>
<dd>- <a href="/tag/memcached" target="_blank">Memcached</a> (процессорное время? или пропускная способность сети?)</dd>
</dl>
</li>
<li>Старайтесь учесть особенности использования Вашего приложения.
<dl>
<dd>- Возможен ли резкий рост нагрузки, связанный с каким-либо событием? Например: какое-либо бедствие, или может быть новость?</dd>
<dd>- Flickr получает на 20-40% больше новых фотографий в первый рабочий день нового года, чем в любой пик в предыдущем году.</dd>
<dd>- По воскресеньям нагрузка в среднем на 40-50% выше, чем в любой другой день недели.</dd>
</dl>
</li>
<li>Учтите возможность экспонентациального роста. Больше пользователей означает больше контента, больше контента означает больше соединений, больше соединений означает более активное использование.</li>
<li>Планируйте возможные варианты управления работой системы в периоды пиковых нагрузок.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/masshtabiruemost/arkhitektura-flickr/feed/</wfw:commentRss>
		<slash:comments>41</slash:comments>
		</item>
		<item>
		<title>На пути к идеалу</title>
		<link>http://www.insight-it.ru/programmirovanie/php/na-puti-k-idealu/</link>
		<comments>http://www.insight-it.ru/programmirovanie/php/na-puti-k-idealu/#comments</comments>
		<pubDate>Thu, 07 Feb 2008 12:39:34 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[online]]></category>
		<category><![CDATA[интернет]]></category>
		<category><![CDATA[информационные технологии]]></category>
		<category><![CDATA[код]]></category>
		<category><![CDATA[кодинг]]></category>
		<category><![CDATA[оптимизация]]></category>
		<category><![CDATA[Программирование]]></category>
		<category><![CDATA[производительность]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/programming/php/na-puti-k-idealu/</guid>
		<description><![CDATA[&#8230;или 15 привычек, которые помогут ускорить PHP-приложение Практически каждый программист стремится в своих приложениях не только максимально точно реализовать требуемый функционал, но и сделать это как можно более эффективным методом. Для этого конечно же необходимо проектирование, подходящий выбор используемых технологий, возможно некоторый опыт в предметной области, этот список можно продолжать достаточно долго, но я позволю [...]]]></description>
			<content:encoded><![CDATA[<h4 style="text-align: right">&#8230;или 15 привычек, которые помогут ускорить PHP-приложение</h4>
<p>Практически каждый программист стремится в своих приложениях не только максимально точно реализовать требуемый функционал, но и сделать это как можно более эффективным методом. Для этого конечно же необходимо проектирование, подходящий выбор используемых технологий, возможно некоторый опыт в предметной области, этот список можно продолжать достаточно долго, но я позволю себе этого не делать, так как речь сегодня пойдет не об этом. Вместо этого хочу обратить Ваше внимание на более простые и &#171;приземленные&#187; методы <a href="/tag/optimizaciya" target="_blank">оптимизации</a> PHP-кода, которые может быть и не так эффективны по сравнению с указанными выше, но зато не требуют каких-либо усилий со стороны кодера и/или программиста, достаточно лишь воспринимать их как &#171;не вредные&#187; привычки.</p>
<p><span id="more-25"></span></p>
<p>Прочитав достаточно солидный объем разного рода документации по <a href="/tag/php" target="_blank">PHP</a>, я часто натыкался на статьи и тексты, так или иначе связанные с <a href="/tag/proizvoditelnost" target="_blank">производительностью</a> PHP-скриптов. Порой в такого рода источниках информации удавалось найти достаточно интересные и неочевидные факты об этом языке программирования, которые не смотря на свою простоту могли дать вполне заметный прирост к <a href="/tag/proizvoditelnost" target="_blank">производительности</a> итогового приложения. Я почему-то очень серьезно стал относиться к производительности написанных мной скриптов, и довольно часто стал испытывать на практике спорные моменты в реализации, о которых узнавал из <a href="/net" target="_blank">Сети</a> или каких-либо других источников, с помощью самописных или <a href="/tag/opensource" target="_blank">opensource</a> benchmark&#8217;ов, хотя порой и просто внедряя в реальные приложения. Как ни странно, в большинстве случаев практика подтверждала теорию, и я стал постоянно пользоваться этими простыми правилами, о которых я и хочу Вам рассказать.</p>
<h4>Повышения значения индекса с помощью ++$i;</h4>
<p>Этот факт был наверное одним из самых удивительных для меня, когда я впервые о нем услышал, но действительно операция <strong>++<span style="color: blue">$i</span></strong>; выполняется несколько быстрее, чем <strong><span style="color: blue">$i</span>++;</strong>. или другие вариации на ту же тему вроде <strong><span style="color: blue">$i</span>+=<span style="color: blue">1</span>;</strong>. Привычка использовать в качестве индекса цикла переменную под названием i, казалось бы стара как Мир, мне она досталась в наследство от <strong>C</strong>, а в месте с ней &#171;в комплекте&#187; шла привычка писать выражение <strong>i++</strong> в заголовках циклов. Разница в скорости обработки этих выражений, насколько мне известно, обусловлена разным количеством элементарных машинных операций, которые необходимо выполнить процессору (в точных цифрах не уверен, пишу по памяти, но <strong>++<span style="color: blue">$i</span>;</strong> требует трех элементарных операций, а <strong><span style="color: blue">$i</span>++;</strong> – четырех). В справедливости этого факта не трудно убедиться, достаточно написать простенький скрипт, состоящий из цикла с достаточно большим количеством итераций, и замерить любым способом точное время его выполнения при использовании разных способов инкрементации индекса цикла.</p>
<h4>Вывод статического контента без помощи PHP</h4>
<p>Сейчас тот факт, что использование интерпретатора PHP для вывода статического контента сильно замедляет этот процесс, кажется мне очевидным, но поначалу я использовал <strong><span style="color: #17349c">echo</span></strong> там, где он был необходим, ничуть не чаще, чем там, где он лишь замедляет работу скрипта. От использования еще менее эффективного способа &#8212; <strong><span style="color: #17349c">print</span></strong>, меня избавила моя лень: писать каждый раз на одну букву больше дико не хотелось (в отличии от <strong><span style="color: #17349c">echo</span></strong>, <strong><span style="color: #17349c">print</span></strong> возвращает информацию об успешности выполнения своей работы, что в большинстве случаев просто-напросто не нужно). Проверить опять же не трудно &#8212; нужен лишь объемистый текстовый файл, который достаточно вывести в browser разными способами и засечь уходящее на это время.</p>
<h4>Вывод статического контента из отдельного файла</h4>
<p>Частенько при желании выполнить указанное в заголовке действие по привычке используют <strong><span style="color: #a3a423">include</span></strong>, <strong><span style="color: #a3a423">require</span></strong> или их <strong><span style="color: #a3a423">_once</span></strong> версии, что является далеко не самой лучшей идеей с точки зрения производительности. Самым быстрыми быстрыми и экономичными поотношению к оперативной памяти являются функции <strong><span style="color: #17349c">readfile</span></strong> и <strong><span style="color: #17349c">fpassthru</span></strong>. В качестве доказательства этого факта приведу таблицу, демонстрирующую статистику выполнения этой операции различными методами и позаимствованную с <a href="http://www.raditha.com/wiki/Readfile_vs_include" target="_blank" rel="nofollow">одного англоязычного сайта</a>:</p>
<table align="center" border="1" cellspacing="0" width="100%">
<tr>
<th rowspan="2" style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial" align="center" valign="center">Функция</th>
<th colspan="2" style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial" align="center">Время (сек.)</th>
<th colspan="2" style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial" align="center">Оперативная память (байт)</th>
</tr>
<tr>
<td style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial" align="center">32Kb файл</td>
<td style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial" align="center">1Mb файл</td>
<td style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial" align="center">32Kb файл</td>
<td style="background: #fcfcc0 none repeat scroll 0% 50%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial" align="center">1Mb файл</td>
</tr>
<tr>
<td>file_get_contents</td>
<td align="right">0.00152</td>
<td align="right">0.00564</td>
<td align="right">52480</td>
<td align="right">1067856</td>
</tr>
<tr>
<td>fpassthru</td>
<td align="right"><strong>0.00117</strong></td>
<td align="right">0.00184</td>
<td align="right">20016</td>
<td align="right">20032</td>
</tr>
<tr>
<td>fgets</td>
<td align="right">0.00195</td>
<td align="right">0.07190</td>
<td align="right">30760</td>
<td align="right">30768</td>
</tr>
<tr>
<td>file</td>
<td align="right">0.00157</td>
<td align="right">0.06464</td>
<td align="right">87344</td>
<td align="right">2185624</td>
</tr>
<tr>
<td>require_once</td>
<td align="right">0.00225</td>
<td align="right">0.08065</td>
<td align="right">67992</td>
<td align="right">2067696</td>
</tr>
<tr>
<td>readfile</td>
<td align="right"><strong>0.00117</strong></td>
<td align="right">0.00191</td>
<td align="right"><strong>19192</strong></td>
<td align="right">19208</td>
</tr>
</table>
<h4>Вывод переменных</h4>
<p>Наверняка вам известно, что переменные можно выводить с помощью конструкции вроде <strong><span style="color: #17349c">echo</span> <span style="color: red">&#171;</span><span style="color: blue">$var</span> <span style="color: red">text&#187;</span>;</strong>, что является одним из самых удобных вариантов решения этой задачи благодаря минимальному количеству символов, которые необходимо набрать, но с точки зрения быстродействия этот вариант далек от идеала, так как влечет за собой достаточно серьезные преобразования в памяти сервера, эффект которых порой бывает заметен невооруженным глазом. Частично ущерб производительности можно сгладить заменой этой конструкции на <strong><span style="color: #17349c">echo</span> <span style="color: blue">$var</span>.<span style="color: red">&#187; text&#187;</span>;</strong>, что приводит к несколькому усложнению внешнего вида кода и несколько поправляет ситуацию со скоростью выполнения. Но как известно знак . обозначает конкатенацию двух строк, что тоже требует некоторых вычислений и затрат памяти, но и от нее можно избавиться, заменив на запятую. Выражение <strong><span style="color: #17349c">echo</span> <span style="color: blue">$var</span>,<span style="color: red">&#187; text&#187;</span>;</strong> ничем по своему эффекту не отличается от предложенных ранее вариантов, за исключением максимального быстрого выполнения, обусловленного отсутствием дополнительных преобразований в процессе передачи просто последовательности из константы и переменной.</p>
<h4>Избегайте выполнения лишних действий</h4>
<p>Достаточно абстрактное утверждение, но тем не мение постоянное напоминание себе о нем может избавить Вас от совершения массы ошибок. Самой широкораспространенной является наверное вызов какой-либо функции (чаще всего <strong><span style="color: #17349c">count</span>();</strong> или <strong><span style="color: #17349c">strlen</span>();</strong>) в проверке условия выхода из цикла. Когда-нибудь доводилось писать видеть в собственном или чужом коде выражение вида <strong><span style="color: #a3a423">for</span>(<span style="color: blue">$i</span> = <span style="color: #a3a423">0</span>; <span style="color: blue">$i</span> &lt; <span style="color: #17349c">count</span>(<span style="color: blue">$array</span>); ++<span style="color: blue">$i</span>) { &#8230; }</strong>? А задумываться о последовательности выполнения действий при его обработке? Стоит только немного начать размышлять и ошибка становится очевидной: <strong><span style="color: #17349c">count</span>();</strong> выполняется при каждой итерации цикла, что приводит к подсчету количества элементов массива при каждой проверки условия выхода из цикла &#8212; почему бы не посчитать это значение заранее и сравнивать значения индекса с переменной, а не с результатом выполнения функции?</p>
<h4>@</h4>
<p>Использование этого оператора стоит избегать при каждой возможности. Казалось бы такое простое действие, как сокрытие вывода возможного сообщения об ошибке, влечет за собой достаточно трудоемкую последовательность действий: устанавливает значение параметра PHP-интерпретатора <strong>error_reporting = 0</strong>, выполняет указанное за этим оператором действие, возвращает значение <strong>error_reporting</strong> в исходное состояние.</p>
<h4>Маленькие мелочи</h4>
<p>Развивая тему предыдущего подраздела, хочется обратить внимания, что даже на еще более элементарных вещах можно сэкономить драгоценное процессорное время:</p>
<ul>
<li>Вместо условия <strong><span style="color: #a3a423">if</span>(<span style="color: blue">$variableOne</span> == <span style="color: blue">$variableTwo</span>) { &#8230; }</strong> можно написать <strong><span style="color: #a3a423">if</span>(<span style="color: blue">$variableOne</span> === <span style="color: blue">$variableTwo</span>) { &#8230; }</strong>, что избавит от проверки на соответствие типов данных и приведения их друг к другу, в некоторых случаях эти действия эти случаях эти действия конечно же и бывают необходимы, но бывает это далеко не часто.</li>
<li>Глядя на выражения вроде <strong><span style="color: #a3a423">if</span>(<span style="color: blue">$boolean</span> == true) { &#8230; }</strong>, я чаще всего вспоминаю цитату из одного малоизвестного интернет-ресурса: <strong>if (b.ToString().length &lt; 5) { &#8230; }</strong>. Хоть и не имет никакого отношения к PHP, но суть проблемы отражает очень ярко.</li>
<li>Самым очевидным способом проверить попадает ли длина строки в какой-либо диапазон является использование функции <strong><span style="color: #17349c">strlen</span>();</strong> и сравнение полученного результата с фиксированными значениями, но зачем выполнять лишний вызов функции, если можно воспользоваться услугами конструкцией языка PHP <strong><span style="color: #17349c">isset</span>();</strong> для определения наличия в строке определенных символов. <strong><span style="color: #a3a423">if</span>(<span style="color: #17349c">isset</span>(<span style="color: blue">$str</span>{<span style="color: blue">5</span>})) { &#8230; }</strong> приведет к абсолютно тем же результатам, что и <strong><span style="color: #a3a423">if</span>(<span style="color: #17349c">strlen</span>($str)&gt;4){ &#8230; }</strong></li>
<li>Битовые операции выполняются намного быстрее относительно обычных арифметических действий. Об этом факте редко вспоминают, да и работать с ними умеет далеко не каждый, но порой они бывают очень актуальны, особенно при частой работе с числами кратными двойке.</li>
<li>Угадайте, что делает интерпретатор при виде надписи <strong><span style="color: blue">1</span>/</strong><strong><span style="color: blue">2</span></strong>? Правильно: делит <strong><span style="color: blue">1</span></strong> на <strong><span style="color: blue">2</span></strong>. Зачем лишний раз утруждать его, когда можно написать просто половину &#8212; <strong><span style="color: blue">0.5</span></strong>.</li>
<li>При возвращении значения переменной из функции при помощи <strong><span style="color: #17349c">global</span></strong> выполняется на порядок больше действий, чем при классическом <strong><span style="color: #17349c">return</span></strong>.</li>
<li>Конечно же фраза <strong><span style="color: blue">$array</span>[text];</strong> интерпритируется практически точно так же, как и <strong><span style="color: blue">$array</span>[<span style="color: red">'text'</span>];</strong>, но зачем выполнять лишнее преобразование из необъявленной константы в строку, проверять, что такой константы все же не существует, выводить сообщение типа <strong>E_NOTICE</strong>, если можно всего этого не делать?</li>
<li>По возможности не используйте <strong><span style="color: #17349c">require_once</span>();</strong> или <strong><span style="color: #17349c">include_once</span>();</strong> неоднократно по отношению к одному и тому же файлу. При отсутствии какого-либо эффекта, попусту тратится время на обработку повторного запроса.</li>
<li>Даже &#171;безобидных&#187; ошибок стоит избегать, лишняя проаерка потратит не так много процессорного времени, как генерирование достаточно длинного сообщения об ошибке и вывод его в <em>stdout</em>, <em>stderr</em> или <em>лог-файл</em>, а также не стоит забывать, что даже &#171;безобидные&#187; ошибки могут стать потенциальной угрозой безопасности приложения вцелом.</li>
</ul>
<h4>В заключении&#8230;</h4>
<p>&#8230;хотелось бы упомянуть одну из первых статей по <a href="/tag/optimizaciya" target="_blank">оптимизации</a> <a href="/tag/php" target="_blank">PHP</a>, которые мне доводилось читать, до сих пор храню ссылку на нее в bookmark&#8217;ах, именно она и выступала в роли <em><a href="http://mgccl.com/php-speed-freaks" target="_blank" rel="nofollow">одного из основных источников информации</a></em> для этого текста. В качестве возможных вариантов продолжения чтения про <a href="/tag/php" target="_blank">PHP</a> хотелось бы предложить Вам соответствующие <a href="/php" target="_blank">раздел сайта</a>, <a href="/dzhentelmenskij-nabor-php-programmista/" target="_blank">серию статей</a>, <a href="/tag/php" target="_blank">тэг</a> и <a href="/feed" target="_blank">RSS-ленту</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/php/na-puti-k-idealu/feed/</wfw:commentRss>
		<slash:comments>32</slash:comments>
		</item>
		<item>
		<title>Шаблонизация</title>
		<link>http://www.insight-it.ru/programmirovanie/php/shablonizaciya/</link>
		<comments>http://www.insight-it.ru/programmirovanie/php/shablonizaciya/#comments</comments>
		<pubDate>Sat, 26 Jan 2008 12:25:36 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[blitz]]></category>
		<category><![CDATA[Smarty]]></category>
		<category><![CDATA[template]]></category>
		<category><![CDATA[template engine]]></category>
		<category><![CDATA[интернет]]></category>
		<category><![CDATA[ООП]]></category>
		<category><![CDATA[разработка]]></category>
		<category><![CDATA[шаблон]]></category>
		<category><![CDATA[шаблонизация]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/programming/php/shablonizaciya/</guid>
		<description><![CDATA[Наверняка Вы часто замечали, что в пределах одного сайта все (или покрайней мере большинство) страниц имеют много общего: структуру, расположение элементов, дизайн и так далее. Основным различием обычно является лишь содержание. Естественно, что делается это не спроста: именно общие компоненты сайта создают в голове посетителей тот самый образ, который производит общее подсознательное впечатление о сайте, [...]]]></description>
			<content:encoded><![CDATA[<p>Наверняка Вы часто замечали, что в пределах одного сайта все (или покрайней мере большинство) страниц имеют много общего: структуру, расположение элементов, дизайн и так далее. Основным различием обычно является лишь содержание. Естественно, что делается это не спроста: именно общие компоненты сайта создают в голове посетителей тот самый образ, который производит общее подсознательное впечатление о сайте, а также позволяет посетителям отличать сайт А от сайта Б.</p>
<p>Продолжая разговор, начатый еще в <a target="_blank" href="/programming/php/obshhaemsya-s-bazojj-dannykh/">одной</a> из <a target="_blank" href="/dzhentelmenskij-nabor-php-programmista">предыдущих статей</a>, рассмотрим организацию интерфейса между двумя другими составляющими практически любого интернет-проекта: скриптов (все так же на примере PHP) и страницами, отправляемыми посетителям посредством http-сервера.</p>
<p><span id="more-32"></span></p>
<p>С точки зрения веб-разработчика было бы как минимум не логично мешать в кучу постоянные части страниц с динамическими. Для этого существует множество причин, в том числе, например, экономия вычислительной мощности сервера на отсутствии необходимости каждый раз заново генерировать статичные элементы или неминуемое сокращение объемов кода, который необходимо написать, в случае если статический и динамический контент разделены. Отделенную подобным образом статическую часть страниц (слегка модифицированную с целью обозначить правила, по которым будет проводиться &quot;заполнение&quot; ее динамическим контентом) принято называть словом <em>&quot;шаблон&quot;</em>.</p>
<p>Наверняка у Вас уже возникло два вполне логичных вопроса:</p>
<ol>
<li>Как можно разделить таким образом контент?</li>
<li>Как потом восстановить страницу в исходном виде?</li>
</ol>
<p>Вариантов ответа на каждый из них можно придумать множество: начиная с банальных вариаций на тему применения <strong>echo</strong>, заканчивая применением достаточно серьезных готовых решений вроде широкораспространенного <a href="http://smarty.php.net" target="_blank" rel="nofollow"><strong>Smarty</strong></a> или существенно более эффективного <a href="http://alexeyrybak.com/blitz/blitz_ru.html" target="_blank" rel="nofollow"><strong>Blitz</strong></a>. Каждый из них имеет свои сильные и слабые стороны, но в целом любой из них можно оценить по двум критериям: производительности и удобстве организации кода.</p>
<p>Какие-либо цифры оценки производительности приводить не буду, так как, во-первых, в Сети можно найти много benchmark&#8217;ов, посвященных этой теме, а, во-вторых, просто-напросто вовсе не о цифрах я хотел с Вами поговорить. Как известно самым производительным по крайней мере с теоретической точки зрения является метод под названием <strong>$php$ mess</strong>, заключается он в следующем: вся страница размещается в рамках одного файла, при этом статическая часть документа пишется просто &quot;как есть&quot; в соответствии с необходимым стандартом, а изменяемые части организуются размещенным в необходимых местах PHP-кодом, окруженным стандартной конструкцией <strong>&lt;?php&nbsp;&nbsp;&nbsp;?&gt;</strong>. Но огромнейший недостаток очевиден &#8212; огромное количество информации расположенной в одном файле, при отсутствии какого-либо более четкого разделения PHP-кода и остального содержимого, чем указанная выше конструкция, приводит к постоянной путанице в коде, а также существенным затратам времени программиста при попытках исправить ту или иную часть документа.</p>
<p>На противоположной стороне нашей шкалы <em>удобство-производительность</em> я бы расположил уже упомянутое выше решение под названием <strong>Smarty</strong>. Представляет оно собой целую систему, реализованную также на PHP, и предоставляющую огромное количество возможностей по решению нашей задачи. Шаблоны хранятся в отдельных файлах, для определения мест расположения динамического контента используется специальный синтаксис, который прост как три копейки, так как разрабатывался с расчетом не на программистов, а по принципу &quot;чем проще, тем лучше&quot;. Именно этот факт сделал <strong>Smarty</strong> одним из самых (если не самым) распространенных движков шаблонизации (или как их принято правильно называть &quot;Template Engine&quot;). Но, к сожалению, за удобство приходится платить, в этом случае производительностью: вся система сама по себе громоздка и состоит из множества файлов, между которыми все данные так или иначе передаются, а так как написано она на PHP (который является далеко не самым производительным языком программирования, в основном в силу своей интерпритируемости и некоторых других особенностей), конкуренции в плане производительности многим другим вариантам решения нашей задачи <strong>Smarty</strong> составить не в состоянии.</p>
<p>Одним из лучших &quot;компромиссных&quot; вариантов, которые доступны на данный момент, могу назвать также упомянутый выше <strong>Blitz</strong>. Реализован он в виде модуля PHP, написанного на языке <strong>C</strong>, что является залогом его отличной производительности. При этом общая его концепция близка к <strong>Smarty</strong>: шаблоны также хранятся в отдельных файлах и подчинены незамысловатому синтаксису (который вообще можно понять и запомнить буквально за 15-20 минут, прочитав статью, <a href="http://alexeyrybak.com/blitz/blitz_ru.html" target="_blank" rel="nofollow">ссылку на которую</a> я уже приводил выше), а в PHP-скриптах после установки становится доступен специальный класс для управления модулем. Но основное достоинство этого решения является одновременно и его основным недостатком &#8212; редкий хостинг имеет этот модуль в списке предустановленных (видимо в силу своей не очень обширной известности, обусловленной ), а доступ к http-серверу и PHP-интерпретатору, который необходим для установки PHP-модулей, предоставляется чаще всего только на дорогих тарифах виртуального хостинга или на различных вариантах VPS или арендуемых серверов.</p>
<p>Помимо этого некоторые энтузиасты берутся на написание &quot;собственных&quot; <em>Template Engine</em>, базирующихся на различных вариантов использования PHP-функций вроде <strong>preg_replace</strong>. Если честно такие попытки редко заканчиваются успехом: в лучшем случае удается добиться удобства использования самим разработчиком, но чаще всего в ущерб производительности. Заниматься подобными экспериментами я Вам не советую, вместо этого я предлагаю написать <em>&quot;обертку&quot;</em> к приглянувшемуся распространенному <em>Template Engine</em>, что позволит не только сделать его использование более удобным конкретно для Вас, но и позволит заменить его на другой с минимальными затратами сил и времени (например в случае, если модуль <strong>Blitz</strong> недоступен).</p>
<h3>Разрабатываем &quot;обертку&quot;</h3>
<p>Сразу скажу, что цели привести конкретный пример пригодного для реального использования кода я перед собой не ставлю в этой части моего повествования. Я лишь хочу показать направление, в котором можно провести разработку с целью облегчения собственной же жизни, т.е. предоставить Вам альтернативу простому использованию тех или иных решений в том виде, в котором они предоставлены разработчиками.</p>
<p>Если Ваш выбор всетаки пал на написание &quot;оболочки&quot;, не смотря на принесение в жертву несущественной части производительности, то стоит для начала определиться: а что же мы будем &quot;заворачивать&quot;? В качестве примера я, пожалуй, буду использовать <strong>Blitz</strong>, как самый оптимальный вариант (по крайней мере с моей точки зрения).</p>
<p>Начать стоит как обычно с пустой заготовки для класса:</p>
<pre lang="PHP">
<?php
class Template
{
  private $engine; // экземпляр оригинального класса управления модулем
  public function __construct($template)
  {
    //можно сразу указать указать путь к папке с шаблонами
    $this->engine=new Blitz('./template/'.$template.'.tpl');
  }
}
?>
</pre>
<p>Далее следует решить какие все же модификации мы будем производить для собственного удобства над стандартным решением. Попробую привести несколько примеров в отношении <strong>Blitz</strong>, для начала хочу обратить внимание, что при внимательном прочтении все той же статьи от разработчика этого шаблонизатора, можно обнаружить, что модуль показывает более высокие показатели производительности при однократном вызове метода <em>set</em>. Достичь это можно выполнением этого метода с указанием в качестве одного из входных параметров &quot;многоуровнего&quot; массива, составленного специальным образом (надеюсь Вы все же к этому моменту уже успели прочитать неоднократно упоминавшуюся статью, и представляете принцип работы модуля). Написание механизма составления такого массива позволит как сократить время разработки, так и сэкономит драгоценные миллисекунды, вычитаемые из свободного времени посетителей сайта в процессе генерации страницы.</p>
<p>В любом случае понадобится переменная для его хранения:</p>
<pre lang="PHP">
<?php
class Template
{
  private $engine; // экземпляр оригинального класса управления модулем
  private $array;  // собираемый массив
  public function __construct($template)
  {
    $this->array=array();
    //можно сразу указать указать путь к папке с шаблонами
    $this->engine=new Blitz('./template/'.$template.'.tpl');
  }
}
?>
</pre>
<p>А также метод, переопределяющий стандартный <em>set</em> на метод, добавляющий новые значения к нашему массиву (хотя можно и любое другое понравившееся название использовать):</p>
<pre lang="PHP">
function set($caption,$value)
{
  $this->array[$caption]=$value;
}
</pre>
<p>После чего оригинальный <em>set</em> можно использовать уже непосредственно перед <em>parse</em>, с указанием уже собранного массива в качестве параметра. За компанию позволю произвести себе еще одну модификацию: в подавляющем большинстве случаев <em>parse</em> используется в совокупности с <strong>echo</strong>, чтобы не указывать каждый раз это слово &#8212; можно включить его прямо в наш класс:</p>
<pre lang="PHP">
function parse()
{
  if(count($this->array))$this->engine->set($this->array);
  echo $this->engine->parse();
}
</pre>
<p>Еще одним возможным вариантом модификации может стать обработка всех (или какой-то части, если есть необходимость) динамических данных с помощью какой-либо функции, например это актуально для <strong>htmlspecialchars</strong>:</p>
<pre lang="PHP">
function set($caption,$value)
{
  $this->array[$caption]=$this->html($value);
}
function rawset($caption,$value)
{
  $this->array[$caption]=$value;
}
private function html($array)
{
  if(is_array($array))
  {
    foreach($array as $caption => $value)
    $value=$this->html($value);
    return $array;
  }
  else return htmlspecialchars($array,ENT_QUOTES);
}
</pre>
<p>Как нетрудно заметить, в методе используется рекурсия, так как структура передаваемых параметром массивов неизвестна.</p>
<p>Надеюсь написанный выше текст подтолкнет Вас к действию или хотябы заставит задуматься над имеющимся выбором, если же Вы читали его лишь &quot;для общего развития&quot;, то тем более хочется сказать Вам огромное <em>Спасибо</em><em> за то, что дочитали до конца это повествование.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/php/shablonizaciya/feed/</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
		<item>
		<title>Общаемся с базой данных</title>
		<link>http://www.insight-it.ru/programmirovanie/php/obshhaemsya-s-bazojj-dannykh/</link>
		<comments>http://www.insight-it.ru/programmirovanie/php/obshhaemsya-s-bazojj-dannykh/#comments</comments>
		<pubDate>Wed, 16 Jan 2008 19:04:09 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[БД]]></category>
		<category><![CDATA[интерфейс]]></category>
		<category><![CDATA[кодинг]]></category>
		<category><![CDATA[ООП]]></category>
		<category><![CDATA[СУБД]]></category>
		<category><![CDATA[технология]]></category>
		<category><![CDATA[хранение данных]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/programming/php/obshhaemsya-s-bazojj-dannykh/</guid>
		<description><![CDATA[На этот раз хочется обсудить такой одновременно важный и несложный момент в реализации работы любого интернет-проекта, как координации работы Ваших скриптов с СУБД. Если подойти к этому вопросу &#34;в лоб&#34;, то код станет очень неудобен как для понимания, так и для использования: код станет переполнен различными функциями соединения с БД, отправки запросов, преобразования результатов запросов [...]]]></description>
			<content:encoded><![CDATA[<p>На этот раз хочется обсудить такой одновременно важный и несложный момент в реализации работы любого интернет-проекта, как координации работы Ваших скриптов с СУБД.</p>
<p>Если подойти к этому вопросу &quot;в лоб&quot;, то код станет очень неудобен как для понимания, так и для использования: код станет переполнен различными функциями соединения с БД, отправки запросов, преобразования результатов запросов в массивы PHP, подсчета строк, которые затронул запрос, а также многие и многие другие.</p>
<p>Для желающих минимизировать подобного рода издержки в процессе написания кода, хочу предложить один из, на мой взгляд, самых эффективных способов решения этой проблемы.</p>
<p><span id="more-26"></span></p>
<p>Этим способом будет являться написание класса, реализующего интерфейс между СУБД и PHP-скриптами. Для начала стоит определиться с ассортиментом функций, которые будет призван выполнять наш класс:</p>
<ul>
<li><em>установка соединения</em>, а также проверка успешности выполнения этого действия;</li>
<li><em>отправка запросов</em>, как заданных извне так и, возможно, из какого-либо ассортимента заранее написанных запросов;</li>
<li><em>обработка результатов запросов</em>, не ограничивающаяся одним SELECT, должны быть предоставлены методы обработки результатов любых видов запросов (или хотябы большинства).</li>
</ul>
<p>Вполне очевидным является тот факт, что методы этого класса будут использоваться практически повсеместно в большинстве проектов. Вследствии чего становится нецелесообразным создание объекта нашего класса и передача его по всем функциям и методам всех скриптов, в таких случае намного предпочтительнее делать владельцем методов и переменных сам класс, а не экземпляр класса, с помощью ключевого слова <strong>static</strong>. Это позволит пользоваться услугами нашего класса из любого места кода. Приступим-с собственно к кодингу, начать стоит с заготовки пустого класса:</p>
<pre lang="PHP">
<?php
class SQL
{
  private static $connection; // соединение с СУБД
  static function connect()  // установка соединения
  {

  }
  static function query($str,$bool=false) // произвольный запрос
  {

  }

?>
</pre>
<p>В зависимости от предпочитаемой Вами СУБД набор конкретных функций, используемых в реализации нашего класса, будет вариироваться. В большинстве случаев предпочитаю пользоваться PostgreSQL, на это причин у меня несколько, но это тема для отдельного разговора. Благодаря этому факту приводимый в качестве примера код будет использовать функции для работы именно с этой СУБД. Для поклонников же других этот систем вопрос в подавляющем большинстве случаев заключается лишь в замене этих функций на аналогичные из других модулей PHP, например для популярной и широкораспространенной MySQL достаточно будет всеголишь пройтись автозаменой <strong>pg_ =&gt; mysql_</strong> и слегка подредактировать параметры некоторых функций.</p>
<p>Перейдем к реализации установления соединения с СУБД, не стоит ожидать увидеть здесь ничего необычного:</p>
<pre lang="PHP">
static function connect()  // установка соединения
{
  self::$connection=pg_pconnect("host=localhost dbname=pgsql user=pgsql password=MyPassword");
  // не забываем менять указанные данные для авторизации на правильные
  if(!isset(self::$connection))
  {
    echo "Сайт не работает по техническим причинам.Просим прощения за доставленные неудобства.";
    exit;	// ни в коем случае не выводим более информативных сообщений об ошибке, чем это
  }
}
</pre>
<p>А вот с отправкой и обработкой результатов запросов ситуация далеко не так однозначна. Помимо простой передачи самого текста запроса в СУБД, необходимо правильно определить тип запроса и в соответствии с этим обработать результат. Можно конечно попытаться сделать это автоматически на основе вытаскивания первого слова из текста запроса, но мне всетаки кажется более предпочтительным определение &quot;вручную&quot; желаемого вида представление результата. Выполнение произвольных запросов может выглядеть, например, следующим образом:</p>
<pre lang="PHP">
static function query($str,$bool=false) // произвольный запрос
{
  //echo $str.""; // очень удобно на стадии разработки в процессе поиска ошибок
  $result=@pg_query(self::$connection,$str); // @ - для сокрытия теоретически возможных ошибок
  // or die('Query failed: '.pg_last_error());
  // не забываем убирать в комментарий в финальном варианте проекта
  // или совсем удалять
  if($result)  // Если получен результат, отличный от false
  {
    if($bool)  // Если выбран результат в виде boolean
    {
      return true;
    }
    else  // Если выбран результат в виде массива
    {
      $n=pg_num_rows($result);	// для создания универсального формата массива
      if($n==1)return pg_fetch_array($result,0,PGSQL_ASSOC);
      else  // даже когда результат содержит только одну строку
      {
        $j=pg_num_rows($result);
        $list=array();
        for($i=0;$i<$j;$i++)
        $list[]=pg_fetch_array($result,$i,PGSQL_ASSOC);
        return $list;
      }
    }
  }else return false;
}
</pre>
<p>Помимо базовой отправки запросов, в некоторых случаях имеет смысл написать несколько методов, отправляющих частоиспользуемые запросы, что в некоторых случаях позволяет сократить объем и уменьшить нагроможденность кода. Хоть я и предпочитаю не пользоваться такими вещами, но привести пример такого рода метода все же стоит:</p>
<pre lang="PHP">
static function selectAll($table)
{ // пример метода отправки чаcтоиспользуемых запросов
  return self::query("select * from ".$table.";");
}
</pre>
<p>Если чувствуете необходимость в подобных функциях, можно написать огромное количество, все ограничивается лишь Вашим воображением и знаниями SQL.</p>
<p>Что ж, осталось лишь собрать весь код в <a href="/wp-content/uploads/source/sql.class.phps">единый листинг</a>:</p>
<pre lang="PHP">
<?php
class SQL
{
  private static $connection; // соединение с СУБД
  static function connect()  // установка соединения
  {
    self::$connection=pg_pconnect("host=localhost dbname=pgsql user=pgsql password=MyPassword");
    // не забываем менять указанные данные для авторизации на правильные
    if(!isset(self::$connection))
    {
      echo "Сайт не работает по техническим причинам.Просим прощения за доставленные неудобства.";
      exit;	// ни в коем случае не выводим более информативных сообщений об ошибке, чем это
    }
  }
  static function query($str,$bool=false) // произвольный запрос
  {
    //echo $str.""; // очень удобно на стадии разработки в процессе поиска ошибок
    $result=@pg_query(self::$connection,$str); // @ - для сокрытия теоретически возможных ошибок
    // or die('Query failed: '.pg_last_error());
    // не забываем убирать в комментарий в финальном варианте проекта
    // или совсем удалять
    if($result)  // Если получен результат, отличный от false
    {
      if($bool)  // Если выбран результат в виде boolean
      {
        return true;
      }
      else  // Если выбран результат в виде массива
      {
         $n=pg_num_rows($result);	// для создания универсального формата массива
         if($n==1)return pg_fetch_array($result,0,PGSQL_ASSOC);
         else  // даже когда результат содержит только одну строку
         {
           $j=pg_num_rows($result);
           $list=array();
           for($i=0;$i<$j;$i++)
           $list[]=pg_fetch_array($result,$i,PGSQL_ASSOC);
           return $list;
         }
      }
    }else return false;
  }
  static function selectAll($table)
  { // пример метода отправки чаcтоиспользуемых запросов
    return self::query("select * from ".$table.";");
  }
}
?>
</pre>
<p>Вот так вот оно и выглядит в простейшем варианте, дорабатывать под собственные нужды код можно до бесконечности естественно, но в большинстве случаев даже такой реализации вполне должно хватать.</p>
<p>Эта статья является частью <a href="/dzhentelmenskij-nabor-php-programmista">серии статей "Джентельменский набор PHP программиста"</a>, если Вам понравилась эта статья то очень вероятно, что Вам придутся по душе и остальные статьи.</p>
<p>Не забываем <a href="/feed" target="_blank">подписываться на RSS блога</a>, а также на <a href="/comments/feed" target="_blank">ленту комментариев</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/php/obshhaemsya-s-bazojj-dannykh/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>Защита интернет-ресурсов в картинках</title>
		<link>http://www.insight-it.ru/programmirovanie/php/zashhita-internet-resursov-v-kartinkax/</link>
		<comments>http://www.insight-it.ru/programmirovanie/php/zashhita-internet-resursov-v-kartinkax/#comments</comments>
		<pubDate>Sun, 13 Jan 2008 19:30:00 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[captcha]]></category>
		<category><![CDATA[OCR]]></category>
		<category><![CDATA[online]]></category>
		<category><![CDATA[защита интернет-ресурсов]]></category>
		<category><![CDATA[интернет]]></category>
		<category><![CDATA[класс]]></category>
		<category><![CDATA[код]]></category>
		<category><![CDATA[кодинг]]></category>
		<category><![CDATA[ООП]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/programming/php/zashhita-internet-resursov-v-kartinkax/</guid>
		<description><![CDATA[Этой статьей мне хотелось бы открыть мою первую серию статей &#34;Джентельменский набор PHP программиста&#34;. Как и во всей остальной серии здесь пойдет речь о программировании на PHP для интернет-проектов, но в каждой статье я буду выбирать один узкий аспект и на протяжении всей статьи буду стараться показать возможные варианты его реализации и применения. Сегодня таким [...]]]></description>
			<content:encoded><![CDATA[<p>Этой статьей мне хотелось бы открыть мою первую <a href="/dzhentelmenskij-nabor-php-programmista/" target="_blank">серию статей &quot;Джентельменский набор PHP программиста&quot;</a>. Как и во всей остальной серии здесь пойдет речь о программировании на PHP для интернет-проектов, но в каждой статье я буду выбирать один узкий аспект и на протяжении всей статьи буду стараться показать возможные варианты его реализации и применения.</p>
<p>Сегодня таким аспектом станет защита интернет-ресурса от возможного возникновения нежелательного контента со стороны пользователей с помощью технологии captcha (точнее о &quot;графическом&quot; варианте ее реализации), о которой уже <a href="/net/otkuda-voznikaet-spam-i-kak-s-nim-borotsya/" target="_blank">неоднократно шла речь</a>.</p>
<p><span id="more-23"></span></p>
<p>Начать имеет смысл с небольшого напоминания о принципе работы этой технологии: перед потенциальным посетителем ставится некое препятствие, которое ему необходимо преодолеть для  продолжения работы с интернет-ресурсом. Существует множество <a target="_blank" href="/net/7-sposobov-zashhitit-svoj-internet-resurs-ot-nezhelatelnoj-informacii/">вариантов такого рода препятствий</a>. Как уже упоминалось, сегодня мы будем реализовывать только один наиболее распространенный тип &#8212; &quot;графический&quot;. В простейшем случае он представляет собой просьбу переписать с изображения некий набор символов. В процессе генерирования изображения, символы сильно искажаются с целью предотвращения возможности их распознавания любой программой с помощью технологии OCR.</p>
<h3>Подготовка</h3>
<p>Прежде чем начать писать код стоит более детально осознать какая же цель перед нами стоит: нам необходимо написать скрипт, генерирующий искаженное изображение некоторого набора символов и незаметно для пользователя передающее этот набор какому-либо другому скрипту, который нас пока мало интересует, но ясно лишь, что собственно проверкой будет заниматься именно он на основе данных полученных от пользователя и нашего скрипта. Способов исказить текст существует огромное количество, в ходе написания статьи постараюсь упомянуть несколько самых эффективных и широкоиспользуемых из них.</p>
<p>В первую очередь стоит подготовить некий каркас кода, который мы будем впоследствии заполнять. Он будет состоять из двух частей:</p>
<ol>
<li>Описание класса, генерирующего изображение</li>
<li>Файл, который будет вызываться browser&#8217;ом. В нем будет подключено описание нашего класса, выбор настроек данного конкретного изображения и выполнено создание объекта класса, в соответствии с выбранными настройками.</li>
</ol>
<p>Для начала давайте определимся со списком параметров, которые будет иметь наш класс. Во-первых, нужно решить какой текст будет генерироваться, самый простой и распространенный вариант &#8212; просто четыре цифры, я в примере на нем и остановлюсь, а реально же можно использовать абсолютно любые приходящие в голову варианты. Во-вторых, размеры изображения и текста &#8212; их лучше подобрать фиксированными так, чтобы было максимально читабельно, при минимальных размерах изображения, но при желании можно сделать и возможность изменения их извне. Последним в списке параметров будет цвет фона и текста &#8212; их как раз лучше задавать вне класса, так как основным действием, необходимым при переносе этого скрипта с одного сайт на другой &#8212; подбор используемых цветов таким образом, чтобы изображение смотрелось не очень ужасно при текущем варианте дизайна, изменения в других параметрах требуются на порядок реже.</p>
<p>Итак, создание объекта будем производить максимально простым способом, параметрами укажем белый и черный цвета.</p>
<pre lang="php">
<?php
require_once('captcha.class.php');	// указываем правильный путь к описанию класса
new Captcha(array(255,255,255),array(0,0,0));
?>
</pre>
<p style="font-size: 75%;">(для удобного чтения таких вставок исходного кода достаточно нажать на нее один раз левой кнопкой мыши и использовать стрелки &larr; и &rarr; на клавиатуре)</p>
<p>Заготовка для самого класса будет выглядеть примерно следующим образом (предположим, что он хранится в файле <u>captcha.class.php</u>):</p>
<pre lang="PHP">
<?php
class Captcha
{
   private $string;	// генерируемый текст
   private $bgcol;	// основной цвет фона
   private $fgcol;	// основной цвет текста
   private $height;	// высота изображения
   private $width;	// ширина изображения
   function __construct($bgcol,$fgcol)  // конструктор, вызывается при создании экземпляра класса
   {

   }
}
?>
</pre>
<h3>Задаем параметры</h3>
<p>Первым делом при создании объекта необходимо задать остальные параметры, размеры изображения можно указать прямо в конструкторе, а для генерации текста лучше написать отдельную функцию:</p>
<pre lang="PHP">
<?php
class Captcha
{
   private $string;	// генерируемый текст
   private $bgcol;	// основной цвет фона
   private $fgcol;	// основной цвет текста
   private $height;	// высота изображения
   private $width;	// ширина изображения
   function __construct($bgcol,$fgcol)  // конструктор, вызывается при создании экземпляра класса
   {
	$this->width=250;
	$this->height=80;
	$this->fgcol=$fgcol;
	$this->bgcol=$bgcol;
	$this->generateSymbols();
   }
   private function generateSymbols()   // генерация четырех цифр
   {
      $this->string=$this->leadingZero(rand()%10000,4);
   }
   private function leadingZero($num,$length) // дополнения числа num лидирующими нулями
   {						// до длины length
	$str=strrev($num);
	for($i=strlen($str);$i<$length;++$i)$str.="0";
	return strrev($str);
   }
}
?>
</pre>
<p>Этих данных нам должно хватить для написания функции, генерирующей изображение.</p>
<h3>Генерируем изображение</h3>
<p>Если забыть, что текст необходимо искажать, то функция, генерирующая изображение выглядела бы просто как:</p>
<pre lang="PHP">
private function generateImage()  // генерация изображения
{
   $im=@imagecreatetruecolor($this->width,$this->height);
   $bcol=imagecolorallocate($im,$this->bgcol[0],$this->bgcol[1],$this->bgcol[2]);
   $fcol=imagecolorallocate($im,$this->fgcol[0],$this->fgcol[1],$this->fgcol[2]);
   imagefill($im,0,0,$bcol);
   imagettftext($im,40,10,20,25,$fcol,"./font/font_name.ttf",$this->string));
   header('Content-Type: image/png');
   imagepng($im);
   imagedestroy($im);
}
</pre>
<p><span style="background-color: Yellow;">В данном методе используются функции модуля PHP под названием GD, основывающегося на одноименной библиотеке, убедитесь, что на Вашем хостинге этот модуль установлен.</span></p>
<p>Реально же ей пользоваться не стоит &#8212; такое изображение с легкостью поддается OCR. Полученный текст необходимо тем или иным образом исказить. Для вывода изображения используется формат PNG, но никто не мешает воспользоваться JPEG или GIF, для этого достаточно заменить везде png на название соответствующего формата.</p>
<h3>Искажаем текст</h3>
<p>Вот списочек тех, способов искажения текста, которыми я буду пользоваться в примере, пользоваться всеми сразу естественно никто не заставляет, да и включив воображение можно придумать много модификаций приведенных мной способов или абсолютно других:</p>
<ul>
<li><i>использование нестандартных шрифтов</i> &#8212; функция imagettftext позволяет использовать произвольный шрифт в формате Truetype, чем и необходимо воспользоваться. В Сети можно найти огромное количество бесплатных шрифтов в этом формате. По возможности стоит выбирать шрифты, максимально не похожие на любой стандартный, но при этом легко читающиеся.</li>
<li><i>использование нескольких шрифтов</i> &#8212; сделав подборку подходящих шрифтов, можно не останавливаться на каком-то одном, а сделать выбор текущего шрифта случайным из списка.</li>
<li><i>случайный выбор цветов</i> &#8212; усложняет работу OCR и в большинстве случаев не сильно мешает восприятию человеком.</li>
<li><i>случайное расположение символов</i> &#8212; еще один способ усложнить работу программам, пытающимся прочитать текст.</li>
<li><i>неравномерный фон</i> &#8212; изобразив на фоне какой-либо абстрактный набор любых фигур, можно заставить программу-посетителя подумать что какая-то часть из них является символом. Например, пересечение двух прямых линий часто распознается как буква T или L. Неплохим вариантом является написание на фоне других символов другим цветом, сильно отличающимся от основного и близким к цвету фона.</li>
</ul>
<p>Для начала этого вполне хватит, перейдем к реализации, в комментариях постараюсь указывать все особенности:</p>
<pre lang="PHP">
private function generateImage() // генерация изображения
{
   $im=@imagecreatetruecolor($this->width,$this->height);  // создаем пустое изображение
   $mcol=imagecolorallocate($im,$this->fgcol[0]+rand()%100+80,$this->fgcol[1]+rand()%30+150,$this->fgcol[2]-rand()%55); // выбираем случайным образом
   $kcol=imagecolorallocate($im,$this->fgcol[0]+rand()%100+80,$this->fgcol[1]+rand()%30+150,$this->fgcol[2]-rand()%20); // несколько цветов
   $lcol=imagecolorallocate($im,$this->bgcol[0]-rand()%20,$this->bgcol[1]-rand()%20,$this->bgcol[2]-rand()%20);
   $bcol=imagecolorallocate($im,$this->bgcol[0],$this->bgcol[1],$this->bgcol[2]);
   $fcol=imagecolorallocate($im,$this->fgcol[0],$this->fgcol[1],$this->fgcol[2]);
   imagefill($im,0,0,$bcol);  // заполняем изображение фоном
   $array=array(6,7,6,6,20,20,25,26,31,32,37,39,41); // список названий подходящих шрифтов
   $n=$array[rand()%count($array)];  // наугад выбираем из них один
   $m=rand()%50+1;
   $k=rand()%50+1;
   for($i=0;$i<$m;++$i)
   imageline($im,0,rand()%$this->height,$this->width,rand()%$this->height,$lcol); // создаем на фоне несколько линий
   for($i=0;$i<$k;++$i)
   imageline($im,rand()%$this->width,0,rand()%$this->width,$this->height,$lcol); // и еще несколько
   /*
   Генерируем текст: две строки на фон, а также интересующие нас символы по одному.
   */
   imagettftext($im,rand()%20+40,rand()%100-50,rand()%$this->height*0.8,rand()%50+25,$kcol,"./font/".$k.".ttf",$this->randomString(rand()%15));
   imagettftext($im,rand()%40+35,rand()%70-35,rand()%$this->height*0.8,rand()%25+25,$mcol,"./font/".$m.".ttf",$this->randomString(5+rand()%4));
   for($i=0;$i<strlen($this->string);++$i)
   imagettftext($im,rand()%10+33,rand()%70-35,15+$i*$this->width/5*1.1+rand()%5,rand()%7+$this->height*0.73,$fcol,"./font/".$n.".ttf",$this->string[$i]);
   for($i=0;$i<$m/10;++$i)
   imageline($im,0,rand()%$this->height,$this->width,rand()%$this->height,$mcol); // еще линии
   for($i=0;$i<$k/4;++$i)
   imageline($im,rand()%$this->width,0,rand()%$this->width,$this->height,$mcol);  // и еще немного
   for($i=0;$i<$k/6;++$i)
   imageline($im,rand()%$this->width,0,rand()%$this->width,$this->height,$fcol);  // и еще чуть-чуть
   header('Content-Type: image/png');
   imagepng($im);
   imagedestroy($im);
}
private function randomString($length)  // генерируем случайный набор символов заданной длины
{
  $list="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ!@#$%^&#038;**()-=_+.,<>/\|;:";
  for($i=0,$str="";$i<$length;++$i)$str.=substr($list,mt_rand(0,strlen($list)-1),1);
  return $str;
}
</pre>
<p>Стоит заметить, что конкретные цифры необходимо подбирать индивидуально, в примере они указаны абсолютно произвольно. Использование конкретно этих же цифр приведет к далеко не самым лучшим результатам.</p>
<h3>Сборка</h3>
<p>Не стоит забывать, что помимо генерации самого изображения, необходимо передать написанный текст другому скрипту, который будет сверять данные. Удобнее всего это делать через глобальный массив <span style="color: rgb(0, 0, 255);">$_SESSION</span>.</p>
<p>Собрав все написанное выше, и учтя передачу текста, можно получить следующий класс:</p>
<pre lang="PHP">
class Captcha
{
   private $string;	// генерируемый текст
   private $bgcol;	// основной цвет фона
   private $fgcol;	// основной цвет текста
   private $height;	// высота изображения
   private $width;	// ширина изображения
   function __construct($bgcol,$fgcol)  // конструктор, вызывается при создании экземпляра класса
   {
      $this->width=250;
      $this->height=80;
      $this->fgcol=$fgcol;
      $this->bgcol=$bgcol;
      $this->generateSymbols();
      $this->generateImage();
   }
   private function generateImage() // генерация изображения
   {
      $im=@imagecreatetruecolor($this->width,$this->height);  // создаем пустое изображение
      $mcol=imagecolorallocate($im,$this->fgcol[0]+rand()%100+80,$this->fgcol[1]+rand()%30+150,$this->fgcol[2]-rand()%55); // выбираем случайным образом
      $kcol=imagecolorallocate($im,$this->fgcol[0]+rand()%100+80,$this->fgcol[1]+rand()%30+150,$this->fgcol[2]-rand()%20); // несколько цветов
      $lcol=imagecolorallocate($im,$this->bgcol[0]-rand()%20,$this->bgcol[1]-rand()%20,$this->bgcol[2]-rand()%20);
      $bcol=imagecolorallocate($im,$this->bgcol[0],$this->bgcol[1],$this->bgcol[2]);
      $fcol=imagecolorallocate($im,$this->fgcol[0],$this->fgcol[1],$this->fgcol[2]);
      imagefill($im,0,0,$bcol);  // заполняем изображение фоном
      $array=array(6,7,6,6,20,20,25,26,31,32,37,39,41); // список названий подходящих шрифтов
      $n=$array[rand()%count($array)];  // наугад выбираем из них один
      $m=rand()%50+1;
      $k=rand()%50+1;
      for($i=0;$i<$m;++$i)
      imageline($im,0,rand()%$this->height,$this->width,rand()%$this->height,$lcol); // создаем на фоне несколько линий
      for($i=0;$i<$k;++$i)
      imageline($im,rand()%$this->width,0,rand()%$this->width,$this->height,$lcol); // и еще несколько
      /*
      Генерируем текст: две строки на фон, а также интересующие нас символы по одному.
      */
      imagettftext($im,rand()%20+40,rand()%100-50,rand()%$this->height*0.8,rand()%50+25,$kcol,"./font/".$k.".ttf",$this->randomString(rand()%15));
      imagettftext($im,rand()%40+35,rand()%70-35,rand()%$this->height*0.8,rand()%25+25,$mcol,"./font/".$m.".ttf",$this->randomString(5+rand()%4));
      for($i=0;$i<strlen($this->string);++$i)
      imagettftext($im,rand()%10+33,rand()%70-35,15+$i*$this->width/5*1.1+rand()%5,rand()%7+$this->height*0.73,$fcol,"./font/".$n.".ttf",$this->string[$i]);
      for($i=0;$i<$m/10;++$i)
      imageline($im,0,rand()%$this->height,$this->width,rand()%$this->height,$mcol); // еще линии
      for($i=0;$i<$k/4;++$i)
      imageline($im,rand()%$this->width,0,rand()%$this->width,$this->height,$mcol);  // и еще немного
      for($i=0;$i<$k/6;++$i)
      imageline($im,rand()%$this->width,0,rand()%$this->width,$this->height,$fcol);  // и еще чуть-чуть
      header('Content-Type: image/png');
      imagepng($im);
      imagedestroy($im);
   }
   private function generateSymbols()   // генерация четырех цифр
   {
      $this->string=$this->leadingZero(rand()%10000,4);
   }
   private function leadingZero($num,$length) // дополнения числа num лидирующими нулями
   {						// до длины length
      $str=strrev($num);
      for($i=strlen($str);$i<$length;++$i)$str.="0";
      return strrev($str);
   }
   private function randomString($length)  // генерируем случайный набор символов заданной длины
   {
      $list="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ!@#$%^&#038;**()-=_+.,<>/\|;:";
      for($i=0,$str="";$i<$length;++$i)$str.=substr($list,mt_rand(0,strlen($list)-1),1);
      return $str;
   }
}
</pre>
<p>Слегка доработав его и приведя в более подходящий вид, можно добиться генерации изображений, выглядящих например вот так:</p>
<p> <img style="margin: 6px; float: left;" title="Пример получающегося изображения" alt="CAPTCHA Sample" src="/wp-content/uploads/captcha-sample.png" />
<p>Специально не выкладываю уже доведенный до ума класс, чтобы у читателей не возникало желания просто взять и воспользоваться им, это приведет лишь к очередной серии captcha-клонов.</p>
<p>Для Вашего удобства предлагаю скачать используемые в примере <a href="/wp-content/uploads/source/captcha.class.phps">код класса</a> и <a href="/wp-content/uploads/source/captcha.phps">код вызываемого browser'ом файла</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/programmirovanie/php/zashhita-internet-resursov-v-kartinkax/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Откуда возникает спам и как с ним бороться</title>
		<link>http://www.insight-it.ru/set/otkuda-voznikaet-spam-i-kak-s-nim-borotsya/</link>
		<comments>http://www.insight-it.ru/set/otkuda-voznikaet-spam-i-kak-s-nim-borotsya/#comments</comments>
		<pubDate>Thu, 03 Jan 2008 17:25:32 +0000</pubDate>
		<dc:creator>Иван Блинков</dc:creator>
				<category><![CDATA[Сеть]]></category>
		<category><![CDATA[captcha]]></category>
		<category><![CDATA[crawler]]></category>
		<category><![CDATA[online]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[spider]]></category>
		<category><![CDATA[защита интернет-ресурсов]]></category>
		<category><![CDATA[интернет]]></category>
		<category><![CDATA[проверка]]></category>
		<category><![CDATA[спам]]></category>
		<category><![CDATA[электронная почта]]></category>

		<guid isPermaLink="false">http://www.insight-it.ru/?p=3</guid>
		<description><![CDATA[На сегодняшний день далеко не каждый пользователь Сети является человеком, возможно это покажется странным для не интересующегося ИТ читателя, но существует множество программ, способных передвигаться по сайтам, следуя по гипер-ссылкам, как внутри одного сайта, так и переходя с одного сайта на другой (в целом такой тип программ называется spider или crawler). Такие программы могут иметь [...]]]></description>
			<content:encoded><![CDATA[<p>На сегодняшний день далеко не каждый  пользователь <a href="/net" target="_blank">Сети</a> является человеком, возможно это покажется странным для не интересующегося ИТ читателя, но существует множество программ, способных передвигаться по сайтам, следуя по гипер-ссылкам, как внутри одного сайта, так и переходя с одного сайта на другой (в целом такой тип программ называется <a href="/tag/spider" target="_blank">spider</a> или <a href="/tag/crawler" target="_blank">crawler</a>). Такие программы могут иметь совершенно разное предназначение, самый распространенный пример: поисковые системы используют <a href="/tag/crawler" target="_blank">crawler</a>&#8216;ов для пополнения своих индексов, но, к сожалению, далеко не все программы этого класса написаны для благих целей.</p>
<p><span id="more-3"></span></p>
<h3>Good vs Evil</h3>
<p>Большая часть &quot;хороших&quot; <a href="/tag/spider" target="_blank">spider</a>&#8216;ов используется лишь для сбора информации о сайте и следуют пожеланиям владельцев сайтов, оставленных ими в специальном файле под названием robots.txt, либо внутри <a href="/tag/html" target="_blank">HTML</a>-разметки с помощью специально предназначенных для этого тэгов (этот механизм выходит за рамки данного повествования, так что позволю себе его пропустить, оставив как тему для отдельного разговора).</p>
<p>Но даже сбор информации во время автоматизированного путешествия программы по сайтам можно использовать в корыстных целях &#8212; на многих сайтах люди размещают свою контактную информацию для тех или иных целей, и некоторые сайты эту информацию &quot;публикуют&quot;. <a href="/tag/spider" target="_blank">Spider</a>, настроенный на сбор контактной информации (в основном адресов электронной почты и номеров ICQ и прочих служб обмена сообщениями) может в очень сжатые сроки насобирать длинный список адресов, пригодный, например, для рассылки нежелательной рекламы, в простонародье называемой <em><a href="/tag/spam" target="_blank">спам</a></em>. Избежать попадания своей контактной информации в такие списки относительно просто &#8212; достаточно лишь следить за тем, чтобы она либо не публиковалась, либо была защищена любым из простейших способов защиты от такого рода программ, начиная от банального требования регистрации для просмотра контактных данных пользователей, заканчивая выводом адресов через изображения или  шифрование посредством <em>JavaScript</em>.</p>
<p>Среди прочих функций, которые может выполнять такого рода программа, одной из наиболее часто используемых является возможность заполнения такой неотъемлемой составляющей практически любого сайта, как <em>формы</em>. Имея возможность заполнения существенно б<em>о</em>льшего количества форм в единицу времени, чем человек, такие программы служат основным источником <em><a href="/tag/spam" target="_blank">спама</a></em> в гостевых книгах, форумах и блогах. Еще одним из возможных применений автоматического заполнения форм является регистрация на множестве интернет-ресурсов с целью получения какой-либо выгоды, например регистрация сайтов в каталогах. Помимо этого <a href="/tag/crawler" target="_blank">crawler</a> перемещается по сайту с относительно высокой скоростью, что резко увеличивает нагрузку на <a href="/tag/server" target="_blank">сервер</a>, особенно при недостаточно оптимизированном движке сайта и/или недостатке ресурсов сервера, выделяемых на выполнение скриптов сайта.</p>
<h3>Защита форм от автоматического заполнения</h3>
<p>Наверняка многие из вас раньше слышали термин <a href="/tag/captcha" target="_blank">CAPTCHA</a>, но боялись спросить: что же он значит? Как не трудно догадаться этот термин является аббревиатурой <img src='http://www.insight-it.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . Расшифровывается она как &quot;<strong>C</strong>ompletely <strong>A</strong>utomated <strong>P</strong>ublic <strong>T</strong>uring test to tell <strong>C</strong>omputers and <strong>H</strong>umans <strong>A</strong>part&quot;. Для меня до сих пор остается загадкой по какому принципу выбирались слова для составления этой аббревиатуры, наверное тупо случайным образом <img src='http://www.insight-it.ru/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . Смысл этой фразы в переводе на русский можно передать как &quot;полностью автоматический способ отличить человека от компьютера&quot;. Конечно же имеется ввиду не внешние различия, а особенности их поведения на просторах сети Интернет. В роли &quot;компьютера&quot; в данном случае как раз выступают программы, о которых шла речь в самом начале.  Эта технология позволяет владельцам сайтов, желающих исключить (по крайней мере чисто теоретически, на практике же &#8212; минимизировать) посещение своего ресурса &quot;плохими&quot; &quot;компьютерами&quot;, крайне затруднить их использование.</p>
<p>В основе этой технологии лежит тот факт, что у программ в большинстве случаев отсутствует даже какое-либо подобие образного мышления &#8212; они следуют заранее четко определенному алгоритму. Существует множесво вариантов реализации защиты сайта с использованием этого недостатка компьютерных программ, но все они представляют некоторую проверку, предлагаемую пользователю и стремящуюся к удовлетворению следующего ряда требований:</p>
<ul>
<li>Современные компьютеры не должны иметь возможности точно ее пройти.</li>
<li>Она должна быть &quot;по зубам&quot; большинству людей.</li>
<li>Не должна полагаться на тот факт, что потенциальный &quot;злоумышленник&quot; просто не знаком с принципом работы данной проверки.</li>
</ul>
<p>Более подробно о возможностях этой <a href="/tag/tekhnologiya" target="_blank">технологии</a> можно узнать, прочитав <a href="/net/7-sposobov-zashhitit-svoj-internet-resurs-ot-nezhelatelnoj-informacii/">запись о нескольких вариантах ее реализации</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.insight-it.ru/set/otkuda-voznikaet-spam-i-kak-s-nim-borotsya/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

