Назад | Перейти на главную страницу

Соединения TIME_WAIT не очищаются по истечении периода ожидания

Я тестирую один из своих серверов под нагрузкой, используя постоянный поток новых сетевых подключений, tcp_fin_timeout установлен на 60, поэтому, если я отправлю постоянный поток примерно из 100 запросов в секунду, я ожидаю увидеть скользящее среднее 6000 (60 * 100) соединений в TIME_WAIT состояние, это происходит, но глядя в netstat (используя -o), чтобы увидеть таймеры, я вижу такие соединения, как:

TIME_WAIT   timewait (0.00/0/0)

если их тайм-аут истек, но соединение все еще висит, у меня в конечном итоге кончаются соединения. Кто-нибудь знает, почему эти соединения не очищаются? Если я перестану создавать новые соединения, они в конечном итоге исчезнут, но пока я постоянно создаю новые соединения, они этого не делают, похоже, что у ядра нет возможности их очистить? Есть ли какие-то другие параметры конфигурации, которые мне нужно установить для удаления соединений, как только они истекли?

На сервере работает Ubuntu, а мой веб-сервер - nginx. Также у него есть iptables с отслеживанием соединений, не уверен, что это вызовет эти TIME_WAIT связи, чтобы жить дальше.

Спасибо, Марк.

Эта проблема была интересной, как я часто задавался вопросом. Я сделал пару тестов и обнаружил интересные результаты. Если я открываю одно соединение с сервером и жду 60 секунд, оно неизменно очищается (никогда не доходило до 0,00 / 0/0). Если я открывал 100 подключений, они тоже очищались через 60 секунд. Если бы я открыл 101 соединение, я бы начал видеть соединения в указанном вами состоянии (которое я также видел раньше). И они, кажется, длятся примерно 120 секунд или 2xMSL (что составляет 60) независимо от того, какое значение fin_timeout установлено. Я покопался в исходном коде ядра и нашел то, что, по моему мнению, является «причиной». Похоже, есть код, который пытается ограничить количество жаток сокета, которое происходит за «цикл». Сама тактовая частота устанавливается по шкале на основе HZ:

linux-source-2.6.38/include/net/inet_timewait_sock.h:
     35 #define INET_TWDR_RECYCLE_SLOTS_LOG     5
     36 #define INET_TWDR_RECYCLE_SLOTS         (1 << INET_TWDR_RECYCLE_SLOTS_LOG)
     37 
     38 /*
     39  * If time > 4sec, it is "slow" path, no recycling is required,
     40  * so that we select tick to get range about 4 seconds.
     41  */
     42 #if HZ <= 16 || HZ > 4096
     43 # error Unsupported: HZ <= 16 or HZ > 4096
     44 #elif HZ <= 32
     45 # define INET_TWDR_RECYCLE_TICK (5 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     46 #elif HZ <= 64
     47 # define INET_TWDR_RECYCLE_TICK (6 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     48 #elif HZ <= 128
     49 # define INET_TWDR_RECYCLE_TICK (7 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     50 #elif HZ <= 256
     51 # define INET_TWDR_RECYCLE_TICK (8 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     52 #elif HZ <= 512
     53 # define INET_TWDR_RECYCLE_TICK (9 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     54 #elif HZ <= 1024
     55 # define INET_TWDR_RECYCLE_TICK (10 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     56 #elif HZ <= 2048
     57 # define INET_TWDR_RECYCLE_TICK (11 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     58 #else
     59 # define INET_TWDR_RECYCLE_TICK (12 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     60 #endif
     61 
     62 /* TIME_WAIT reaping mechanism. */
     63 #define INET_TWDR_TWKILL_SLOTS  8 /* Please keep this a power of 2. */
The number of slots is also set here:
     65 #define INET_TWDR_TWKILL_QUOTA 100

В фактическом коде timewait вы можете увидеть, где он использует цитату, чтобы остановить отключение соединений TIME_WAIT, если уже выполнено слишком много:

linux-source-2.6.38/net/ipv4/inet_timewait_sock.c:
    213 static int inet_twdr_do_twkill_work(struct inet_timewait_death_row *twdr,
    214                                     const int slot)
    215 {
...
    240                 if (killed > INET_TWDR_TWKILL_QUOTA) {
    241                         ret = 1;
    242                         break;
    243                 }

Здесь есть дополнительная информация о том, почему HZ установлен на то, что это такое: http://kerneltrap.org/node/5411 Но нередко его увеличивают. Однако я думаю, что обычно более распространено включение tw_reuse / recycling, чтобы обойти этот механизм ведра / квоты (что кажется мне запутанным сейчас, когда я читал об этом, увеличение HZ было бы гораздо более безопасным и чистым решением). Я опубликовал это как ответ, но думаю, здесь можно было бы больше обсудить, как это «правильно» исправить. Спасибо за интересный вопрос!

Вместо того, чтобы использовать tcp_tw_recycle = 1 используйте следующее:

tcp_tw_reuse = 1

Отчеты о повторном использовании не работают и в некоторых случаях не работают, когда вы используете NAT или балансировку нагрузки.

net.ipv4.tcp_fin_timeout по умолчанию 60 секунд. Мне никогда не было ясно, почему сокеты, как правило, остаются в TIME_WAIT дольше этого предела.

Сообщается, что tcp_tw_recycle не работает, я не знаю, так как не использую его. Вероятно, вам нужно установить tcp_tw_reuse в 1, но предположительно это вызовет проблемы с NAT.