Максим Мошков. О создании жизнестойкого веб-сервера --------------------------------------------------------------- Тезисы для семинара WebClub Date: 17 Nov 1999 --------------------------------------------------------------- Что может сделать сервер Обычный интелевый хост Linux или FreeBSD с апачем в состоянии обслужить 100-150 статических реквестов в секунду. Это 7Млн реквестов в сутки, что соответствует 200 тысяч посетителей в сутки. Трафик генерится при этом 1-2М в секунду. Вопрос, надо ли вам большего? 300 MaxClients соответствует 3M запросов в сутки, 60 тысяч человек. 400М RAM Пример Lenta.Ru. День выборов. 400Мб RAM, MaxClients 512, Timeout 120, CacheTime 900 500,000 cgi-html-реквестов + 5Млн img-файлов в час пик 35,000 запросов, 540 одновременных httpd, load 8-15 swapa не было Диск: только SCSI. Вот и все, что можно требовать от машины. И НИКАКОГО swap! (Имеется ввиду, что у веб-сервера swapобласть быть должна, но она обязана быть пустой) Технология ВСЕГДА СТАВИТЬ Last-Modified АТРИБУТ В ВЫДАЧУ CGI-СКРИПТОВ - документ без временного штампа не сохраняется в локальном кэше, и постоянно перезасасывается при просмотре Переименовать свою директорию CGI-скриптов из cgi-bin во что-нибудь другое - Прокси-серверы не кэшируют URL вида http://host.name/cgi-bin/file/name.txt и каждый раз вынуждены обращаться к вам на сервер. Всегда устанавливать поле Last-modified у Русского-Апача с автоматическим угадыванием кодировки + Да, если не взводить это поле, то на proxy-серверах не застрянут файлы в неккоректной кодировке. - Но насколько напрягутся все остальные юзеры (а их >95%), и сам веб-сервер... CharsetDisableForcedExpires on CacheNegotiatedDocs Не применять авторедирект по чарсету в русском Apache CharsetNormalizeToUrl none CharsetAutoRedirect koi8-r none CharsetAutoRedirect windows-1251 none Хранить документы на сервере в кодировке windows-1251 CharsetSourceEnc koi8-r + Поскольку 95% посетителей живут в этой кодировке, для них серверу не потребуется перекодировать документы. - rus-apach _всегда_ перекодирует документ. Даже win в win Файлам с SSI сервер сбрасывает Last-Modified, но это лечится SSI - порождает дополнительную нагрузку. Лучше выделить их только на отдельное расширение .shtml, и не трогать чистостатические .htm и .html. В конфигуре сервера есть директива, возвращающая Last-Modified SSI-файлам XBitHack full и выполнить chmod 755 *.shtml Фреймы не использовать Усложняют программирование и добавляют лишние реквесты: (двухфреймовая страница - 3 файла вместо одного!) Не делать суперобложек, максимум info в головную страницу Лишний клик, потеря посетителей, снижение глубины просмотра. НИКАКИХ ANIMATED-ГИФОВ - Из-за ошибки в Netscape-навигаторе он постоянно перезапрашивает animated-гиф по сети, посылая запрос на сервер каждые 10-15 секунд Представьте, что на вашу страницу с 10 анимированными гифами зашло двадцать Netscape и просто смотрят на нее ни во что не кликая. Netscap'ы сами начнут слать вашему серверу IFMS-запросы в темпе 20 запросов в секунду. Лишние имаджи = потерянные деньги + Многие хостеры не берут денег за траффик и размеры графики можно не считать. - Но часто включают счетчик на _входящий_ зарубежный траффик. Помните, что сам HTTP-реквест от зарубежного посетителя - _входящий_ Всего-то в нем 200-300 байт. Но если у вас на каждой страничке по 20 гиф-файлов с оформлением, то один HTML-клик из-за заграницы обойдется в 4Кб входного трафика. Помножим на 10 тысяч страничек в день, да на 30 дней - 1.2Gb - входящей зарубежки. 100-200 баксов - как с куста. Лишние имаджи = замедленный отклик и потерянные посетители - Много дополнительных реквестов за графикой забивают входную очередь, переполняя MaxClients, более приоритетные запросы на обычные html вынуждены стоять в общей очереди, задерживая отклик до 10-30 секунд. + Отнести всю графику на отдельный порт, и на него повесить "худой" отдельный веб-сервер, который может только обслуживать статические файлы и ничего кроме. В нем - сокращенный TimeOut, и меньше жрется виртуальной памяти. + khttpd для Linux - работает как модуль ядра - с минимальным оверхедом. http://www.fenrus.demon.nl/index.html ║ http://www.fenrus.demon.nl/index.html + thttpd - держит до 2000 реквестов/сек без ограничения числа коннектов под FreeBSD на нем сделан images.rambler.ru, под Linux глючит http://www.acme.com -> freeware ║ http://www.acme.com Mathopd (на нем сделан top.list.ru) ║ http://mathop.diva.nl/ + Размышления/советами по поводу производительных http-серверов: ║ http://www.kegel.com/c10k.html .htaccess в юзерских директориях отменить Делаем AllowOverride None иначе сервер при открытии любого документа будет последовательно шерстить все вышестоящие директории на предмет наличия в них .htaccess 404 код - Сошедший с ума робот собирает невероятное количество 404 ошибок, зацикливаясь в них на веки 404 код не делать cgi-скриптом 404 код не делать "красивым" - с гифчиками и указаниями на прочие разделы Роботы идут лесом robots.txt Обязательно делать файл robots.txt, потому что он - наиболее запрашиваемый на сервере документ, и иначе порождает массу 404 - см. выше, особенно если 404 - cgi-скрипт Разумные роботы слушаются запретов в файле robots.txt # "Скажем НЕТ offline-качалкам User-Agent: DISCo Pump, Wget, WebZIP, Teleport Pro, WebSnake, Offline Explorer, Web-By-Mail Disallow: / Управление доступом через httpd.conf Пример перекрывает доступ к нашим .zip файлам если их линкуют не с наших страниц а снаружи. SetEnvIfNoCase Referer lib\.ru internal_referer SetEnvIfNoCase User-Agent Teleport internal_referer SetEnvIfNoCase User-Agent Vampire internal_referer SetEnvIfNoCase User-Agent ReGet internal_referer SetEnvIfNoCase User-Agent GetRight internal_referer SetEnvIfNoCase User-Agent Wget internal_referer <Files ~ "\.zip$"> ErrorDocument 403 http://lib.ru/books/index.htm order deny,allow deny from all allow from env=internal_referer </Files> Развивать его можно по разным направлениям: по разному обрабатывать разных Us╜ er-Agent, проверять IP-клиента и многое другое, и главное, что все это делается не в cgi-скрипте, а на уровне базового httpd - а значит дешево обходится серверу. Если робот упорствует, его уничтожают route add -host 123.456.789.1 gw localhost Если на на mod_rewrite, как то так - по условиям - RewriteCond %{HTTP_USER_AGENT} Teleport [NC,OR] RewriteCond %{HTTP_USER_AGENT} MSIECrawler [NC,OR] RewriteCond %{HTTP_USER_AGENT} DISCoFinder [NC,OR] RewriteCond %{HTTP_USER_AGENT} WebCrawler [NC,OR] RewriteCond %{HTTP_USER_AGENT} spider [NC,OR] все запросы от известных роботов на динамические страницы перенаправляются на статическую заглушку RewriteRule ^/news.html? /static_index.html [R] NC = No Case R = redirect L = Last rule Например - переадресовка всех внешних рефероров на архивы - на морду сайта RewriteEngine on RewriteCond %{HTTP_REFERER} !^http://(www\.lib\.ru/)|(lib\.ru/).*$ [NC] RewriteBase /home/lib-www/docs/ RewriteRule ^arc/.*\.(zip)|(rar)$ http://www.lib.ru/ [R] RewriteCond %{HTTP_REFERER} !^http://(www\.lib\.ru/)|(lib\.ru/).*$ [NC] RewriteBase /home/lib-www/docs/ RewriteRule ^index2\.html$ http://www.lib.ru/ [R] Или так: RewriteEngine On RewriteCond %{HTTP_REFERER} !^http://allowed-site1.com*$ [NC] RewriteCond %{HTTP_REFERER} !^http://www.allowed-site1.com*$ [NC] RewriteCond %{HTTP_REFERER} !^http://allowed-site2.com*$ [NC] RewriteCond %{HTTP_REFERER} !^http://www.allowed-site2.com.*$ [NC] RewriteRule ^.*$ http://site.com/another_pic.gif [R,L] Даже так: RewriteEngine on RewriteCond %{HTTP_REFERER} !^$ RewriteCond %{HTTP_REFERER} !^http://(www\.)?domen.ru/.*$ [NC] RewriteRule \.(gif|jpg)$ http://www.domen.ru/fuck_off.gif [R,L] RewriteEngine on RewriteCond %{REMOTE_ADDR} !^81.19.69.21$ RewriteRule ^(/n/.*) https://lenta.ru$1 [R,L] RewriteEngine on RewriteCond %{REMOTE_ADDR} !^81.19.69.28 RewriteCond %{REMOTE_ADDR} !^81.19.68.[6-9] RewriteCond %{REMOTE_ADDR} !^81.19.68.1[012]. RewriteRule ^(/N/.*) https://lenta.ru$1 [R,L] # Allow from 81.19.68.64/255.255.255.224 Баннеры Не ставьте баннеры на самый верх - Баннер сверху отнимает 1-2 реквеста из 4 - и в итоге грузится вперед тормозя ваши сайтовые картинки + в ссылке на img src баннера вместо hostname ставьте IP - сэкономите посетителю dns-ресолвинг - а это 2-30 секунд. Задержка в загрузке "вашего" содержимого - держит у _вас_ лишние httpd Ни в коем случае не делать uniq-url для баннера с помощью SSI virtual cgi include. Потому что ps -axf покажет вам: 12858 ? S 0:00 \_ /usr/local/apache/sbin/httpd 12859 ? S 0:00 \_ /usr/local/apache/sbin/httpd 12862 ? S 0:00 \_ /usr/local/apache/sbin/httpd 13097 ? Z 0:00 | \_ (rand.cgi <zombie>) 13098 ? Z 0:00 | \_ (rb2 <zombie>) 13103 ? Z 0:00 | \_ (rb2 <zombie>) 13104 ? Z 0:00 | \_ (c4.pl <zombie>) 13105 ? Z 0:00 | \_ (random.cgi <zombie>) 12863 ? S 0:00 \_ /usr/local/apache/sbin/httpd 12868 ? S 0:00 \_ /usr/local/apache/sbin/httpd Вместо этого использовать var - дату <!--#config timefmt="%H%w%e%M%S"--> <a href=http://rb2.design.ru/cgi-bin/href/nit?<!--#echo var="date_local"--> target="_top"> <!--#config timefmt="%M%H%S%I%e"--> <a href=http://www1.reklama.ru/cgi-bin/href/nit?<!--#echo var="date_local"--> target=_top> <img src=http://www1.reklama.ru/cgi-bin/banner/nit?<!--#echo var="date_local"--> width=468 height=60 border=0 vspace=10 alt="www.reklama.ru. The Banner Network." ismap></a> Tx3 предлагает внутреннюю подкачку баннера: это лишний cgi-скрипт, затем из скрипта делает обращение к баннерному движку - это задержка при генерации html, а значит - больше httpd висящих в памяти. Скрипты 200 тысяч в секунду = 3 скрипта в секунду 30 static в секунду = suexec - запуск cgi-скриптов под юзерским id - да, повышает безопасность, но удваивает число fork+exec при запуске любого cgi-скрипта. Избегайте насколько это возможно. Следить, что вкомпилировано в httpd. Да, конечно код в unix реентерабельный, но ведь у modperl и php3 огромные области инициализируемых данных - все это жрет виртуальную память, и время на обработку одного запроса, да и просто проверка hoock'ов, на которую подвешены модули отнимает время. Стоит ли обрабатывать 100 статических httpd-запросов, для обслуживания которых достаточно одного модуля default с помощью 5M монстра с вкомпилированными в него modperl, php3, ssl httpd - которых за это же время потребуется 2-5. Из 100. Конечно лучший язык для написания cgi-скриптов - perl. Но он безжалостен к серверу. Перл-скрипты - компилируются при каждом вызове. Скорость компиляции сильно зависит, но все равно - это примерно 0.1 сек на 20Kb perl-кода. Мораль - даже без учета на время работы собественно программы 60Kb скрипт сможет выполниться не чаще чем 2-3 раза за секунду! Как выкручиваться из положения? Разбить большой скрипт на много мелких составных частей и подключать их только когда указанный кусок кода требуется при данном случае исполнения кода. Для этого в perl используется оператор "require" (Это грамотный аналог include - грамотность заключена в том, что реяуире - исполнимый оператор, и затягивает дополнительный код только когда он затребован, а при повторном исполнении require он его НЕ перекомпилирует повторно) Прекомпиляция перл. Perl2C. modperl. FastCGI... Кэширование. Можно сохранять результат работы скрипта в кэшфайле и при повторных запроса выдавать его вместо повторной генерации. По субъективным ощущениям кэш файл лучше выдавать не самим скриптом open IN $file; while(){print;} а внутренним редиректом print "Location: http:$file\n\n"; Кэширование с помощью squid в режиме proxy-accelerator Пожалуй, лучшее решение, если надо ускорять cgi-скриптовый сервер. Скорость и нагрузка на машину у squid-accelerator совпадает с работой httpd отдающего статические html и image файлы. А нагрузку на cgi-движок он снижает в 2-3 раза. Squid сможет поддерживать директивы IfModifiedSince и REGET для содержимого скрипта, что, понятное дело самому в скрипте делать очень невесело. Reset в 4 часа ночи Машины стояли мордами друг к другу так, что выезжающая подставка для кофе одного нажимала на кнопку Reset второго, и наоборот. Предыдущая реинкарнация моей lib.ru жила в одном корпусе с другой машиной. Была у них внутри на коленке паяная схема-самоделка, которая позволяла питание передернуть соседу. А вообще для подобных вещей обычный смарт-UPS лучше всего подходит. А компорт от УПСа надо заводить либо на киску, ибо они не дохнут, либо на модем и звонить на него из дома. From: Exler Поскольку охранник раза три за ночь обходил помещение на предмет возгорания (заходил в комнату, включал свет, обозревал помещение, выключал свет и уходил), к выключателю на ночь присоединялась кнопка, которая при нажатии на выключатель автоматически ресетила машину. Apache Config Конфигурационные параметры влияющие на скорость. Options FollowSymLinks - позволяет не проверять симлинки AllowOverride all - позволяет не искать .htaccess во всех поддиректориях Очень важно! На сервере с большой посещаемостью: 1. Картинки снести на выделенный сервер(порт) (или отдельный процесс сервера), и отключить KeepAlive Off Поскольку Alive используется только для подкачки картинок, а для хтмля броузер все равно открывает новый коннект. С KeepAlive каждый сервер обслужив прос еще 15 секунд болтается в памяти ожидая, не придет ли новый запрос на картинку - увеличивая количество процессов раза в 4. Переезд сервера, смена его IP-адреса Старый IP-адрес сидит в кэшах DNS довольно долго (официально - до 8 часов, реально - до двух с лишним суток). Все это время многие клиенты идут по старому IP, на котором их уже никто не ждет - потери посетителей во время "устаканивания DNS достигают от 20 до 60%. Выход: двухшаговая смена ИП с использованием редиректов. 1. Шаг. За два дня до реальной смены IP поднимаем на новом IP виртуальный вебсервер-заглушку, который быдет откликаться на www.washserver.ru, а в его конфигуре ставим редирект всех запросов на http://washserver.ru httpd.conf на новом IP-адресе: <VirtualHost Новый-IP:*> ServerName www.washserver.ru Redirect / http://washserver.ru/ </VirtualHost> DNS-зона домена washserver.ru: @ ИН А старый-IP www IN A новый-IP После этого прописываем в DNS для www.washserver.ru новый IP, а washserver.ru оставляем старым. Посетители, пришедшие на www.washserver.ru будут редиректиться на washserver.ru - т.е. мы никого не потеряем, и ждем 2 суток, пока "разойдется" новый IP для www.washserver.ru Через 2 суток 2 шаг. Реальная смена IP у сервера. Одновременно с этим: На старом IP поднимаем виртуальный вебсервер-заглушку, который будет откликаться на washserver.ru, и делать редирект всех запросов на http://www.washserver.ru В DNS прописываем washserver.ru на новый IP Посетители, пришедшие по старому ИП на washserver.ru будут редиректиться на www.washserver.ru с новым ИП - т.е. мы никого не потеряем. А через 2 суток новый IP для имени washserver.ru разойдется по DNS и редирект можно будет снять. httpd.conf на старом IP-адресе: <VirtualHost Старый-IP:*> ServerName washserver.ru Redirect / http://www.washserver.ru/ </VirtualHost> DNS-зона домена washserver.ru: @ ИН А новый-IP www IN A новый-IP