Я использую относительно простой VPS (Media Temple (ve)) с несколькими веб-сайтами на основе PHP и (в конечном итоге) с несколькими Узел серверы. Чтобы включить поддержку WebSockets, я использую HAProxy на порту 80, который направляет либо nginx, либо определенный процесс Node.
Однако недавно я столкнулся с проблемой, когда в течение примерно 24 часов мой сервер достигает максимально допустимого количества открытых TCP-соединений (numtcpsock
в Parallels Power Panel, который установлен на 1000). Запуск только nginx не вызывает этой проблемы, и в настоящее время у меня нет активных внутренних серверов Node. Nginx подключается к PHP через сокет домена UNIX (и опять же, проблема возникает не только с nginx). Есть мысли о том, что могло вызвать это? Моя конфигурация:
global
## 00-base
maxconn 500
nbproc 2
defaults
## 00-base
mode http
frontend all
## 00-ports
bind 0.0.0.0:80
## 10-config
timeout client 86400000
default_backend nginx
backend nginx
## 00-timeouts
timeout http-keep-alive 5000
timeout server 10000
timeout connect 4000
## 10-servers
server main localhost:8000
Заранее спасибо!
ОБНОВИТЬ: после немного lsof
Я смог определить, что более 90% открытых сокетов TCP действительно принадлежат HAProxy, и подавляющее большинство из них находятся в CLOSE_WAIT
или FIN_WAIT2
состояния. Это ошибка HAProxy? Это похоже на утечку файлового дескриптора, если только это не неправильная конфигурация с моей стороны.
ОБНОВЛЕНИЕ 2: Я заметил закономерность в lsof
вывод. Мне кажется, что происходит то, что nginx закрывает внутреннее соединение с HAProxy, но до того, как HAProxy формально закрывает его, он пытается закрыть внешнее соединение с клиентом (помещая его в FIN_WAIT2
). Поскольку FIN никогда не приходит, связь между nginx и HAProxy остается неизменной. CLOSE_WAIT
навсегда. Теперь единственный вопрос: почему это происходит?
много сокетов в CLOSE_WAIT - это очень плохо для вашего сервера. Это состояние происходит, когда ядро ожидает, пока программное обеспечение пользовательского пространства не примет закрытие сокета. Если многие сокеты долгое время находятся в этом состоянии - значит, программное обеспечение, использующее их, не отвечает. Обычно сокет в этом состоянии потребляет относительно много времени процессора ядра.
Я полагаю, что CLOSE_WAIT в вашем случае является вторичным по отношению к FIN_WAIT2 - HAproxy ожидает закрытия клиентского соединения и закрывает соединение с NGINX сразу после этого.
FIN_WAIT2 - это состояние ожидания другой стороной подтверждения закрытия сокета. Их не так уж и плохо, но это может указывать на перегрузку сети или большие потери.
Можешь попробовать nolinger
вариант с haproxy, чтобы быстрее закрыть соединение. Но будьте осторожны, это нарушит механизм гарантированной доставки tcp.
Проблема вызвана очень большим тайм-аутом. С 24-часовым таймаутом и ограничением до 1000 одновременных подключений вы можете рассчитывать заполнить его клиентами, отключающими грязный путь. Пожалуйста, используйте более разумный тайм-аут, от минут до часов самое большее, действительно нет смысла использовать однодневные тайм-ауты в Интернете. Как сказал DukeLion, система ожидает, пока haproxy закроет соединение, потому что haproxy не получил закрытие от клиента.
Haproxy работает в туннельном режиме для TCP и WebSocket, он следует обычному четырехстороннему закрытию:
- receive a close on side A
- forward the close on side B
- receive the close on side B
- forward the close on side A
В вашем случае я полагаю, что сторона A была сервером, а сторона B - клиентом. Итак, nginx закрылся через некоторое время, сокет перешел в CLOSE_WAIT, haproxy перенаправил близко к клиенту, этот сокет перешел на FIN_WAIT1, клиент ACKed, передал сокет в FIN_WAIT2, а затем ничего не происходит, потому что клиент исчез, что очень часто в сети. И ваш тайм-аут означает, что вы хотите, чтобы это оставалось таким в течение 24 часов.
Через 24 часа ваши сеансы начнут тайм-аут на стороне клиента, поэтому haproxy убьет их и перенаправит close на сторону nginx, избавившись от него тоже. Но очевидно, что вы этого не хотите, WebSocket был спроектирован таким образом, чтобы простаивающее соединение могло быть открыто повторно открыто, поэтому нет причин держать незанятое соединение открытым в течение 24 часов. Никакой брандмауэр не остановит это!