Вот такую картину я увидел сегодня у себя в админке на Linode. Что-то в ней мне показалось странным. Через пару секунд я понял, что именно - надпись “Linode 512″, хотя я точно помнил, что у меня до этого было 360 мегабайт оперативки. Законнектился на сервак, проверил - действительно 360 мегабайт доступно. Зашел на страницу тарифных планов - действительно 360 поменялось на 512, да и остальные планы явно получили побольше оперативки. Как оказалось - увеличение оперативки произошло еще месяц назад, а я его не заметил, т.к. ни разу не перезагружал сервак с того момента, как он прекратил зависать, а для того, чтобы получить заветный прирост ресурсов - нужна перезагрузка.
Некоторые свои посты я дополняю постами-линками на Хабрахабре. Последний подобный пост - “Особенности работы jQuery.live()“. Он так и не попал на главную, но в данном случае это не очень важно, т.к. свой “хабраэффект” я поймал.
До этого у меня случались странности на сервере - он начинал есть все 400% процессора и наглухо зависал - к нему было невозможно подключиться и единственным вариантом восстановления был шатдаун инстанса в админке Linode. Это случалось нечасто и не особо меня парило. Но когда на мой сайт стали постоянно заходить люди с хабра - такое поведение сервера стало постоянным. Он начинал есть весь процессор. После перезагрузки он пару минут держался, а потом опять начинал свой бравое дело. Стало ясно - что-то явно не так в настройках.
После довольно недолгих разбирательств стало ясно, что виноват Apache (у меня стоит Apache + mod_prefork + php + ruby via passenger). Параметры prefork-а:
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxClients 150
MaxRequestsPerChild 0
</IfModule>
Теперь мне стало очевидно всё, кроме одной вещи - почему же все-таки был загружен процессор… А причина зависаний сервера была проста - параметр MaxClients отвечает за то, сколько процессов апач может запустить для обработки пользователей (в prefork-е каждый запрос обрабатывается отдельным процессом, который либо запускается, либо вытаскивается из пула свободных запущенных процессов). Процессы апача ну очень недешевые в плане ресурсов - честно говоря, я до сих пор не знаю, как можно посмотреть, сколько реально кушает процесс апача - по текущей нагрузке на сервак при создании новых процессов апач кушает лишние 10-20мб памяти. 150 процессов * 10 мегабайт… Ну вы поняли. У меня сейчас обычныя VPS-ка на 360Мб. В общем, причина зависаний была в том, что апач создавал кучу процессов и уходит в своп по самое нехочу. А вот вернуться оттуда он уже сам не мог - ему нужна была помощь.
Выход - снижать количество максимально процессов апача. Методом научного тыка была выбрана цифра в 20 процессов. Собственно, сейчас эта цифра вполне себе хорошо работает. После уменьшения MaxClients и перезапуска сервера он падать перестал. Но появилась новая проблема - из-за хабраэффекта сервак хоть и не падал, но отдавал ответы очень долго. Эту проблему я решил не решать, т.к. в тот момент был на работе. Сейчас вот появилось немного времени и я решил протестировать свой блог на отзывчивость.
ab -c 1 -n 100 http://blog.fxposter.org/
Эмм… Результата я не дождался.
Для тех, кто не работал с ab и не знает, что это такое - Apache Benchmark.
ab -c 1 -n 10 http://blog.fxposter.org/
Результат меня неприятно удивил. Результаты, к сожалению, не сохранились, но могу сказать точно, что время генерации страницы было около 1.5 секунды (судя по тому, что сейчас показывает генерация страницы, если её нет в кеше).
Собственно, стало понятно, как бороть “хабраэффект” - нужно сильно ускорить отдачу странички. Как? Да очень просто - кешированием. Слава богу, что это WordPress и тут есть такие плагины, как wp-cache - прикручивается за несколько минут и пока что отлично работает. Если бы такого плагина не было, то, наверное, у меня бы была сейчас статья о том, что такое Reverse Proxy и как его использовать для кеширования. :) А так - поставил, включил. Результаты:
Заодно решил немного пооптимизировать клиентскую часть - добавить expires и сжатие контента (использовался модуль апача deflate).
Приведу конечные файлы:
# /etc/apache2/mods-available/expires.conf
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/gif "access plus 1 weeks"
ExpiresByType image/png "access plus 1 weeks"
ExpiresByType image/jpeg "access plus 1 weeks"
ExpiresByType text/css "access plus 1 weeks"
ExpiresByType application/javascript "access plus 1 weeks"
</IfModule>
# cat /etc/apache2/mods-available/deflate.conf
<IfModule mod_deflate.c>
# AddOutputFilterByType DEFLATE text/html text/plain text/xml
DeflateCompressionLevel 9
# Insert filter
SetOutputFilter DEFLATE
# Netscape 4.x has some problems...
BrowserMatch ^Mozilla/4 gzip-only-text/html
# Netscape 4.06-4.08 have some more problems
BrowserMatch ^Mozilla/4\.0[678] no-gzip
# MSIE masquerades as Netscape, but it is fine
# BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
# NOTE: Due to a bug in mod_setenvif up to Apache 2.0.48
# the above regex won't work. You can use the following
# workaround to get the desired effect:
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
# Don't compress images
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png)$ no-gzip dont-vary
<IfModule mod_headers.c>
# Make sure proxies don't deliver the wrong content
Header append Vary User-Agent env=!dont-vary
</IfModule>
</IfModule>
Если вас интересует, почему эти файлы выглядят именно так - читайте документацию к модулям апача.
На другой ОС (у меня Ubuntu 9.04) эти файлы, наверняка, должны будут находиться в других местах. У себя я их разместил так, потому что эти конфиги автоматически подключаются вместе с подключением соответствующих модов апача (при использовании команды a2enmod).
По идее теперь сайт должен получше откликаться и, думаю, все-таки теперь он выдержит хабраэффект. :)
PS. С точки зрения клиентской оптимизации - css-ки и js-ки лучше бы собрать в один файл, а кучу запросов к изображениям заменить спрайтами. Заниматься первым особо не вижу смысла, т.к. и стилей и скриптов на блоге немного, а изображениями заниматься лень, т.к. все равно собираюсь переезжать на “Twenty Ten“.
Есть такая фича в Rubygems, как установка гем-ов в директорию пользователя ~/.gem, если gem install запускается без sudo и, соответственно, у пользователя нет разрешения установить что-либо в дефолтную директорию.
В Rubygems до версии 1.3.6 это решалось просто - автоматически. Т.е. если вы вместо sudo gem install пишете gem install - вам выдается warning, но гем успешно устанавливается:
[fxposter@kitty ~]$ gem install haml
WARNING: Installing to ~/.gem since /usr/lib/ruby/gems/1.8 and
/usr/bin aren't both writable.
Successfully installed haml-3.0.13
1 gem installed
Installing ri documentation for haml-3.0.13...
Installing RDoc documentation for haml-3.0.13...
Вроде бы всё замечательно. Но с версии 1.3.6 это дефолтное поведение изменилось, и при обновлении с помощью gem update --system об этом честно пишется:
–user-install is no longer the default. If you really liked it, see Gem::ConfigFile to learn how to set it by default. (This change was made in 1.3.6)
Но кто реально читают все эти тексты? Да и не все обновляют Rubygems таким образом. В нашем случае произошла смена сервера. Причем на старом сервере стояла старая версия Ruby Enterprise Edition и дефолтные Rubygems 1.3.5. На новом же серваке наши бравые админы опять всё “покомпилили” и вместе с REE у нас там обновился и Rubygems - теперь там стояла версия 1.3.7. И… сначала отвалился деплой, т.к. по умолчанию rake gems:install у нас там не включен. Запустили rake gems:install - не работает. Запустили gem install - не работает. Всё просто замечательно.
Разбирались долго. Сначала думали на админов, которые в рельсах плохо шарят и думали, что они там “понастраивали”. Кстати, понастраивали они действительно знатно - скомпилированный REE почему-то стоит поверх стандартного Ruby 1.8.5 (у нас там CentOS 5, в ближайшем времени переезжаем на Amazon EC2 на Ubuntu). Но это неважно. После долгого разговора с админом и ковыряния серваков я чисто случайно обратил внимание на разные версии Rubygems. Заподозрил неладное. Обновил их на старом серваке на 1.3.7 и… получил сообщение, написанное выше. Отлично. Теперь осталось восстановить поведение старых Rubygems.
В итоге всё оказалось довольно просто. В файл ~/.gemrc нужно дописать следующую строку: "install: --user-install". После чего мой .gemrc стал выглядеть так:
[waysgo@web-waysgo ~]$ cat .gemrc
---
:benchmark: false
:update_sources: true
:sources:
- http://rubygems.org/
- http://gems.github.com/
:bulk_threshold: 1000
:verbose: true
:backtrace: false
install: --user-install







Последние комментарии