<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Insight IT</title><link>https://www.insight-it.ru/</link><description></description><atom:link href="https://www.insight-it.ru/tag/postgresql/feed/index.xml" rel="self"></atom:link><lastBuildDate>Fri, 13 Apr 2012 20:11:00 +0400</lastBuildDate><item><title>Архитектура Instagram</title><link>https://www.insight-it.ru//highload/2012/arkhitektura-instagram/</link><description>&lt;p&gt;&lt;a href="https://www.insight-it.ru/goto/a8e562b3/" rel="nofollow" target="_blank" title="https://instagram.com/"&gt;Instagram&lt;/a&gt; - всего лишь &lt;a href="/tag/ios/"&gt;iOS&lt;/a&gt;, а теперь
и &lt;a href="/tag/android/"&gt;Android&lt;/a&gt;, приложение для обмена фотографиями с
друзьями. Последнее время находится на слуху благодаря новости о покупке
проекта &lt;a href="/tag/facebook/"&gt;Facebook&lt;/a&gt;'ом за кругленькую сумму. Недавно один
из основателей проекта, Mike Krieger, выступил на конференции с докладом
о техническом аспекте проекта, который я и хотел бы вкратце пересказать.&lt;/p&gt;
&lt;!--more--&gt;
&lt;h2 id="statistika"&gt;Статистика&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Начало:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1 сервер слабее Macbook Pro&lt;/li&gt;
&lt;li&gt;25к регистраций в первый день&lt;/li&gt;
&lt;li&gt;2 разработчика&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Сегодня:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;40+ миллионов пользователей&lt;/li&gt;
&lt;li&gt;100+ виртуальных серверов в EC2, в том числе:&lt;/li&gt;
&lt;li&gt;Проект куплен Facebook за &lt;em&gt;1 млрд. долл&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;1 миллион регистраций за 12 часов после запуска Android-версии&lt;/li&gt;
&lt;li&gt;5 разработчиков&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="tekhnologii"&gt;Технологии&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/tag/ubuntu/"&gt;Ubuntu&lt;/a&gt; &lt;a href="/tag/linux/"&gt;Linux&lt;/a&gt; 11.04&lt;/strong&gt; - основная
операционная система&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/tag/python/"&gt;Python&lt;/a&gt;&lt;/strong&gt; - основной язык программирования серверной части&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/tag/django/"&gt;Django&lt;/a&gt;&lt;/strong&gt; - фреймворк&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/amazon/"&gt;&lt;strong&gt;Amazon&lt;/strong&gt;&lt;/a&gt;:&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/tag/ec2/"&gt;EC2&lt;/a&gt;&lt;/strong&gt;&amp;nbsp;- хостинг&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/tag/elb/"&gt;ELB&lt;/a&gt;&lt;/strong&gt;&amp;nbsp;- балансировка входящих HTTP-запросов&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/tag/route53/"&gt;Route53&lt;/a&gt;&lt;/strong&gt; - DNS&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/tag/s3/"&gt;S3&lt;/a&gt;&lt;/strong&gt; - хранение фотографий&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/tag/cloudfront/"&gt;CloudFront&lt;/a&gt;&lt;/strong&gt; - &lt;a href="/tag/cdn/"&gt;CDN&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/tag/nginx/"&gt;nginx&lt;/a&gt;&lt;/strong&gt; - второй уровень балансировки входящихHTTP-запросов&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/tag/gunicorn/"&gt;gunicorn&lt;/a&gt;&lt;/strong&gt; - WSGI-сервер&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/haproxy/"&gt;&lt;strong&gt;HAProxy&lt;/strong&gt;&lt;/a&gt;&amp;nbsp;- балансировка нагрузки внутри системы&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/postgresql/"&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt;&lt;/a&gt; - основное хранилище данных&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/postgis/"&gt;&lt;strong&gt;postgis&lt;/strong&gt;&lt;/a&gt; - поддержка гео-запросов&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/pgfouine/"&gt;&lt;strong&gt;pgfouine&lt;/strong&gt;&lt;/a&gt; - отчеты на основе логов&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/tag/pgbouncer/"&gt;pgbouncer&lt;/a&gt;&lt;/strong&gt; - создание пула соединений&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/redis/"&gt;&lt;strong&gt;Redis&lt;/strong&gt;&lt;/a&gt; - дополнительное хранилище данных&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/memcached/"&gt;&lt;strong&gt;Memcached&lt;/strong&gt;&lt;/a&gt; - кэширование&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/gearman/"&gt;&lt;strong&gt;Gearman&lt;/strong&gt;&lt;/a&gt; - очередь задач&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/solr/"&gt;&lt;strong&gt;Solr&lt;/strong&gt;&lt;/a&gt; - гео-поиск&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/tag/munin/"&gt;munin&lt;/a&gt;&lt;/strong&gt;, &lt;a href="/tag/statsd/"&gt;&lt;strong&gt;statsd&lt;/strong&gt;&lt;/a&gt;, &lt;a href="/tag/pingdom/"&gt;&lt;strong&gt;pingdom&lt;/strong&gt;&lt;/a&gt; - мониторинг&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/fabric/"&gt;&lt;strong&gt;Fabric&lt;/strong&gt;&lt;/a&gt; - управление кластером&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/xfs/"&gt;&lt;strong&gt;xfs&lt;/strong&gt;&lt;/a&gt; - файловая система&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="filosofiia"&gt;Философия&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Простота&lt;/li&gt;
&lt;li&gt;Минимизация операционных издержек&lt;/li&gt;
&lt;li&gt;Использование подходящих инструментов&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="istoriia"&gt;История&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Забыли сделать &lt;strong&gt;favicon.ico&lt;/strong&gt; до запуска - в первый же день логи
пестрили ошибками 404&lt;/li&gt;
&lt;li&gt;Для хранения данных использовали просто &lt;strong&gt;Django &lt;a href="/tag/orm/"&gt;ORM&lt;/a&gt;&lt;/strong&gt; и
&lt;strong&gt;PostgreSQL&lt;/strong&gt; (из-за postgis)&lt;/li&gt;
&lt;li&gt;Начали с одного слабого сервера, после успешного запуска решили
переехать на &lt;strong&gt;EC2&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Довольно быстро пришлось вынести &lt;a href="/tag/subd/"&gt;СУБД&lt;/a&gt; на отдельный сервер
(виртуальный, естественно)&lt;/li&gt;
&lt;li&gt;Количество фотографий продолжало расти и расти, даже самый большой
инстанс &lt;strong&gt;EC2&lt;/strong&gt; не справлялся&lt;/li&gt;
&lt;li&gt;Решили вертикально разделить данные на несколько баз, с использованием
механизма &lt;strong&gt;routers&lt;/strong&gt; из ORM, параллельно избавившись от внешних ключей&lt;/li&gt;
&lt;li&gt;Через несколько месяцев суммарный размер базы данных перевалил за 60Гб и
перестало справляться и это решение&lt;/li&gt;
&lt;li&gt;Следующим шагом стало горизонтальное разбиение данных &lt;em&gt;(sharding)&lt;/em&gt;:&lt;/li&gt;
&lt;li&gt;Создали несколько тысяч логических баз данных.&lt;/li&gt;
&lt;li&gt;Распределили их по существенно меньшему количеству физических серверов (читай: виртуальных машин).&lt;/li&gt;
&lt;li&gt;Написали свой механизм определения где искать какую базу данных, с поддержкой миграции (вероятно тоже на основе routers).&lt;/li&gt;
&lt;li&gt;По последним данным под &lt;strong&gt;PostgreSQL&lt;/strong&gt; используется 12+12 виртуальных
машин с максимальной оперативной памятью (68.4Гб), а также сетевые диски
EBS, объединенные в программный RAID посредством mdadm. Это необходимо,
чтобы весь массив данных помещался в памяти, EBS не в состоянии
обеспечить достаточную производительность.&lt;/li&gt;
&lt;li&gt;С некоторыми задачами лучше справляется &lt;strong&gt;Redis&lt;/strong&gt;:&lt;/li&gt;
&lt;li&gt;Для каждого пользователя в Redis есть список идентификаторов новых
фотографий от других пользователей, на которых он подписан.&lt;/li&gt;
&lt;li&gt;При отображении потока новых для пользователя фотографий делается
выборка части такого списка, после чего посредством multiget достается
подробная о них информация из memcached.&lt;/li&gt;
&lt;li&gt;Пробовали возложить на него задачу хранения списков подписчиков, но в
итоге вернулись к решению на &lt;strong&gt;PostgreSQL&lt;/strong&gt; с небольшим кэшированием.&lt;/li&gt;
&lt;li&gt;В Redis также хранится информация о сессиях.&lt;/li&gt;
&lt;li&gt;Несколько фактов о Redis:&lt;ul&gt;
&lt;li&gt;Так как все находится в памяти - очень быстрые операции записи и работы с множествами.&lt;/li&gt;
&lt;li&gt;Является не заменой, а дополнением к основному хранилищу данных.&lt;/li&gt;
&lt;li&gt;Redis хорош для структур данных, которые относительно ограничены.&lt;/li&gt;
&lt;li&gt;Отлично подходит для кэширования комплексных структур данных, где нужно большее, чем просто получить значение по ключу (например - счетчики, подмножества, проверка вхождения в множества).&lt;/li&gt;
&lt;li&gt;Механизм репликации (посредством slaveof) позволяет легко
масштабировать операции чтения.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Пользователи синхронно загружают фотографии на медиа-сервер с
(опциональными) заголовком и месте на карте, все остальное происходит
асинхронно посредством очередей, например:&lt;ul&gt;
&lt;li&gt;Сохраняются гео-метки, обновляется &lt;strong&gt;Solr&lt;/strong&gt; (который впоследствии заменил postgis).&lt;/li&gt;
&lt;li&gt;Идентификатор нового фото добавляется в обсуждавшиеся выше списки для всех подписчиков автора.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Поначалу использовали &lt;a href="/tag/apache/"&gt;Apache&lt;/a&gt; + &lt;code&gt;mod_wsgi&lt;/code&gt; для запуска
&lt;strong&gt;Django&lt;/strong&gt;, впоследствии перешли к gunicorn из-за меньшего потребления
ресурсов и простоты настройки.&lt;/li&gt;
&lt;li&gt;С недавних пор начали использовать&amp;nbsp;&lt;strong&gt;Amazon ELB&lt;/strong&gt;&amp;nbsp;вместо &lt;strong&gt;DNS
round-robin&lt;/strong&gt; для первичной балансировки входяших HTTP-запросов, что
позволило:&lt;/li&gt;
&lt;li&gt;избежать необходимости дешифровки &lt;a href="/tag/ssl/"&gt;&lt;strong&gt;SSL&lt;/strong&gt;&lt;/a&gt; посредством nginx;&lt;/li&gt;
&lt;li&gt;ускорить исключение из балансировки проблемных серверов.&lt;/li&gt;
&lt;li&gt;Благодаря использованию &lt;strong&gt;xfs&lt;/strong&gt; есть возможность "замораживать" и
"размораживать" дисковые массивы при резервном копировании.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="podvodim-itogi"&gt;Подводим итоги&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Многие проблемы с масштабируемостью - результат банальных
    человеческих ошибок.&lt;/li&gt;
&lt;li&gt;Масштабирование = замена всех деталей в машине на скорости 150 км/ч.&lt;/li&gt;
&lt;li&gt;Заранее сложно узнать как в основном будут обращаться к данным, без
    реального использования.&lt;/li&gt;
&lt;li&gt;В первую очередь попытайтесь адаптировать известные Вам технологии и
    инструменты для создания простого и понятного решения, прежде чем
    бросаться на поиски чего-то нетривиального.&lt;/li&gt;
&lt;li&gt;Дополните свое основное хранилище более гибким компонентом, вроде
    Redis.&lt;/li&gt;
&lt;li&gt;Постарайтесь не использовать два инструмента для решения одной и той
    же задачи.&lt;/li&gt;
&lt;li&gt;Оставайтесь гибкими и ловкими = напоминайте себе о том, что на самом
    деле имеет значение.&lt;/li&gt;
&lt;li&gt;Разрабатывайте решения, к которым не придется постоянно возвращаться
    из-за их сбоев.&lt;/li&gt;
&lt;li&gt;Активное юнит- и функциональное тестирование стоят потраченного на
    них времени.&lt;/li&gt;
&lt;li&gt;DRY: не делайте одну и ту же работу несколько раз.&lt;/li&gt;
&lt;li&gt;Слабая связанность посредством уведомлений или сигналов позволяет
    легко менять структуру проекта.&lt;/li&gt;
&lt;li&gt;Дисковый ввод-вывод часто оказывается узким местом, особенно на EC2.&lt;/li&gt;
&lt;li&gt;Спускаться до C нужно только при необходимости, большую часть работы
    лучше делать в Python.&lt;/li&gt;
&lt;li&gt;Короткий цикл разработки - залог быстрого развития.&lt;/li&gt;
&lt;li&gt;Частые совместные рассмотрения кода нужны, чтобы все были в курсе
    происходящего.&lt;/li&gt;
&lt;li&gt;Не изобретайте велосипед.&lt;/li&gt;
&lt;li&gt;Окружите себя с толковыми &lt;a href="https://www.insight-it.ru/consulting/"&gt;консультантами&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Культура открытости вокруг разработки.&lt;/li&gt;
&lt;li&gt;Делитесь с &lt;a href="/tag/opensource/"&gt;opensource&lt;/a&gt; сообществом.&lt;/li&gt;
&lt;li&gt;Фокусируйтесь на том, что вы делаете лучше всего.&lt;/li&gt;
&lt;li&gt;Вашим пользователям абсолютно без разницы, написали ли Вы
    собственную СУБД или нет.&lt;/li&gt;
&lt;li&gt;Не переоптимизируйте и не предполагайте заранее как сайт будет
    расти.&lt;/li&gt;
&lt;li&gt;Не рассчитывайте, что "кто-то еще присоединится к команде и
    разберется с этим".&lt;/li&gt;
&lt;li&gt;Для социальных стартапов очень мало, или даже совсем нет, нерешимых
    вопросов, связанных с масштабируемостью.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="istochnik-informatsii"&gt;Источник информации&lt;/h2&gt;
&lt;p&gt;Упоминавшаяся во вступлении неприлично длинная презентация из 185
слайдов:&lt;/p&gt;
&lt;iframe data-aspect-ratio="" data-auto-height="true" frameborder="0" height="600" id="doc_73113" scrolling="no" src="//www.scribd.com/embeds/89025069/content?start_page=1&amp;amp;view_mode=scroll" width="100%"&gt;&lt;/iframe&gt;
&lt;p&gt;На видео, к сожалению, это выступление не записывалось.&lt;/p&gt;
&lt;p&gt;Часть информации взята из &lt;a href="https://www.insight-it.ru/goto/ce2d4e38/" rel="nofollow" target="_blank" title="http://instagram-engineering.tumblr.com/"&gt;технического блога Instagram&lt;/a&gt;.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Fri, 13 Apr 2012 20:11:00 +0400</pubDate><guid>tag:www.insight-it.ru,2012-04-13:highload/2012/arkhitektura-instagram/</guid><category>Amazon</category><category>Android</category><category>CloudFront</category><category>django</category><category>EC2</category><category>ELB</category><category>Fabric</category><category>Facebook</category><category>gearman</category><category>gunicorn</category><category>HAProxy</category><category>Intagram</category><category>iOS</category><category>Linux</category><category>Memcached</category><category>Munin</category><category>nginx</category><category>ORM</category><category>pgbouncer</category><category>pgFouine</category><category>Pingdom</category><category>postgis</category><category>PostgreSQL</category><category>Python</category><category>Redis</category><category>Route53</category><category>S3</category><category>Solr</category><category>statsd</category><category>Ubuntu</category><category>WSGI</category><category>xfs</category><category>Архитектура Instagram</category></item><item><title>Добро пожаловать в Сказку! (вакансия закрыта)</title><link>https://www.insight-it.ru//vacancy/2011/dobro-pozhalovat-v-skazku-vakansiya/</link><description>&lt;div class="card orange darken-3"&gt;
&lt;p&gt;&lt;div class="card-content white-text center"&gt;
&lt;strong&gt;Вакансия более не актуальна&lt;/strong&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Компания SKAZKA, российский разработчик и издатель
онлайн-игр, ищет опытного веб-разработчика на Python. Предлагается
работа в команде MMO-проекта "Королевство", а также
участие в разработке ряда новых игр (одна в активной разработке, а
другая проектируется).&lt;/p&gt;
&lt;h2 id="o-proekte"&gt;О проекте&lt;/h2&gt;
&lt;p&gt;Проект "Королевство" состоит из 2-х частей:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;непосредственно игра (сервер &amp;ndash; C+Python, клиент &amp;ndash; Flash)&lt;/li&gt;
&lt;li&gt;социальная сеть &amp;ndash; веб-часть проекта, содержащая все полагающиеся
    атрибуты соцсетей в реалиях игры (nginx, Python, Twisted, XSLT,
    memcached, PostgreSQL)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="obiazannosti"&gt;Обязанности&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Полная самостоятельная поддержка и разработка всех проектов компании
    со стороны веба&lt;/li&gt;
&lt;li&gt;Оптимизация существующего кода для увеличения производительности и
    уменьшения нагрузок&lt;/li&gt;
&lt;li&gt;Разработка модулей для существующего проекта&lt;/li&gt;
&lt;li&gt;Доработка функционала, фикс багов&lt;/li&gt;
&lt;li&gt;Интергация проектов с российскими и зарубежными соцсетями&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="trebovaniia-k-kandidatu"&gt;Требования к кандидату&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Уверенное знание Python&lt;/li&gt;
&lt;li&gt;Уверенное знание JavaScript&lt;/li&gt;
&lt;li&gt;Знание XSLT&lt;/li&gt;
&lt;li&gt;Умение разбираться в чужом коде&lt;/li&gt;
&lt;li&gt;Умение объективно оценивать собственные трудозатраты по той или иной
    задаче и укладываться в поставленные сроки&lt;/li&gt;
&lt;li&gt;Английский язык на уровне чтения технической документации&lt;/li&gt;
&lt;li&gt;Коммуникабельность, дисциплинированность и ответственность&lt;/li&gt;
&lt;li&gt;Приветствуется опыт работы над сложными/высоконагруженными
    web-проектами&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="usloviia-raboty"&gt;Условия работы&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Полный рабочий день в офисе в Москве (пять минут пешком от метро
    Третьяковская)&lt;/li&gt;
&lt;li&gt;Работа над интересными, перспективными проектами&lt;/li&gt;
&lt;li&gt;Отличная команда единомышленников, каждый из которых является
    профессионалом в своей области&lt;/li&gt;
&lt;li&gt;Начало рабочего дня: с 10.00 до 12.00 (по желанию)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Зарплата: 50-100 тыс. руб. в месяц&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="kuda-pisat"&gt;Куда писать?&lt;/h2&gt;
&lt;div class="card orange darken-3"&gt;
&lt;p&gt;&lt;div class="card-content white-text center"&gt;
&lt;strong&gt;Вакансия более не актуальна&lt;/strong&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;P.S.: Если Вы не дотягиваете до требований, но в Сказку хочется - все
равно пишите, еще есть вакансия джуниора.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Mon, 14 Mar 2011 15:32:00 +0300</pubDate><guid>tag:www.insight-it.ru,2011-03-14:vacancy/2011/dobro-pozhalovat-v-skazku-vakansiya/</guid><category>nginx</category><category>Python</category><category>Twisted</category><category>XSLT</category><category>memcached</category><category>PostgreSQL</category><category>JavaScript</category></item><item><title>Архитектура DISQUS</title><link>https://www.insight-it.ru//highload/2011/arkhitektura-disqus/</link><description>&lt;p&gt;&lt;img alt="DISQUS" class="left" src="https://www.insight-it.ru/images/disqus.jpg" title="DISQUS"/&gt;
&lt;a href="https://www.insight-it.ru/goto/a754581e/" rel="nofollow" target="_blank" title="https://disqus.com"&gt;DISQUS&lt;/a&gt; - самая популярная система
комментирования и одновременно самое большое в мире Django-приложение.
Она установлена более чем на полумиллионе сайтов и блогов, в том числе и
очень крупных, таких как Engadget, CNN, MTV, IGN. Основной особенностью
в её реализации является тот факт, что DISQUS не является тем сайтом,
который хотят увидеть пользователи, он лишь предоставляет механизмы
комментирования, авторизации и интеграции с социальными сетями. Пики
нагрузки возникают одновременно c появлением какой-то шумихи в
Интернете, что достаточно непредсказуемо. Как же им удается справляться
с этой ситуацией?&lt;!--more--&gt;&lt;/p&gt;
&lt;h2 id="platforma"&gt;Платформа&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="/tag/linux/"&gt;Linux&lt;/a&gt; - операционная система&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/python/"&gt;Python&lt;/a&gt; - язык программирования&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/django/"&gt;Django&lt;/a&gt; - основной framework&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/apache/"&gt;Apache 2.2&lt;/a&gt; +&amp;nbsp;&lt;a href="/tag/wsgi/"&gt;mod_wsgi&lt;/a&gt; - веб-сервер&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/postgresql/"&gt;PostgreSQL&lt;/a&gt; - СУБД&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/memcached/"&gt;memcached&lt;/a&gt; - кэширование&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/haproxy/"&gt;HAProxy&lt;/a&gt; - балансировка нагрузки&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/slony/"&gt;Slony&lt;/a&gt; - репликация данных&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/heartbeat/"&gt;heartbeat&lt;/a&gt; - обеспечение
    доступности&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="statistika"&gt;Статистика&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;До 17 тысяч запросов в секунду&lt;/li&gt;
&lt;li&gt;500 000 сайтов&lt;/li&gt;
&lt;li&gt;15 миллионов зарегистрированных пользователей&lt;/li&gt;
&lt;li&gt;75 миллионов комментариев&lt;/li&gt;
&lt;li&gt;250 миллионов посетителей (на август 2010г.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="osnovnye-trudnosti"&gt;Основные трудности&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Непредсказуемость нагрузки (основными причинами шумихи в Интернете
    являются катастрофы и выходки знаменитостей)&lt;/li&gt;
&lt;li&gt;Обсуждения никогда не теряют актуальность (нельзя держать в кэше все
    дискуссии с 2008 года)&lt;/li&gt;
&lt;li&gt;Нельзя угадать на каком сайте из тысяч возникнет пик трафика&lt;/li&gt;
&lt;li&gt;Персональные настройки, динамическое разбиение на страницы и
    сортировки снижают эффективность кэширования&lt;/li&gt;
&lt;li&gt;Высокая доступность (из-за разнообразия сайтов и их аудитории сложно
    запланировать технические работы)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="arkhitektura"&gt;Архитектура&lt;/h2&gt;
&lt;p&gt;&lt;img alt="Архитектура DISQUS" class="responsive-img" src="https://www.insight-it.ru/images/disqus_architecture.jpeg" title="Архитектура DISQUS"/&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Оборудование&lt;/strong&gt;, в сумме около 100 серверов:&lt;ul&gt;
&lt;li&gt;30% веб-серверов (Apache + &lt;code&gt;mod_wsgi&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;10% серверов баз данных (PostgreSQL)&lt;/li&gt;
&lt;li&gt;25% кэш-серверов (memcached)&lt;/li&gt;
&lt;li&gt;20% балансировка нагрузки и обеспечение доступности (HAProxy +
    heartbeat)&lt;/li&gt;
&lt;li&gt;15% прочие сервера (Python скрипты)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Балансировка нагрузки&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;HAProxy:&lt;ul&gt;
&lt;li&gt;Высокая производительность&lt;/li&gt;
&lt;li&gt;Интеллектуальная проверка доступности&lt;/li&gt;
&lt;li&gt;Неплохая статистика&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Репликация&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;Используется Slony-I&lt;/li&gt;
&lt;li&gt;Основана на триггерах&lt;/li&gt;
&lt;li&gt;Master/Slave для обеспечения большего объема операций чтения&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Высокая доступность&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;heartbeat&lt;/li&gt;
&lt;li&gt;Пассивная копия мастер баз данных на случай сбоя основной&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Партиционирование&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;Реализовано на уровне кода&lt;/li&gt;
&lt;li&gt;Простая реализация, быстрые положительные результаты&lt;/li&gt;
&lt;li&gt;Два метода разделения данных:&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Вертикальное:&lt;/em&gt;&lt;ul&gt;
&lt;li&gt;Создание нескольких таблиц с меньшим количеством колонок
    вместо одной (она же нормализация)&lt;/li&gt;
&lt;li&gt;Позволяет разделять базы данных&lt;/li&gt;
&lt;li&gt;Данные объединяются в коде (медленнее, чем на уровне
    СУБД, но не намного)&lt;/li&gt;
&lt;li&gt;Бартер производительности на масштабируемость&lt;/li&gt;
&lt;li&gt;Более эффективное кэшировние&lt;/li&gt;
&lt;li&gt;Механизм роутеров в Django позволяет достаточно легко
    реализовать данный функционал&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Горизонтальное:&lt;/em&gt;&lt;ul&gt;
&lt;li&gt;Некоторые сайты имеют очень большие массивы данных&lt;/li&gt;
&lt;li&gt;Партнеры требуют повышенного уровня доступности&lt;/li&gt;
&lt;li&gt;Помогает снижать загрузку по записи на мастер базе
    данных&lt;/li&gt;
&lt;li&gt;В основном используется все же вертикальное
    партиционирование&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Производительность базы данных&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;Особое внимание уделяется тому, чтобы индексы помещались в
    оперативную память&lt;/li&gt;
&lt;li&gt;Логирование медленных запросов (автоматизировано с помощью
    syslog-ng + pgFouine + cron)&lt;/li&gt;
&lt;li&gt;Использование пулов соединений (Django не умеет этого,
    используется pgbouncer, позволяет экономить на ресурсоемких
    операциях установления и прекращения соединений)&lt;/li&gt;
&lt;li&gt;Оптимизация QuerySet'ов:&lt;ul&gt;
&lt;li&gt;Не используется чистый SQL&lt;/li&gt;
&lt;li&gt;Встроенный кэш позволяет выделять части выборки&lt;/li&gt;
&lt;li&gt;Но это не всегда нужно, они убрали этот кэш&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Атомарные операции:&lt;ul&gt;
&lt;li&gt;Поддерживают консистентность данных&lt;/li&gt;
&lt;li&gt;Использование update(), так как save() не является
    thread-safe&lt;/li&gt;
&lt;li&gt;Отлично работают для таких вещей, как счетчики&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Транзакции:&lt;ul&gt;
&lt;li&gt;TransactionMiddleware поначалу использовалось, но со
    временем стало обузой&lt;/li&gt;
&lt;li&gt;В &lt;code&gt;postgrrsql_psycopg2&lt;/code&gt; есть опция autocommit:&lt;ul&gt;
&lt;li&gt;Это означает что каждый запрос выполняется в отдельной
    транзакции&lt;/li&gt;
&lt;li&gt;Обработка каждого пользовательского HTTP-запроса не
    начинает новую транзакцию&lt;/li&gt;
&lt;li&gt;Но все же транзакции из нескольких операций записи в
    СУБД нужны (сохранение нескольких объектов одновременно
    и полный откат в случае ошибки)&lt;/li&gt;
&lt;li&gt;В итоге все HTTP-запросы по-умолчанию начинаются в
    режиме autocommit, но в случае необходимости
    переключаются в транзакционный режим&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Отложенные сигналы&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;Постановка в очередь низкоприоритетных задач (даже если они не
    длинные по времени)&lt;/li&gt;
&lt;li&gt;Асинхронные сигналы очень удобны для разработчика (но не так,
    как настоящие сигналы)&lt;/li&gt;
&lt;li&gt;Модели отправляются в очередь в сериализованном виде&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Кэширование&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;Используется memcached&lt;/li&gt;
&lt;li&gt;Новый pylibmcна основе libmemcached в качестве клиента (проекты
    django-pylibmc и django-newcache)&lt;/li&gt;
&lt;li&gt;Настраиваемые алгоритмы поведения клиента&lt;/li&gt;
&lt;li&gt;Используется &lt;code&gt;_auto_reject_hosts&lt;/code&gt; и &lt;code&gt;_retry_timeout&lt;/code&gt; для
    предотвращения повторных подключений к вышедшим из строя
    кэш-серверам&lt;/li&gt;
&lt;li&gt;Алгоритм размещения ключей: консистентное хэширование на основе
    libketama&lt;/li&gt;
&lt;li&gt;Существует проблема, когда одно очень часто используемое
    значение в кэше инвалидируется:&lt;ul&gt;
&lt;li&gt;Множество клиентов одновременно пытаются получить новое
    значение из СУБД одновременно&lt;/li&gt;
&lt;li&gt;В большинстве случаев правильным решением было бы вернуть
    большинству устаревшие данные и позволить одному клиенту
    обновить кэш&lt;/li&gt;
&lt;li&gt;django-newcache и MintCache умеют это делать&lt;/li&gt;
&lt;li&gt;Заполнение кэша новым значением вместо удаления при
    инвалидации также помогает избежать этой проблемы&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Мониторинг&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;Информация о производительности запросов к БД, внешних вызовов и
    рендеринге шаблонов записывается через собственный middleware&lt;/li&gt;
&lt;li&gt;Сбор и отображение с помощью Ganglia&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Отключение функционала&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;Необходим способ быстро отключить новый функционал, если
    оказывается, что он работает не так, как планировалось&lt;/li&gt;
&lt;li&gt;Система должна срабатывать мгновенно, по всем серверам, без
    записи на диск&lt;/li&gt;
&lt;li&gt;Позволяет запускать новые возможности постепенно, лишь для части
    аудитории&lt;/li&gt;
&lt;li&gt;Позволяет постоянно использовать основную ветку кода&lt;/li&gt;
&lt;li&gt;Аналогичная система используется и в &lt;a href="/tag/facebook/"&gt;Facebook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Масштабирование команды разработчиков&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;Небольшая команда&lt;/li&gt;
&lt;li&gt;Месячная аудитория / количество разработчиков = 40 миллионов&lt;/li&gt;
&lt;li&gt;Это означает:&lt;ul&gt;
&lt;li&gt;Автоматическое тестирование&lt;/li&gt;
&lt;li&gt;И максимально простой процесс разработки&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Новый сотрудник может начать работать уже через несколько минут,
    нужно лишь:&lt;ul&gt;
&lt;li&gt;Установить и настроить PostgreSQL&lt;/li&gt;
&lt;li&gt;Скачать исходный код из &lt;a href="/tag/git/"&gt;git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;С помощью pip и virtualenv установить зависимости&lt;/li&gt;
&lt;li&gt;Изменить настройки в settings.py&lt;/li&gt;
&lt;li&gt;Выполнить автоматическое создание структуры данных
    средствами Django&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Непрерывное тестирование&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;Ежедневное развертывание с помощью &lt;a href="/tag/fabric/"&gt;Fabric&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="/tag/hudson/"&gt;Hudson&lt;/a&gt; обеспечивает регулярно осуществляет и
    тестирует сборки&lt;/li&gt;
&lt;li&gt;Интегрирован &lt;a href="/tag/selenium/"&gt;Selenium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Быстрое тестирование с помощью &lt;a href="/tag/pyflakes/"&gt;Pyflakes&lt;/a&gt; и
    post-commit hooks&lt;/li&gt;
&lt;li&gt;70 тысяч строк Python кода, 73% покрытие тестами, прогон всех
    тестов занимает 20 минут&lt;/li&gt;
&lt;li&gt;Собственная система исполнения тестов с поддержкой XML,
    Selenium, подсчета количества запросов, тестирования
    Master/Slave базы данных и интеграцией с очередью&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Отслеживание проблем и задач&lt;/strong&gt;:&lt;ul&gt;
&lt;li&gt;Переключились с Trac на Redmine (из-за поддержки под-задач)&lt;/li&gt;
&lt;li&gt;Отправка исключений на e-mail - плохая идея&lt;/li&gt;
&lt;li&gt;Раньше использовали django-db-log, но теперь опубликовали свою
    систему сбора ошибок и логов под названием
    &lt;a href="https://www.insight-it.ru/goto/2e33ac0/" rel="nofollow" target="_blank" title="https://github.com/dcramer/django-sentry"&gt;Sentry&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="delaem-vyvody"&gt;Делаем выводы&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Язык программирования, каким бы он ни был, не является проблемой&lt;/li&gt;
&lt;li&gt;Django в целом очень хорош (но приходится все же использовать набор
    собственных патчей)&lt;/li&gt;
&lt;li&gt;Даже при использовании низкопроизводительного framework можно
    построить масштабируемую систему&lt;/li&gt;
&lt;li&gt;Вертикальное партиционирование позволяет пожертвовать
    производительностью в пользу масштабируемости&lt;/li&gt;
&lt;li&gt;Даже небольшой командой разработчиков можно добиться высоких
    результатов, если не пренебрегать автоматизацией тестирования&lt;/li&gt;
&lt;li&gt;Большое значение имеет возможность вовремя отслеживать и оперативно
    реагировать на сбои&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="istochnik-informatsii"&gt;Источник информации&lt;/h2&gt;
&lt;p&gt;Данная статья написана на основе выступления Jason Yan и David Cramer на
DjangoConf 2010. В презентации можно найти примеры кода, ссылки на
упоминаемые проекты и дополнительные материалы:&lt;/p&gt;
&lt;div class="video-container no-controls"&gt;
&lt;iframe allowfullscreen="" frameborder="0" height="355" marginheight="0" marginwidth="0" scrolling="no" src="//www.slideshare.net/slideshow/embed_code/key/21F2PzBmYATx2Y" width="425"&gt; &lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;Другие статьи по масштабируемости высоконагруженных систем можно
почитать &lt;a href="https://www.insight-it.ru/highload/"&gt;в соответствующем разделе&lt;/a&gt;, а вовремя узнавать о
новых - &lt;a href="/feed/"&gt;подписавшись на RSS&lt;/a&gt;. Вчера, кстати, прикрутил DISQUS к
Insight IT, приглашаю постоянных читателей и всех остальных
потестировать :)&lt;/em&gt;&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Wed, 02 Mar 2011 03:37:00 +0300</pubDate><guid>tag:www.insight-it.ru,2011-03-02:highload/2011/arkhitektura-disqus/</guid><category>Apache</category><category>DISQUS</category><category>django</category><category>Fabric</category><category>Ganglia</category><category>Git</category><category>HAProxy</category><category>heartbeat</category><category>Hudson</category><category>Linux</category><category>Memcached</category><category>pgbouncer</category><category>pgFouine</category><category>PostgreSQL</category><category>Pyflakes</category><category>Python</category><category>Selenium</category><category>Slony</category><category>syslog-ng</category><category>WSGI</category><category>Архитектура DISQUS</category><category>Масштабируемость</category></item><item><title>Веб-сервер за два вечера</title><link>https://www.insight-it.ru//linux/2008/web-server-za-dva-vechera/</link><description>&lt;p&gt;&lt;img alt="Beastie" class="left" src="https://www.insight-it.ru/images/beastie.png" title="Beastie: The BSD Daemon"/&gt;
Многие из вас наверняка все еще помнят те времена, когда компьютерная
техника находилась лишь на ранней стадии своего развития. Позволить себе
иметь в личном распоряжении персональный компьютер мог далеко не каждый,
а о серверном оборудовании и вовсе не могло быть и речи.&lt;/p&gt;
&lt;p&gt;Но, к счастью, времена меняются, и на сегодняшний день покупка даже
серверного оборудования связана с достаточно скромными затратами,
сопоставимыми с бюджетом покупки настольного компьютера или ноутбука. Но
возникает другой вопрос - а что же с этим оборудованием делать? Вполне
логичным ответом было бы: "использовать по прямому назначению", о чем мы
с Вами сегодня и поговорим в компании с замечательным персонажем по
имени &lt;em&gt;&lt;a href="/tag/beastie/"&gt;Beastie&lt;/a&gt;&lt;/em&gt; и операционной системой
&lt;strong&gt;&lt;a href="/tag/freebsd/"&gt;FreeBSD&lt;/a&gt;&lt;/strong&gt;, с которой он частенько ассоциируется.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Под "использованием по прямому назначению" конечно же можно было
подразумевать множество разных применений, но я хотел все-таки
остановиться на варианте использования в роли веб-сервера, как
альтернативу многочисленным услугам по предоставлению shared и VPS
хостинга.&lt;/p&gt;
&lt;h3 id="predistoriia"&gt;Предистория&lt;/h3&gt;
&lt;p&gt;Некоторое время назад ко мне в руки попал простенький сервер, который
как раз предполагалось использовать как хостинг для одного из проектов.
Оставалось лишь сделать его пригодным для выполнения этой задачи.
Казалось бы дело это как минимум не тривиальное, но буквально через пару
дней мне довелось убедиться в обратном.&lt;/p&gt;
&lt;p&gt;Ассортимент оборудования, спрятанного внутри &lt;strong&gt;1U&lt;/strong&gt; корпуса, был вполне
стандартным, ничего особенного: процессор &lt;strong&gt;Intel Xeon 5335&lt;/strong&gt;,
оперативная память &lt;strong&gt;Kingston 2х2 GB ECC Full-buffered&lt;/strong&gt;, жесткий диск
изначально только один - &lt;strong&gt;WD 150 GB 10000rpm SATA&lt;/strong&gt;, а вот модель
материнской платы, к сожалению, на память назвать не могу, вроде что-то
от &lt;strong&gt;SuperMicro&lt;/strong&gt;, с простенькой встроенной видеокартой, сетевой картой
с двумя гигабитными Ethernet портами и встроенным же видимо software
RAID-контроллером. Опытный глаз наверняка заметил бы в этом списке
сильную недоукомплектацию, особенно проявляющуюся при упоминании
процессора в единственном числе, отсутствии RAID, и скромным объемам
оперативной памяти. Объясняется это достаточно просто - проект еще
предстоит тестировать перед запуском, а этой платформы для этого будет
более чем достаточно.&lt;/p&gt;
&lt;p&gt;Перед запуском проекта в открытое плавание естественно предстоит upgrade
оборудования.&lt;/p&gt;
&lt;h3 id="den-pervyi"&gt;День первый&lt;/h3&gt;
&lt;h4&gt;Подготовка&lt;/h4&gt;
&lt;p&gt;Если верить бумажкам, идущим в комплекте с сервером, на единственный
жестком диск в магазине установили демо-версию одной из серверных
операционных систем от одной мало кому известной корпорации. Смотреть
что это за зверь такой у меня особого желания не было, по-этому я не
долго думая пошел искать среди своей коллекции дистрибутивов болванку с
заранее выбранным &lt;a href="/tag/opensource/"&gt;opensource&lt;/a&gt; решением вопроса об
&lt;a href="/tag/os/"&gt;операционной системе&lt;/a&gt; - &lt;strong&gt;&lt;a href="/tag/freebsd/"&gt;FreeBSD 6.2&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Почему выбор пал именно на эту &lt;a href="/tag/os/"&gt;ОС&lt;/a&gt; объяснить не так уж и
просто, но я все же попробую. Выбор был достаточно классический:
&lt;em&gt;&lt;a href="/tag/unix/"&gt;Unix&lt;/a&gt; vs &lt;a href="/tag/linux/"&gt;Linux&lt;/a&gt;&lt;/em&gt;, возникали еще некоторые
сомнения насчет решений от &lt;em&gt;Sun&lt;/em&gt; в виде &lt;em&gt;Solaris&lt;/em&gt; и &lt;em&gt;OpenSolaris&lt;/em&gt;, но от
них я отказался достаточно быстро в основном из-за более чем скромной
документации и проприетарного происхождения, попутно закрыв глаза на все
положительные отзывы, которые я видел в Сети.&lt;/p&gt;
&lt;p&gt;Так как мне хотелось иметь иметь перед собой &lt;em&gt;конструктор&lt;/em&gt; для сбора
системы именно таким образом, как было бы удобно мне, а не разработчикам
дистрибутива, то список вариантов, выступавших на стороне
&lt;a href="/tag/linux/"&gt;Linux&lt;/a&gt; быстро начал сокращаться, начиная с &lt;strong&gt;CentOS&lt;/strong&gt;.
Предпоследним вычеркнутым из списка дистрибутивов &lt;a href="/tag/linux/"&gt;Linux&lt;/a&gt;
был &lt;strong&gt;Debian&lt;/strong&gt;, что оставило в нем лишь &lt;a href="/tag/gentoo/"&gt;Gentoo Linux&lt;/a&gt;.
Финальный выбор между &lt;a href="/tag/freebsd/"&gt;FreeBSD&lt;/a&gt; и &lt;a href="/tag/gentoo/"&gt;Gentoo&lt;/a&gt;
был сделан уже легче: во-первых, по &lt;a href="https://www.insight-it.ru/linux/2008/gentoo-linux-sony-vaio/"&gt;своему опыту с ноутбуком&lt;/a&gt; я уже понял, что с
&lt;a href="/tag/gentoo/"&gt;Gentoo&lt;/a&gt; предстояло бы немало хлопот, а, во-вторых, в новый
&lt;em&gt;конструктор&lt;/em&gt;, как ни крути, "играть" намного интереснее, чем в старый,
так что долго думать не пришлось :)&lt;/p&gt;
&lt;h4&gt;Установка&lt;/h4&gt;
&lt;p&gt;Найдя наконец диск с &lt;a href="/tag/freebsd/"&gt;FreeBSD&lt;/a&gt;, я попытался решить
следующий возникший вопрос: а как же установить операционную систему с
компакт-диска на компьютер, не имеющий соответствующего привода? Так как
сервер был запломбирован и находился на гарантии, вариант частично
разобрать и подключить обычный привод отпал сразу же, ровно как и
вариант с подключением внешнего привода по причине его отсутствия.
Подходящее решение было найдено практически сразу же, благо жесткие
диски подключались по принципу hotswap: вытащив жесткий диск без
развинчивания корпуса, я подключил его к подвернувшемуся под руку
настольному компьютеру, обладающему DVD-приводом. Загрузка прошла
успешно и я приступил к установке, руководствуясь &lt;a href="https://www.insight-it.ru/goto/358a772c/" rel="nofollow" target="_blank" title="http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/book.html"&gt;FreeBSD Handbook&lt;/a&gt;,
пересказывать его особого желания у меня нет, остановлюсь лишь на
некоторых особенностях этого процесса.&lt;/p&gt;
&lt;p&gt;Первым этапом установки, где пришлось задуматься, был &lt;strong&gt;fdisk&lt;/strong&gt;
(разбиение диска на так называемые &lt;em&gt;slice&lt;/em&gt;). Для избежания путаницы для
самого себя, я решил, что размещу рабочие директории http-сервера и базы
данных в &lt;strong&gt;/var&lt;/strong&gt;, которую и выделил в отдельный &lt;em&gt;slice&lt;/em&gt;, занимающий
б&lt;em&gt;о&lt;/em&gt;льшую часть доступного дискового пространства. В ассортимент
доступного при установке программного обеспечения я особо вникать не
стал, так как знал, что у меня всегда будет возможность заняться им
позже, и как следствие этого выбрал что-то очень близкое к стандартному
набору &lt;a href="/tag/po/"&gt;ПО&lt;/a&gt;.
Подтвердив установку и подождав достаточно непродолжительный период
времени, я перезагрузил систему, вытащив установочный диск в процессе.
Установка оказалась на удивление элементарной, что привело к полученной
с первой попытки работоспособной системе. Увидев долгожданное
приглашение к вводу логина и пароля я убедился, что могу
беспрепятственно получить доступ к консоли и сразу же выключил систему,
чтобы перенести жесткий диск обратно на сервер.&lt;/p&gt;
&lt;p&gt;Так как сетевое подключение еще только предстояло настроить, то на
сервер переносить пришлось не только жесткий диск, но и монитор с
клавиатурой. На новом оборудовании все так же прекрасно запустилось, и я
принялся за настройку подключения. Особых проблем не возникло - в
&lt;a href="https://www.insight-it.ru/goto/ecbcc1a6/" rel="nofollow" target="_blank" title="http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/book.html#NETWORK-COMMUNICATION"&gt;Handbook&lt;/a&gt;'е
все более чем качественно задокументировано, самым сложным был процесс
выбора драйвера, вернее осознавание того, что он изначально правильно
сам установился. Следующей маленькой проблемой было угадывание какой же
из &lt;em&gt;Ethernet&lt;/em&gt;-портов был только что настроен, и, соответственно,
подключение кабеля именно в него, а не в его соседа. После завершения
всех манипуляций я с радостью обнаружил, что &lt;strong&gt;ping&lt;/strong&gt; от сервера до
gateway'а успешно проходит, что по сути и означало окончание настройки
сетевого подключения.
Следущей целью было избавить себя от необходимости пользоваться
позаимствованными у другого компьютера клавиатурой и монитором. Дело
тоже оказалось достаточно нехитрым, &lt;strong&gt;sshd&lt;/strong&gt; установился и настроился
вполне самостоятельно где-то в процессе установки, от меня потребовалось
лишь создать дополнительного пользователя, написать нехитрую строчку в
&lt;strong&gt;rc.conf&lt;/strong&gt;: &lt;code&gt;sshd_enable="YES"&lt;/code&gt; и собственно запустить daemon'а. Этого
было вполне достаточно, чтобы набрав на своем &lt;a href="/tag/noutbuk/"&gt;ноутбуке&lt;/a&gt;
&lt;strong&gt;ssh&lt;/strong&gt; в консоли, с указанием необходимых параметров, получить
удаленный доступ к серверу по протоколу &lt;strong&gt;SSH&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Решив, что для начала этого будет вполне достаточно, я отправился по
другим делам, так как тот вечер еще даже не успел подойти к своему
завершению.&lt;/p&gt;
&lt;h3 id="den-vtoroi"&gt;День второй&lt;/h3&gt;
&lt;h4&gt;Программное обеспечение&lt;/h4&gt;
&lt;p&gt;Хорошо, вполне работоспособную &lt;a href="/tag/os/"&gt;операционную систему&lt;/a&gt; мы
получили. Осталось снабдить ее необходимым &lt;a href="/tag/po/"&gt;программным обеспечением&lt;/a&gt; для выполнения своих обязанностей, определенных
нами заранее.&lt;/p&gt;
&lt;p&gt;Прежде чем что-либо устанавливать, очень не пожалел, что ознакомился как
с соответствующим &lt;a href="https://www.insight-it.ru/goto/6ffb90d6/" rel="nofollow" target="_blank" title="http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/book.html#PORTS"&gt;разделом handbook'a&lt;/a&gt;,
так и с &lt;a href="https://www.insight-it.ru/goto/a3e92771/" rel="nofollow" target="_blank" title="http://www.freebsd.org/ports/"&gt;доступным ассортиментом ПО&lt;/a&gt;.
После этого я перешел-таки собственно к выбору и установке
&lt;a href="/tag/po/"&gt;ПО&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Так как одной из основных составных частей практически любого
    веб-сервера является http-daemon, именно с его выбора я и решил
    начать. Причем начал еще задолго до описываемых событий, вся
    многофункциональность &lt;a href="/tag/apache/"&gt;Apache&lt;/a&gt; мне была не нужна, а
    аналоги &lt;code&gt;mod_auth&lt;/code&gt; и &lt;code&gt;mod_rewrite&lt;/code&gt; есть и в более легких
    http-серверах. Cамо веб-приложение, которое там предполагалось
    располагать, работает по большей части на &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt;, так что
    ничего особенного от httpd совсем не требовалось. В итоге финальный
    выбор был между &lt;em&gt;быстрыми&lt;/em&gt; и &lt;em&gt;легкими&lt;/em&gt; вариантами: &lt;strong&gt;&lt;a href="/tag/nginx/"&gt;nginx&lt;/a&gt;&lt;/strong&gt; и &lt;strong&gt;&lt;a href="/tag/lighttpd/"&gt;lighttpd&lt;/a&gt;&lt;/strong&gt;, какой-либо весомой причины по которой я выбрал &lt;strong&gt;&lt;a href="/tag/lighttpd/"&gt;lighttpd&lt;/a&gt;&lt;/strong&gt; с &lt;code&gt;mod\_fastcgi&lt;/code&gt; привести
    не могу, основным фактором был мой некоторый опыт работы с ним в
    прошлом, и отсутствие такового в отношении &lt;strong&gt;&lt;a href="/tag/nginx/"&gt;nginx&lt;/a&gt;&lt;/strong&gt;. Установка
    прошла легко и непринужденно с помощью в сжатые сроки найденного в
    &lt;a href="/tag/google/"&gt;Google&lt;/a&gt;
&lt;a href="https://www.insight-it.ru/goto/14d0519d/" rel="nofollow" target="_blank" title="http://www.cyberciti.biz/faq/howto-setup-lighttpd-fastcgi-php-server/"&gt;мануала&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Другим немаловажным компонентом сервера является &lt;strong&gt;ftpd&lt;/strong&gt;, как
    известно используемый для передачи файлов. Собственно говоря, если
    активное его использование не планируется, то особого значения какой
    именно сервер будет использоваться значения не имеет: любой из
    доступных устанавливается настраивается в пару простых шагов без
    каких-либо проблем (если это имеет значение - я выбрал &lt;strong&gt;vsftpd&lt;/strong&gt;,
    так как мне уже далеко не один раз доводилось его настраивать на
    домашних компьютерах, и, как следствие, даже инструкция не
    понадобилась). Но при потенциальной возможности работы через
    Интернет, этот протокол является достаточно уязвимым, так как не
    использует никакого шифрования. Эта проблема решается с помощью
    механизма &lt;em&gt;FTP over SSH&lt;/em&gt;, который представляет собой использование
    &lt;strong&gt;SSH&lt;/strong&gt; в роли туннеля для передачи файлов по &lt;strong&gt;FTP&lt;/strong&gt;. О том, как
    воспользоваться этим механизмом вам подскажет &lt;strong&gt;man ssh&lt;/strong&gt;,
    какой-либо дополнительной конфигурации он не требует, разве что
    настройки соответствующим образом firewall'а, но об этом я расскажу
    позже.&lt;/li&gt;
&lt;li&gt;Сам &lt;a href="/tag/php/"&gt;PHP&lt;/a&gt; установлен последней доступной в
    &lt;a href="https://www.insight-it.ru/goto/a3e92771/" rel="nofollow" target="_blank" title="http://www.freebsd.org/ports/"&gt;ports&lt;/a&gt; версии и , как уже
    упоминалось, был подключен к &lt;a href="/tag/lighttpd/"&gt;lighttpd&lt;/a&gt; с помощью
    &lt;code&gt;mod_fastcgi&lt;/code&gt;, какой-либо дополнительной конфигурации с моей
    стороны не потребовалось, я разве что выбрал список модулей (в
    общем-то тоже занятие не сложное, достаточно лишь осознавать какие
    именно используются, плюс я еще решил
    &lt;strong&gt;&lt;a href="https://www.insight-it.ru/goto/41a25ac6/" rel="nofollow" target="_blank" title="http://www.hardened-php.net/suhosin/"&gt;Suhosin&lt;/a&gt;&lt;/strong&gt; установить) и
    просто просмотрел по диагонали все конфиги (в основном сам
    &lt;strong&gt;php.ini&lt;/strong&gt; и &lt;strong&gt;lighttpd.conf&lt;/strong&gt;) на предмет их соответствия
    потребностям моего приложения. Отдельная история возникла с лишь
    одним модулем - &lt;em&gt;&lt;a href="/tag/blitz/"&gt;Blitz&lt;/a&gt;&lt;/em&gt;, который на данный момент все
    еще отсутствует в репозиториях как &lt;a href="/tag/freebsd/"&gt;FreeBSD&lt;/a&gt;, так и
    подавляющего большинства (если не всех) дистрибутивов
    &lt;a href="/tag/linux/"&gt;Linux&lt;/a&gt;. Его пришлось устанавливать вручную из
    исходников по &lt;a href="https://www.insight-it.ru/goto/1e985790/" rel="nofollow" target="_blank" title="http://alexeyrybak.com/blitz/blitz_ru.html#install_config.install"&gt;соответствующему мануалу&lt;/a&gt;,
    что правда тоже дело не хитрое и заняло всего несколько минут.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="/tag/subd/"&gt;СУБД&lt;/a&gt;&lt;/strong&gt; особо выбирать не пришлось - приложение
    написано было с расчетом на &lt;strong&gt;&lt;a href="/tag/postgresql/"&gt;PostrgeSQL&lt;/a&gt;&lt;/strong&gt;, ее
    соответственно и прикручивал к &lt;strong&gt;&lt;a href="/tag/php/"&gt;PHP&lt;/a&gt;.&lt;/strong&gt; Этот этап был
    пожалуй одним из самых проблематичных, так как сразу после
    классического &lt;code&gt;make install clean&lt;/code&gt; соответствующий daemon
    запускаться отказался. Какого-либо осознанного сообщения об ошибке
    &lt;code&gt;/usr/local/etc/rc.d/postgresqld start&lt;/code&gt; не выводило как в консоль,
    так и в логи, но тем не менее консольный клиент &lt;strong&gt;psql&lt;/strong&gt; и само
    веб-приложение жаловались на отсутствие запущенной
    &lt;a href="/tag/subd/"&gt;СУБД&lt;/a&gt;. Этот факт сильно затруднял поиск возможных
    вариантов решения на просторах &lt;a href="/tag/internet/"&gt;Сети&lt;/a&gt;, так что не
    найдя ничего полезного я решил заняться диагностикой проблемы и
    поиском решения для нее самостоятельно. Методом проб и ошибок,
    перебрав множество возможных вариантов запуска daemon'а, я пришел к
    выводу, что у пользователя от имени которого он должен был
    запускаться явно проблемы с доступом к файловой системе. Видимо так
    получилось из-за нестандартного расположения самой базы данных - в
    директории &lt;strong&gt;/var&lt;/strong&gt;. Не смотря на тот факт, что &lt;strong&gt;chown&lt;/strong&gt; и
    &lt;strong&gt;chmod&lt;/strong&gt; были использованы по прямому назначению в отношении
    соответствующих директорий для установления прав доступа. В итоге
    оказалось, что директория указанная для этого пользователя как
    домашняя (по памяти пишу, могу ошибиться, но вроде
    &lt;strong&gt;/usr/local/pgsql&lt;/strong&gt;) по каким-то причинам не создалась и
    соответственно именно этот факт и мешал запуску daemon'а.
    Восстановив справедливость в отношении этого пользователя, я
    обнаружил, что &lt;strong&gt;&lt;a href="/tag/postgresql/"&gt;PostrgeSQL&lt;/a&gt;&lt;/strong&gt; успешно
    запустился-таки, а мое приложение тоже стало функционировать именно
    так, как ему было положено. Проверив содержимое соответствующего
    конфига, я решил его больше не трогать, а то как говорится
    "premature optimization is the root of all evil"&amp;amp;copyright;. За
    компанию решил установить веб-интерфейс к
    &lt;a href="/tag/postgresql/"&gt;PostrgeSQL&lt;/a&gt; - &lt;strong&gt;phppgadmin&lt;/strong&gt;. Собравшись из
    &lt;a href="https://www.insight-it.ru/goto/a3e92771/" rel="nofollow" target="_blank" title="http://www.freebsd.org/ports/"&gt;портов&lt;/a&gt;, он повел себя как-то не
    очень адекватно, совсем не так каким я привык его видеть у себя на
    &lt;a href="/tag/noutbuk/"&gt;ноутбуке&lt;/a&gt;, разбираться в причинах было не охота -
    простое копирование и замена соответствующей директории по &lt;em&gt;ftp&lt;/em&gt;
    буквально за минуту решило проблему.&lt;/li&gt;
&lt;li&gt;Вариантов фильтров сетевого трафика в &lt;a href="/tag/freebsd/"&gt;FreeBSD&lt;/a&gt;
    имеется предостаточно:
    &lt;a href="https://www.insight-it.ru/goto/b7a4c2f7/" rel="nofollow" target="_blank" title="http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/book.html#FIREWALLS-PF"&gt;pf&lt;/a&gt;,
    &lt;a href="https://www.insight-it.ru/goto/963bd5ec/" rel="nofollow" target="_blank" title="http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/book.html#FIREWALLS-IPF"&gt;ipf&lt;/a&gt;,
    &lt;a href="https://www.insight-it.ru/goto/aee50277/" rel="nofollow" target="_blank" title="http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/book.html#FIREWALLS-IPFW%22"&gt;ipfw&lt;/a&gt;.
    Опыта работы ни с одним из них у меня не было, так что выбор
    происходил из достаточно субъективных критериев - очевидности
    принципов работы правил и достаточности документации. Так как я был
    уверен, что каждый из них сможет обеспечить достаточный уровень
    безопасности, основываясь на указанных выше критериях в итоге я
    выбрал &lt;strong&gt;ipf&lt;/strong&gt;. Документация позволила легко и непринужденно все
    установить и настроить, правда за компанию пришлось разбираться и с
    &lt;a href="https://www.insight-it.ru/goto/420d629d/" rel="nofollow" target="_blank" title="http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/book.html#KERNELCONFIG"&gt;пересборкой ядра&lt;/a&gt;.
    В качестве базы для построения собственного списка правил я
    использовал приведенный все там же, в документации, пример. Само
    собой пришлось доработать его под конкретную систему, но методом
    проб и ошибок эта задача выполняется достаточно быстро &lt;em&gt;(будте
    осторожны с 22 портом, используемым для SSH - очень легко на этом
    этапе случайно заблокировать самому себе доступ к серверу)&lt;/em&gt;.
    Получившийся в итоге список правил приводить не буду, так как его
    еще предстоит довести до ума на активно работающей системе.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="zakliuchenie"&gt;Заключение&lt;/h3&gt;
&lt;p&gt;Не прошло и двух дней, как из простого набора оборудования получился
вполне готовый к работе веб-сервер, конечно же доводить до ума его
придется еще достаточно долго, но просто стабильно работать он был в
состоянии уже тогда. Дальше его отвезли в место постоянного его
прибывания, подключили к более-менее приличному интернет-каналу, с моей
стороны при этом потребовалось лишь слегка поменять настройки сетевого
подключения, и вот - он уже доступен из Сети. Практически сразу
же обнаружился один мой недочет в плане выбора &lt;a href="/tag/po/"&gt;ПО&lt;/a&gt; - буквально
в первую же ночь после открытия публичного доступа к серверу нашлась
масса желающих попытаться подобрать по словарю логин и пароль для
доступа к серверу по SSH, но он был открыт лишь для одной учетной
записи, у которой было мягко говоря нестандартное имя пользователя, даже
его никто за ночь не смог угадать, а до более чем 20-символьного пароля
дело так и не дошло. На следующее утро я, не долго думая, установил
программу под названием &lt;strong&gt;&lt;a href="https://www.insight-it.ru/goto/d4707261/" rel="nofollow" target="_blank" title="http://www.freebsd.org/cgi/url.cgi?ports/security/sshguard/pkg-descr"&gt;sshguard&lt;/a&gt;&lt;/strong&gt;, которая сразу же предотвратила все последующие попытки подобным образом издеваться над сервером. Дальше надо было настроить запись на
DNS-сервере для ассоциации домена с IP нашего сервера, настроить почту,
закончить работу над самим веб-приложением и много чего еще, но это уже
совсем другая история.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Thu, 14 Feb 2008 15:59:00 +0300</pubDate><guid>tag:www.insight-it.ru,2008-02-14:linux/2008/web-server-za-dva-vechera/</guid><category>Beastie</category><category>FreeBSD</category><category>lighttpd</category><category>PostgreSQL</category><category>Unix</category><category>веб-сервер</category><category>операционная система</category><category>сервер</category><category>системное администрирование</category></item><item><title>Общаемся с базой данных</title><link>https://www.insight-it.ru//php/2008/obshhaemsya-s-bazojj-dannykh/</link><description>&lt;p&gt;На этот раз хочется обсудить такой одновременно важный и несложный
момент в реализации работы любого интернет-проекта, как координации
работы Ваших скриптов с СУБД.&lt;/p&gt;
&lt;p&gt;Если подойти к этому вопросу "в лоб", то код станет очень неудобен как
для понимания, так и для использования: код станет переполнен различными
функциями соединения с БД, отправки запросов, преобразования результатов
запросов в массивы PHP, подсчета строк, которые затронул запрос, а также
многие и многие другие.&lt;/p&gt;
&lt;p&gt;Для желающих минимизировать подобного рода издержки в процессе написания
кода, хочу предложить один из, на мой взгляд, самых эффективных способов
решения этой проблемы.&lt;/p&gt;
&lt;!--more--&gt;
&lt;p&gt;Этим способом будет являться написание класса, реализующего интерфейс
между СУБД и PHP-скриптами. Для начала стоит определиться с
ассортиментом функций, которые будет призван выполнять наш класс:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;установка соединения&lt;/em&gt;, а также проверка успешности выполнения этого
    действия;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;отправка запросов&lt;/em&gt;, как заданных извне так и, возможно, из
    какого-либо ассортимента заранее написанных запросов;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;обработка результатов запросов&lt;/em&gt;, не ограничивающаяся одним SELECT,
    должны быть предоставлены методы обработки результатов любых видов
    запросов (или хотябы большинства).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Вполне очевидным является тот факт, что методы этого класса будут
использоваться практически повсеместно в большинстве проектов.
Вследствии чего становится нецелесообразным создание объекта нашего
класса и передача его по всем функциям и методам всех скриптов, в таких
случае намного предпочтительнее делать владельцем методов и переменных
сам класс, а не экземпляр класса, с помощью ключевого слова &lt;strong&gt;static&lt;/strong&gt;.
Это позволит пользоваться услугами нашего класса из любого места кода.
Приступим-с собственно к кодингу, начать стоит с заготовки пустого
класса:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SQL&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;В зависимости от предпочитаемой Вами СУБД набор конкретных функций,
используемых в реализации нашего класса, будет вариироваться. В
большинстве случаев предпочитаю пользоваться PostgreSQL, на это причин у
меня несколько, но это тема для отдельного разговора. Благодаря этому
факту приводимый в качестве примера код будет использовать функции для
работы именно с этой СУБД. Для поклонников же других этот систем вопрос
в подавляющем большинстве случаев заключается лишь в замене этих функций
на аналогичные из других модулей PHP, например для популярной и
широкораспространенной MySQL достаточно будет всеголишь пройтись
автозаменой &lt;strong&gt;pg_ =&amp;gt; mysql_&lt;/strong&gt; и слегка подредактировать параметры
некоторых функций.&lt;/p&gt;
&lt;p&gt;Перейдем к реализации установления соединения с СУБД, не стоит ожидать
увидеть здесь ничего необычного:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;// установка соединения&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$connection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;pg_pconnect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"host=localhost dbname=pgsql user=pgsql password=MyPassword"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// не забываем менять указанные данные для авторизации на правильные&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$connection&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Сайт не работает по техническим причинам.&lt;/span&gt;
&lt;span class="s2"&gt;Просим прощения за доставленные неудобства."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// ни в коем случае не выводим более информативных сообщений об ошибке, чем это&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;А вот с отправкой и обработкой результатов запросов ситуация далеко не
так однозначна. Помимо простой передачи самого текста запроса в СУБД,
необходимо правильно определить тип запроса и в соответствии с этим
обработать результат. Можно конечно попытаться сделать это автоматически
на основе вытаскивания первого слова из текста запроса, но мне всетаки
кажется более предпочтительным определение "вручную" желаемого вида
представление результата. Выполнение произвольных запросов может
выглядеть, например, следующим образом:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$bool&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// произвольный запрос&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//echo $str."&lt;/span&gt;
&lt;span class="s2"&gt;"; // очень удобно на стадии разработки в процессе поиска ошибок&lt;/span&gt;
&lt;span class="s2"&gt;  &lt;/span&gt;&lt;span class="si"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;=@pg_query(self::&lt;/span&gt;&lt;span class="si"&gt;$connection&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="si"&gt;$str&lt;/span&gt;&lt;span class="s2"&gt;); // @ - для сокрытия теоретически возможных ошибок&lt;/span&gt;
&lt;span class="s2"&gt;  // or die('Query failed: '.pg_last_error());&lt;/span&gt;
&lt;span class="s2"&gt;  // не забываем убирать в комментарий в финальном варианте проекта&lt;/span&gt;
&lt;span class="s2"&gt;  // или совсем удалять&lt;/span&gt;
&lt;span class="s2"&gt;  if(&lt;/span&gt;&lt;span class="si"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;)  // Если получен результат, отличный от false&lt;/span&gt;
&lt;span class="s2"&gt;  {&lt;/span&gt;
&lt;span class="s2"&gt;    if(&lt;/span&gt;&lt;span class="si"&gt;$bool&lt;/span&gt;&lt;span class="s2"&gt;)  // Если выбран результат в виде boolean&lt;/span&gt;
&lt;span class="s2"&gt;    {&lt;/span&gt;
&lt;span class="s2"&gt;      return true;&lt;/span&gt;
&lt;span class="s2"&gt;    }&lt;/span&gt;
&lt;span class="s2"&gt;    else  // Если выбран результат в виде массива&lt;/span&gt;
&lt;span class="s2"&gt;    {&lt;/span&gt;
&lt;span class="s2"&gt;      &lt;/span&gt;&lt;span class="si"&gt;$n&lt;/span&gt;&lt;span class="s2"&gt;=pg_num_rows(&lt;/span&gt;&lt;span class="si"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;);  // для создания универсального формата массива&lt;/span&gt;
&lt;span class="s2"&gt;      if(&lt;/span&gt;&lt;span class="si"&gt;$n&lt;/span&gt;&lt;span class="s2"&gt;==1)return pg_fetch_array(&lt;/span&gt;&lt;span class="si"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;,0,PGSQL_ASSOC);&lt;/span&gt;
&lt;span class="s2"&gt;      else  // даже когда результат содержит только одну строку&lt;/span&gt;
&lt;span class="s2"&gt;      {&lt;/span&gt;
&lt;span class="s2"&gt;        &lt;/span&gt;&lt;span class="si"&gt;$j&lt;/span&gt;&lt;span class="s2"&gt;=pg_num_rows(&lt;/span&gt;&lt;span class="si"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;);&lt;/span&gt;
&lt;span class="s2"&gt;        &lt;/span&gt;&lt;span class="si"&gt;$list&lt;/span&gt;&lt;span class="s2"&gt;=array();&lt;/span&gt;
&lt;span class="s2"&gt;        for(&lt;/span&gt;&lt;span class="si"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;=0;&lt;/span&gt;&lt;span class="si"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;&lt;/span&gt;&lt;span class="si"&gt;$j&lt;/span&gt;&lt;span class="s2"&gt;;&lt;/span&gt;&lt;span class="si"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;++)&lt;/span&gt;
&lt;span class="s2"&gt;        &lt;/span&gt;&lt;span class="si"&gt;$list&lt;/span&gt;&lt;span class="s2"&gt;[]=pg_fetch_array(&lt;/span&gt;&lt;span class="si"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="si"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;,PGSQL_ASSOC);&lt;/span&gt;
&lt;span class="s2"&gt;        return &lt;/span&gt;&lt;span class="si"&gt;$list&lt;/span&gt;&lt;span class="s2"&gt;;&lt;/span&gt;
&lt;span class="s2"&gt;      }&lt;/span&gt;
&lt;span class="s2"&gt;    }&lt;/span&gt;
&lt;span class="s2"&gt;  }else return false;&lt;/span&gt;
&lt;span class="s2"&gt;}&lt;/span&gt;
&lt;span class="s2"&gt;?&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Помимо базовой отправки запросов, в некоторых случаях имеет смысл
написать несколько методов, отправляющих частоиспользуемые запросы, что
в некоторых случаях позволяет сократить объем и уменьшить
нагроможденность кода. Хоть я и предпочитаю не пользоваться такими
вещами, но привести пример такого рода метода все же стоит:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;selectAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// пример метода отправки чаcтоиспользуемых запросов&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"select * from "&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;";"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Если чувствуете необходимость в подобных функциях, можно написать
огромное количество, все ограничивается лишь Вашим воображением и
знаниями SQL.&lt;/p&gt;
&lt;p&gt;Что ж, осталось лишь собрать весь код в единый листинг:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SQL&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nv"&gt;$connection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// соединение с СУБД&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;// установка соединения&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$connection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;pg_pconnect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"host=localhost dbname=pgsql user=pgsql password=MyPassword"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// не забываем менять указанные данные для авторизации на правильные&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;isset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$connection&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Сайт не работает по техническим причинам.&amp;lt;br /&amp;gt;Просим прощения за доставленные неудобства."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ни в коем случае не выводим более информативных сообщений об ошибке, чем это&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$bool&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// произвольный запрос&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//echo $str."&amp;lt;br&amp;gt;"; // очень удобно на стадии разработки в процессе поиска ошибок&lt;/span&gt;
    &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;=@&lt;/span&gt;&lt;span class="nb"&gt;pg_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;$connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// @ - для сокрытия теоретически возможных ошибок&lt;/span&gt;
    &lt;span class="c1"&gt;// or die('Query failed: '.pg_last_error());&lt;/span&gt;
    &lt;span class="c1"&gt;// не забываем убирать в комментарий в финальном варианте проекта&lt;/span&gt;
    &lt;span class="c1"&gt;// или совсем удалять&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Если получен результат, отличный от false&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Если выбран результат в виде boolean&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt;  &lt;span class="c1"&gt;// Если выбран результат в виде массива&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;pg_num_rows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// для создания универсального формата массива&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$n&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;pg_fetch_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;PGSQL_ASSOC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;  &lt;span class="c1"&gt;// даже когда результат содержит только одну строку&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nv"&gt;$j&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;pg_num_rows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nv"&gt;$list&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;$j&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="nv"&gt;$list&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;pg_fetch_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;PGSQL_ASSOC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$list&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;selectAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// пример метода отправки чаcтоиспользуемых запросов&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;self&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"select * from "&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;";"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cp"&gt;?&amp;gt;&lt;/span&gt;&lt;span class="x"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;```&lt;/p&gt;
&lt;p&gt;Вот так вот оно и выглядит в простейшем варианте, дорабатывать под
собственные нужды код можно до бесконечности естественно, но в
большинстве случаев даже такой реализации вполне должно хватать.&lt;/p&gt;
&lt;p&gt;Эта статья является частью &lt;a href="https://www.insight-it.ru/dzhentelmenskij-nabor-php-programmista/"&gt;серии статей "Джентельменский набор PHP программиста"&lt;/a&gt;, если Вам
понравилась эта статья то очень вероятно, что Вам придутся по душе и
остальные статьи.&lt;/p&gt;
&lt;p&gt;Не забываем &lt;a href="/feed/"&gt;подписываться на RSS блога&lt;/a&gt;!&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Иван Блинков</dc:creator><pubDate>Wed, 16 Jan 2008 22:04:00 +0300</pubDate><guid>tag:www.insight-it.ru,2008-01-16:php/2008/obshhaemsya-s-bazojj-dannykh/</guid><category>MySQL</category><category>PHP</category><category>PostgreSQL</category><category>SQL</category><category>БД</category><category>интерфейс</category><category>кодинг</category><category>ООП</category><category>СУБД</category><category>технология</category><category>хранение данных</category></item></channel></rss>