У нас 3proxy уже использовался, но только как socks proxy. Захотелось попробовать его как http прокси. Этот сервер называют еще ЗАРАЗА прокси, умеет и http, и socks и много чего еще. Даже опции перенаправления есть, чтобы составлять цепочки из проксей. Не умеет, правда, кэширования. Штатное перенаправление 3proxy дерективой parent работает отлично. Со squid с доменной аутентификацией не заработал.
Было интересно сдюжит ли 3proxy http нагрузку, да и вообще проверить на наличие подводных камней или ошибок конфигурации. Приведу окончательный полный конфиг в конце, а сейчас скажу, что начальный конфиг не сильно отличался от дефолтного, поправил только списки доступа и другую ерудну. С раннего утра выключил основной squid и запустил на его место 3proxy. По-началу все было хорошо и быстро. Однако, чем дальше, тем хуже становилось. По мере прихода сотрудников на работу периодически все больше веб-страниц переставало открываться или подгружаться полностью. В логе /var/log/messages мелькало
kernel: sonewconn: pcb 0xfffff8003012a310: Listen queue
overflow: already in queue awaiting acceptance
# netstat -Lan
сказал мне, что у меня для 3proxy на порту 3128 в очереди больше соединений на подключение, чем их может быть максимум. Увеличив максимиум на уровне системы до 512, ситуация не изменилась:
kern.ipc.somaxconn=512
Вообще в это время в логах 3proxy светилось бы такое:
Warning: too many connected clients (N/M)
Но из-за того, что у меня в настройках 3proxy был выставлен формат логов совместимый со squid (для анализаторов), там был бы только ошметок от этой фразы, и конечно в общем потоке заметить это было сложно. Но ко всему прочему я еще использовал ключ -t для старта службы proxy.
Правило: использовать по-началу и для дебага только стандартный формат логов, и не добавлять к параметрам старта сервисов ключ -t, который убирает служебную инфу.
В общем немного помучавшись, и не имея ничего хорошего в логах, я все вернул обратно и опять пошел читать доку к 3proxy. Практически сразу зацепился за опцию maxconn, которая как на зло по-умолчанию равна 100 (в версиях с 0.7 — 500). Это ж я так никуда не уеду. Прописал в maxconn большее значение. Потом еще выяснил опытным путем, смотрев на вывод netstat -Lan, что в значениях maxconn и максимальном числе соединений (maxqlen) есть закономерность. maxqlen = (maxconn / 16) — 1. Так кратность будет ровной. Т.е., например, если maxconn = 2047, то maxqlen = 128.
Не знаю, может это так устроен unix или это заморочки 3proxy.
Ладно kern.ipc.somaxconn теперь равен 512, maxconn равен 8191. На второй день с утра с энтузиазмом повторил все действо с новыми настройками. Опять все то же самое — отваливается. Начал грешить на опции timeout. Поменял их на:
timeouts 10 15 30 60 180 1800 15 60
Третий день — те же пироги! Google Chrome вообще сказал «ERR_EMPTY_RESPONSE». Вернул все в зад на squid, т.к. не помогает timeout. В этот день я решил, что как-то совсем тупо и в лоб с этим всем пытаюсь разобраться. Вернулся к тестовому стенду, убрал -t из старта сервисов, переключался на стандартный формат логов, взял ab из apache в руки и натравил его на тестовую проксю. Типа такого и сразу несколько раз подряд:
# ab -k -r -n 50000 -c 5000 -X proxy-test.example.org:8080 'http://ya.ru/index.html'
И тут в логе 3proxy глаз зацепился за что-то необычное. Перемотал лог. Стрелять-колотить, вот оно!
pthread_create():_Resource_temporarily_unavailable
Ёмана, как все просто оказывается ;). 3proxy исчерпывает системный лимит тредов на один процесс. Sysctl’ка kern.threads.max_threads_per_proc говорит, что лимит по-умолчанию 1500. Есть еще одна хорошая sysctl переменная, которая показывает сколько раз в системе процессы достигали этого предела. Вот она — kern.threads.max_threads_hits. И конечно, у меня она не равна нулю. Глянул на продакшн сервер, который мучил утром — да, там она тоже ненулевая. Вот оно! Увеличил kern.threads.max_threads_per_proc до 3000.
На след. утро опять запили это все в продакшн и стал ждать наплыва пользователей. Все было лучше, чем в предыдущее утро. Ничего не отваливалось, но со временем начало работать все медленнее и медленнее. Я думал, что это как-то субъективно. Но нет, как только выключил 3proxy и включил squid, разницу сразу увидел. Это был как глоток свежего воздуха!
Тут я почти сдался, лениво начал что-то искать в интернете на эту и тему и где-то в очередном куске документации мелькнуло, что параметр nscache (величина DNS кэша) совсем не используется без задания опции nserver, указывающий на DNS сервер для резолва. У меня, конечно, кроме nscache, не было nserver в конфиге, причем я опустил это для простоты. Пущай из системы узнаёт. Не знаю, поможет ли. Ладно, задал nserver, не надеясь на успех.
После этого я потушил squid и тут же запустил 3proxy. Ура, все пошло как по маслу! Оказывается gethostbyname() в условиях такой нагрузки становится очень дорогим.
Я по началу думал еще, что squid так быстро работает из-за наличия у него веб-кэша. Но, как оказалось, 3proxy работает так же, если его настроить нормально =).
Так я оставил работать 3proxy как основной прокси еще на день. В процессе еще раз израсходовал количество допустимых тредов и процессов, после чего увеличил этот предел до 4000. Вообще в пиковое время ничего не тормозило и была такая картина (сокетов от socks службы там не больше 200):
# sockstat | grep 3proxy | wc -l
5721
З.Ы. Когда 3proxy в превый раз не сдюжил нагрузку, это вообще было типа challende accepted =). Не могу успокоиться, пока не доведу дело до конца и не узнаю в чем проблема.
Конфиг 3proxy:
# cat /usr/local/etc/3proxy.cfg
external 0.0.0.0
internal 192.168.x.x
# It won't use nscache without nserver option
nserver 192.168.x.x
nserver 192.168.x.x
nscache 65536
#
timeouts 1 5 30 60 180 1800 15 60
daemon
log /var/log/3proxy/access.log D
# It's original log format, great for tracking down errors and much more
#logformat "- +_L%t.%. %N.%p %E %U %C:%c %R:%r %O %I %h %T"
# It's squid log format, but it almost hides error messages
logformat "- +_G%t.%. %D %C TCP_MISS/200 %I %1-1T %2-2T %U DIRECT/%R application/unknown"
archiver gz /usr/bin/gzip %F
rotate 7
monitor "/usr/local/etc/3proxy.cfg"
# HTTP proxy settings
#
flush
auth iponly
# These 3 lines show how to redirect
#allow * # Let's allow something and then extend it using parent directive
#parent 1000 http 192.168.2.233 8080 # to squid
#parent 1000 connect 192.168.25.5 3128 # to 3proxy
# !Example!
# allow/deny
deny * * $"/usr/local/etc/3proxy.block_domains"
deny * * 192.168.0.0/16,172.16.0.0/12,10.0.0.0/8
allow * 192.168.0.0/16,172.16.0.0/12,10.0.0.0/8 * 20,21,70,80,81,85,88,210,280,443,488,563,591,777,1024-65535 HTTP
allow * 192.168.0.0/16,172.16.0.0/12,10.0.0.0/8 * 443,563,2083,2096,4074,4443,4445,5900,7772,8080,8087,8090,8443,8444,8510,9080,9443 HTTP_CONNECT
maxconn 8191
proxy -u -t
# SOCKS proxy settings
#
flush
auth iponly
deny * * 192.168.0.0/16,172.16.0.0/12,10.0.0.0/8,127.0.0.1 * * * *
allow * 192.168.0.0/16,172.16.0.0/12,10.0.0.0/8 * 20,21,70,80,81,85,88,210,280,443,488,563,591,777,1024-65535 * * *
maxconn 2047
socks -u -t