Разъяснения по поводу размера окна Linux TCP и задержек

У меня есть задержки при отправке данных по каналу TCP, я не могу понять. Ссылка является 1-гигабитной ссылкой с задержкой между концами около 40 мс. В моей текущей настройке задержка (время от одного сообщения до перехода из пользовательского пространства отправителя в пользовательское пространство получателя) может достигать 100 мс.

Сокет отправителя настраивается с параметром TCP_NODELAY. Буфер отправителя (SO_SNDBUF) настроен на 8 МБ. Буфер приема (SO_RCVBUF) также настроен на 8 МБ. Масштабирование окна Tcp активировано.

update-1: я использую промежуточное программное обеспечение zeromq 3.1.1 для переноса данных. Конфигурация сокета, включая флаг TCP_NODELAY, выполняется промежуточным программным обеспечением. Доступны некоторые параметры, такие как размеры буфера передачи rx и tx, но не TCP_NODELAY. Насколько я понял, TCP_NODELAY активируется, чтобы гарантировать, что данные отправляются по возможности. Между тем фактические отправки сокетов и решение об отправке сообщения выполняются в двух отдельных потоках. Надлежащая группировка выполняется, если в момент отправки первого сообщения в пакете доступно несколько сообщений.

Я запустил захват с tcpdump, из которого были извлечены кадры ниже. После первоначального TCP-рукопожатия отправитель (172.17.152.124) начинает отправку данных. Начальный размер окна составляет 5840 байтов для получателя и 5792 байта для отправителя.

Моя проблема в том, что отправитель отправляет два кадра (#6 и #7), а затем останавливается, ожидая подтверждения получения от получателя. Насколько я вижу, размер окна получателя не достигнут, и передача не должна останавливаться (384 байта ожидают при начальном размере окна приема 5840 байтов). Я начинаю думать, что я не понял правильно, что такое TCP. Может кто-нибудь помочь прояснить?

update-2: мои данные состоят из магического числа, за которым следует метка времени. Я изолировал задержанные пакеты, сравнивая временные метки полезных нагрузок с временными метками, установленными tcpdump. Полезная нагрузка ts кадра № 9 очень близка к полезной нагрузке кадра № 6 и № 7 и явно меньше, чем отметка времени принятого подтверждения в кадре № 8.

update-1: тот факт, что кадр № 9 отправляется не сразу, можно объяснить медленным запуском канала TCP. Фактически, проблема также появляется, когда соединение работает в течение нескольких минут, поэтому медленный запуск не является общим объяснением.

  1. 20: 53: 26.017415 IP 172.17.60.9.39943> 172.17.152.124.56001: Флаги [S], seq 2473022771, победа 5840, опции [mss 1460,sackOK,TS val 4219180820 ecr 0,nop,wscale 8], длина 0

  2. 20: 53: 26.017423 IP 172.17.152.124.56001> 172.17.60.9.39943: Flags [S.], seq 2948065596, ack 2473022772, победа 5792, варианты [mss 1460,sackOK,TS val 186598852 ecr 219180820,nop,wscale 9], длина 0

  3. 20: 53: 26.091940 IP 172.17.60.9.39943> 172.17.152.124.56001: Flags [.], Ack 1, win 23, варианты [nop,nop,TS val 4219180894 ecr 186598852], длина 0

  4. 20: 53: 26.091958 IP 172.17.60.9.39943> 172.17.152.124.56001: Флаги [P.], сек. 1:15, кв. 1, ш в 23, опции [nop,nop,TS val 4219180895 ecr 186598852], длина 14

  5. 20: 53: 26.091964 IP 172.17.152.124.56001> 172.17.60.9.39943: Flags [.], Ack 15, win 12, варианты [nop,nop,TS val 186598927 ecr 4219180895], длина 0

  6. 20: 53: 26.128298 IP 172.17.152.124.56001> 172.17.60.9.39943: Флаги [P.], сек. 1: 257, кв. 15, победа 12, опции [нет, нет,TS val 186598963 ecr 4219180895], длина 256

  7. 20: 53: 26.128519 IP 172.17.152.124.56001> 172.17.60.9.39943: Flags [P.], seq 257: 385, ack 15, win 12, варианты [nop,nop,TS val 186598963 ecr 4219180895], длина 128

  8. 20: 53: 26.202465 IP 172.17.60.9.39943> 172.17.152.124.56001: Flags [.], Ack 257, win 27, варианты [nop,nop,TS val 4219181005 ecr 186598963], длина 0

  9. 20: 53: 26.202475 IP 172.17.152.124.56001> 172.17.60.9.39943: Flags [.], Seq 385:1833, ack 15, win 12, варианты [nop,nop,TS val 186599037 ecr 4219181005], длина 1448

  10. 20: 53: 26.202480 IP 172.17.152.124.56001> 172.17.60.9.39943: Флаги [P.], сек. 1833: 2305, кв. 15, победа 12, опции [нет, нет, TS val 186599037 ecr 4219181005], длина 472

Если это имеет значение, оба конца являются Linux RHEL5, с ядрами 2.6.18, а сетевые карты используют драйверы e1000e.

update-3 Содержимое /etc/sysctl.conf

[jlafaye@localhost ~]$ cat /etc/sysctl.conf | grep -v "^#" | grep -v "^$" 
net.ipv4.ip_forward = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
kernel.sysrq = 0
kernel.core_uses_pid = 1
net.ipv4.tcp_syncookies = 1
kernel.msgmnb = 65536
kernel.msgmax = 65536
kernel.shmmax = 68719476736
kernel.shmall = 4294967296
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 1048576
net.core.wmem_default = 1048576
net.ipv4.tcp_rmem = 65536 4194304 16777216
net.ipv4.tcp_wmem = 65536 4194304 16777216 
net.core.netdev_max_backlog = 10000 
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_mem = 262144 4194304 16777216
kernel.shmmax = 68719476736

2 ответа

Пройдя немного больше в моем трафике, я смог увидеть, что мои данные были не чем иным, как последовательностью небольших пакетов с небольшими периодами простоя между ними.

С полезным инструментом ssЯ смог получить текущий размер окна перегрузки моего соединения (см. cwnd значение на выходе):

[пользователь @ localhost ~] $ / usr / sbin / ss -i -t -e | grep -A 1 56001

ESTAB 0 0 192.168.1.1:56001
192.168.2.1:45614 uid: 1001 ino: 6873875 sk: 17cd4200ffff8804 ts sscscalable wscale:8,9 rto:277 rtt:74/1 ato:40 cwnd:36 отправьте 5.6Mbps rcv_space:5792

Я запустил инструмент несколько раз и обнаружил, что размер окна перегрузки регулярно сбрасывался до первоначального значения (10 мс, на моем компьютере с Linux). Соединение постоянно возвращалось к фазе медленного запуска. В течение периода медленного запуска пакеты с числом сообщений, превышающим размер окна, были задержаны в ожидании подтверждений, связанных с первыми пакетами пакета.

Тот факт, что трафик состоит из последовательности пакетов, вероятно, объясняет сброс размера окна перегрузки.

Деактивировав режим медленного запуска после простоя, я смог избавиться от задержек.

[user @ host ~] $ cat / proc / sys / net / ipv4 / tcp_slow_start_after_idle 0

Это не будет чем-то тонким, например, где-то в обстановке. Это будет проблемой с протоколом, расположенным поверх TCP, или ошибкой кода. Для TCP не существует волшебного переключателя "быстрее", за исключением необычных случаев, таких как сети с очень высокой задержкой или потерей пакетов, вызванной шумом.

Наиболее очевидное объяснение было бы, если код вызывает write или же send с очень маленькими кусками. Вам нужно накопить не менее 2 КБ за отправку, в идеале 16 КБ. Вы говорите, что пакетируете сообщения, но не ясно, что это значит. Вы передаете их в один звонок write или же send? Вы объединяете их в единый протокольный блок данных для протокола, расположенного поверх TCP? Выполнение обеих этих вещей очень помогает с задержкой.

Также избавьтесь от TCP_NODELAY. Это может снизить пропускную способность. Это только для приложений, которые не были предназначены для работы с TCP, или для приложений, которые не могут предсказать, какая сторона должна будет передавать следующую.

Если, конечно, вы на самом деле не размещаете протокол поверх TCP, где вы не знаете, какая сторона будет передавать следующую (например, telnet, например). Тогда имеет смысл установить TCP_NODELAY. Для того, чтобы такой протокол работал с минимальными задержками, требуется значительный опыт. Если это ваша ситуация, опубликуйте более подробную информацию о протоколе, который вы размещаете поверх TCP, как выглядят размеры его протокольных блоков данных и что определяет, какая сторона передает когда.

Если вы действительно пакетируете сообщения, доступные одновременно, и передаете их за один звонок write или же sendто, скорее всего, проблема в том, что другая сторона не отправляет подтверждение прикладного уровня для каждого пакета. Они улучшают задержку, предоставляя пакетам TCP ACKs возможность вложения. Ваш протокол должен включать их, чтобы обеспечить чередование сторон, что помогает снизить задержку.

Другие вопросы по тегам