Я тестирую один из своих серверов под нагрузкой, используя постоянный поток новых сетевых подключений, 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.