9 August 2011

Шортики — короткие и смешные, или как мы готовили сайт к Хабраэффекту

Self Promo


Намба 0: Вступление

Однажды, задумчиво листая башорг, я вдруг понял, что большинство цитат довольно длинные и очень часто их просто лень читать. В наш век всеобщей спешки — на это уходит много времени. Таким образом родился новый формат: Шортики (от английского слова Short — короткий). Шортик — это короткая (из одного-двух предложений) шутка, жестко ограниченная 255 символами (чтобы не повадно было).

Как раз в этот момент я хотел подучить программирование и искал несложную задачу. Сайт я накидал довольно быстро — за пару вечеров, рассказал о нем коллегам по работе и паре знакомых в IRC, и собирался уже о нем забыть, как вдруг обнаружил, что за два дня сайт посетило 500 человек, из которых чуть ли не треть подписалась на RSS. Стало понятно, что концепция людям понравилась. Немного прилизав сайт внешне, я решился показать его Хабрасообществу — Шортики — короткие и смешные, прошу любить и жаловать.

P.S. Учитывая то, что это не совсем стартап (проект пока некоммерческий, да и «стартап» — слишком громкое слово для моей поделки), приобретенная за годы пребывания на сайте хабрасовесть не позволила мне написать нетехническую статью. Поэтому, под катом вас ждет занимательная история о том, как мы готовили Шортики к Хабраэффекту.



Намба 1: Ресурсы

У любого веб-проекта, и особенно у стартапа, всегда есть резкие наплывы посещамости, вызванные разными причинами — публикация статьи на крупном ресурсе (например, на Хабре), пресс-релиз, рекламная кампания, неожиданное упоминание в новостях, итд. Часто (как в последнем примере) всплеск посещаемости происходит ВНЕЗАПНО™.

Это подталкивает к важному выводу — поместить ресурс с посещаемостью в 100к уников в день на начальном VPS за 200 рублей, конечно, можно. Это будет большой повод для гордости в кругу друзей-гиков, однако, скорее всего, приведет к падению сайта в самый ответственный момент. Вообще, хорошо, когда веб-продакшн система работает в обычном режиме не более чем на 10% от ее мощности. Это позволит ей выстоять в момент всплеска посещаемости. Исходя из всего этого, даже такой «легкий» сайт как Шортики лучше поставить на сервер с приличным запасом мощности.

Намба 2: FrontEnd и Backend

Одна из первых заповедей оптимизации веба гласит — разделяй контент статический и динамический. Сделаем мы это стандартным решением: Nginx на фронтенд, Apache слушает на 127.0.0.1. Nginx отдает статический контент, а если видит, что запрос идет к динамическому — отдает запрос «внутрь» апачу:

server {

listen 10.0.121.124:80;
server_name shortiki.com www.shortiki.com;

# Унифицируем домен

if ($host = 'www.shortiki.com' ) {
rewrite ^/(.*)$ http://shortiki.com/$1 permanent;
}

access_log /var/log/vhosts/nginx-shortiki.com-access.log main;

# Статику отдает нгинкс, пусть браузеры возвращаются за ней раз в месяц. А логи запросов к картинкам нам не нужны.

location ~ ^.+\.(html|jpg|jpeg|gif|png|ico|css|js)$ {

root /usr/home/vhosts/shortiki;
expires 30d;
access_log off;

}

location / {

proxy_pass http://127.0.0.1:8081;
# Здесь в нашем случае можно придумать хитрое кеширование, но это тема для отдельной статьи
}
}


* This source code was highlighted with Source Code Highlighter.


Намба 3: MPM

Сегодня Apache умеет работать в двух основных вариантах MPM (Multi-Processing Modules, они определяют, каким образом веб-сервер работает с тредами/дочерними процессами, итп) – prefork и worker.

Prefork работает по следующей схеме – много процессов, один тред на процесс, процессы обрабатывают запросы. Prefork отличается стабильностью, однако потребляет больше памяти и работает медленнее чем worker.

Worker отличается тем, что использует много процессов, много тредов на каждый процесс, запросы обрабатывают треды. Работает worker намного быстрее конкурента и использует меньше памяти, но изоляция между процессами у него неполноценная, что может создать проблемы на сайтах, где есть сессии, регистрации, ну и прочие важные данные. Учитывая популярность префорка, мы будем разбирать оптимизацию на его примере, хотя, в моем случае, с Шортиками логичнее использовать worker.

Намба 4: Accept-фильтры

Accept-фильтры (в данном случае) представляют собой модуль ядра, который буферизирует входящие соединения, и передает запрос веб-серверу только тогда, когда полностью получен корректный HTTP запрос.

В моем случае используется FreeBSD, следовательно, подгружаем модуль:

# kldload accf_http

Делаем так, чтобы модуль подгружался при каждой загрузке системы:

echo 'accf_http_load="YES"' >> /boot/loader.conf

Настраиваем серверы, для апача:

AcceptFilter http httpready

И nginx:

listen 10.0.121.124:80 default sndbuf=16k rcvbuf=8k accept_filter=httpready

И перезапускаем веб-серверы:

/usr/local/etc/rc.d/nginx reload
/usr/local/etc/rc.d/apache22 restart


Намба 5: Тюнинг Apache

Тюнинг Apache традиционно начинается с самой главной рекомендации — первым делом необходимо отключить лишние модули. После того, как ненужные модули выброшены, начинаем подкручивать основные настройки:

MaxClients – параметр, указывающий максимально количество одновременных соединений, которые может держать сервер. Если MaxClients = 300, то при 301 одновременном запросе, последний запрос встанет в очередь, и будет ждать, пока один из процессов освободится, чтобы его обслужить. Основной ресурс, ограничивающий MaxClients, это оперативная память – 300 созданных дочерних процесса апача должны поместиться в памяти одновременно. Обычно принято высчитывать MaxClients исходя из количество свободной памяти:

MaxClients = СвободнаяПамять / РазмерПроцессаАпача

Размер дочернего процесса Apache можно посмотреть в колонке RSS вывода команды top или ps.

Отключаем AllowOverride:

<Directory />
AllowOverride none
</Directory>


* This source code was highlighted with Source Code Highlighter.


Дело в том, что если оставить его его включенным, это заставит апач каждый раз делать запрос к файловой системе, проверяя есть ли файл .htaccess.

Выключаем ExtendedStatus (добавляет 1 или 2 системных вызова на каждый реквест):

ExtendedStatus Off

Добавляем FollowSymLinks для веб-директории, иначе Apache каждый раз будет делать проверку симлинк перед ним или файл:

<Directory />
Options FollowSymLinks
</Directory>


* This source code was highlighted with Source Code Highlighter.


Уменьшаем таймайут:

Timeout 10

Добавляем сжатие некоторых типов данных (экономит много трафика):

<Location />
AddOutputFilterByType DEFLATE text/html text/plain text/css text/xml application/x-javascript
</Location>


* This source code was highlighted with Source Code Highlighter.


MinSpareServers и MaxSpareServers – эти параметры, указывающие, какое количество дочерних процессов держать «в холостую». Например, если сейчас запросами заняты четыре процесса, а MinSpareServers равен 2, то апач запустит еще 2 процесса, которые будут простаивать в ожидании запросов. Хитрость в том, что создание нового процесса – относительно ресурсоемкая задача, и, в принципе, настройки тут сводятся к тому, чтобы избежать ситуации, когда сервер начинает постоянно создавать/убивать процессы.

MinSpareServers 2
MaxSpareServers 8


При таких настройках, апаче всегда будет держать 2 процесса в ожидании запросов, но в то же время, если простаивает уже 8 или более процессов – начнет их прибивать, освобождая ресурсы.
Перед Хабраэффектом можно поставить и побольше:

MinSpareServers 8
MaxSpareServers 32


StartServers – количество дочерних процессов, которые Apache создаст при запуске. Если StartServers меньше MinSpareServers, то апач догонит значение до MinSpareServers. Эта настройка зависит от изначальной нагрузки на сайт, и количества ресурсов в нашем случае:

StartServers 8

MaxRequestPerChild указывает сколько запросов обработает процесс перед тем, как будет убит и запущен новый вместо него. Это делается для того, чтобы нивелировать влияние утечек памяти. Если ваш код написан хорошо и у вас нет таких проблем – можно смело повышать значение, однако выше 10000 ставить не рекомендуется, раз в 10000 запросов передернуть процесс это не страшно, зато хорошая профилактика.

MaxRequestPerChild 3000

KeepAlive – настройка, позволяющая обработать несколько запросов в пределах одной TCP сессии, и не открывать новую сессию на каждый запрос. Важно оставить значение KeepAliveTimeout низким, т.к. при большом количестве запросов много процессов будут проводить время в ожидании, и тогда апачу нужно будеть запускать еще процессы.

KeepAlive On
KeepAliveTimeout 5


Намба 6: Тюнинг PHP

В нашем конкретном случае, мы сведем тюнинг php к установке php модуля memcache и самого memcached демону, поскольку все остальные параметры особо не повлияют на производительность. В FreeBSD это просто:

cd /usr/ports/databases/pecl-memcache
make install clean

cd /usr/ports/databases/memcached
make install clean


Намба 7: Добавляем мемкеш

Почти все пользовали, которые зайдут на сайт — зайдут с главной страницы. Следовательно, ее важнее всего оптимизировать. На главной странице у нас один SQL запрос, который берет из базы 20 самых свежих шортиков:

SELECT sid, sdate, stext, srating FROM quotes ORDER BY id ASC LIMIT $shortik_first, $shortiks_main

* This source code was highlighted with Source Code Highlighter.


Лезть в базу каждый раз при открытии страницы нет смысла, а посему, легким движением руки запрос превращается в:

// Устанавливаем соединение с Memcached

$mem = new Memcache();
$mem->connect('localhost', 11211);

// Обнуляем переменные

$quotesonpage = '';

// Проверяем есть ли в мемкеше нужная запись

if ( !$mem->get['s_main'] ) {

// Если нет - забираем ее из MySQL...

$connect = @mysql_connect ($server, $user, $pass) or die('Could not connect: ' . mysql_error());
@mysql_select_db("ShoDB");

$query = "SELECT sid, sdate, stext, srating FROM quotes ORDER BY id ASC LIMIT $shortik_first, $shortiks_main";

@mysql_set_charset('utf8',$connect);

$get_smain = mysql_query ($query) or die('Cannot execure query: ' . mysql_error());

$quotesonpage = array();
while ($shortik = mysql_fetch_assoc($get_smain)) {
$quotesonpage[] = $shortik;
}

$quotesonpage = array_reverse($quotesonpage);

// ...и добавляем в memcache, с временем жизни в полчаса (1800 секунд).

$mem->set('quotes', $quotesonpage,MEMCACHE_COMPRESSED,1800);
} else {
$quotesonpage = $mem->get['quotes'];
}


* This source code was highlighted with Source Code Highlighter.


Таким образом, при любом количестве пользователей, нагрузка главной страницы на MySQL равна одному запросу в полчаса. Разумеется, свежие шортики будут появляться на сайте с задержкой в полчаса, но это не страшно.

Намба 8: Милости просим :)


Добро пожаловать на Шортики! Надеюсь сайт будет так же тепло принят, как и в узком кругу тех, кто приходил в первые дни жизни ресурса.

Напоминаю, что изначальная цель проекта была в том, чтобы подтянуть навыки программирования, так что не стреляйте в пианиста программиста, он пишет как может :)
Tags:стартапapachephpnginxfreebsd
Hubs: Self Promo
+156
2.7k 237
Comments 205
Ads