---------------------------------------------------------------
  Тезисы для семинара 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
  + thttpd - держит до 2000 реквестов/сек без ограничения числа коннектов
    под FreeBSD на нем сделан images.rambler.ru, под Linux глючит
    http://www.acme.com -> freeware
    Mathopd (на нем сделан top.list.ru)
  + Размышления/советами по поводу производительных http-серверов:

.htaccess в юзерских директориях отменить
    Делаем
    AllowOverride None
    иначе сервер при открытии любого документа будет последовательно
    шерстить все вышестоящие директории на предмет наличия в них .htaccess



  - Сошедший с ума робот собирает невероятное количество 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>

Развивать  его  можно по разным направлениям: по разному обрабатывать разных
User-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

RewriteEngine on
RewriteCond %{REMOTE_ADDR}  ^92.241.17[67]
RewriteRule ^(.*) http://www.az.lib.ru$1 [R,L]


RewriteEngine on
RewriteRule ^/d/dunaew_w_a - [L]
RewriteRule ^/editors/d/dunaew_w_a - [L]
RewriteRule (.*) http://zhurnal.lib.ru$1 [L,R=301]
# R=301 - moved permanently

Подробнее тут:
http://www.pcre.ru/docs/apache/text/urltrans


Не ставьте баннеры на самый верх
 - Баннер сверху отнимает 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

    Свежее. Спасаемся от DDoS-атак

tail -f /var/log/httpd/access_log | grep $WRONGURLREGEXP |\ while read ip f ; do iptables -L -n | grep -q " $ip " || iptables -I INPUT -s $ip -j DROP ; done ; iptables -I INPUT -s 0/0 -d $yourIP -p tcp --dport 80 -m state -state NEW -m limit -limit 300/second -limit-burst 400 -j DROP A этот рул дропает новые реквесты, если их более 300 в секунду, при условии что 400 реквестов уже прошло Тоже самое, только без учета предварительных реквестов iptables -I INPUT -s 0/0 -d $yourIP -p tcp --dport 80 -m limit -limit 5/second -j DROP Если список заблоченых адресов слишком велик, ядро перестает с ним справляться Тогда воспользуемся IPlist'ом - он ведет проверку блоклиста в user-area, возвращая в iptables флажок ban/nonban. http://iplist.sourceforge.net/documentation.html

    Настройки ядра FreeBSD 6.3

/boot/loader.conf : kern.maxusers="512" kern.ipc.nmbclusters="32768" kern.ipc.nmbufs="131072" net.inet.icmp.icmplim=400 kern.maxfiles=32768 kern.maxdsiz="1024M"

    Настройки ядра Linux

Некоторые параметры настроек tcp-стека: помогут убрать сообщения в /var/log/messages Oct 19 07:50:24 lib kernel: printk: 2 messages suppressed. sysctl net.ipv4.tcp_syncookies=1 sysctl net.ipv4.tcp_max_syn_backlog=4096 sysctl net.ipv4.tcp_tw_recycle=1 sysctl net.ipv4.tcp_tw_reuse=1 Увеличивает tcp-буфер, это уменьшает количество отдельных обращений к диску при запросе больших файлов sysctl net.ipv4.tcp_wmem="4096 131072 1048576"

    Настройки ядра Linux, по состоянию на 2009 год

Не проверялось, разобраться sysctl -w net.core.rmem_max=16777216 sysctl -w net.core.wmem_max=16777216 sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216" sysctl -w net.ipv4.tcp_wmem="4096 65536 16777216" sysctl -w sysctl net.ipv4.tcp_window_scaling=1 sysctl -w sysctl net.ipv4.tcp_timestamps=1 sysctl -w sysctl net.ipv4.tcp_sack=1 sysctl -w net.ipv4.tcp_no_metrics_save=1 sysctl -w net.ipv4.tcp_moderate_rcvbuf=1 sysctl -w net.core.netdev_max_backlog=1000 sysctl -w net.ipv4.tcp_congestion_control=htcp sysctl -w net.core.somaxconn=1000 ifconfig eth0 txqueuelen 1000

Популярность: 7, Last-modified: Thu, 13 May 2010 14:23:53 GmT