Привет!
Продолжаем изучать маршрут сетевого пакета по подсистемам ядра linux.
В первой части мы разобрали отрезок от сетевой карты гипервизора до txqueue
- очередь перед виртуальной машиной в которой и крутится наш сервис.
Теперь разберем следующий отрезок - от сетевой карты ВМ и до самого приложения.
RX queue
Процесс обработки трафика виртуальной машиной идентичен с гипервизоров - softirqd
демоны разгребают RX
очереди. Ничего нового.
Различия начинаются на уровнях сетевого стека, точнее перед ним.
input_pkt_queue
После RXq
пакет прибывает к input_pkt_queue
- очередь перед стеком протоколов TCP/IP.
Переполнение input_pkt_queue
ведет к росту значения во второй колонке файла /proc/net/softnet_stat
, а node_exporter содержит метрику node_softnet_dropped_total
.
Узнать максимальную длину, а так же изменить значение можно командой sysctl net.core.netdev_max_backlog
.
Теперь поднимемся на уровень TCP протокола.
если создается новое соединение…
Для обмена данными, например по HTTP, предварительно требуется установить соединение. Этот процесс называют tcp three-way handshake.
При инициализации соединение меняет свое состояние, например SYN_RECV->ESTABLISHED
для server или SYN_SENT->ESTABLISHED
для client.
Введем еще две очереди - SYN Queue
и Accept Queue
.
Процесс со стороны server:
- поступает
SYN
пакет; - соединение переходит в состояние
SYN_RECV
и попадает вSYN Queue
; SYN-ACK
пакет направляется к client;- поступает клиентский
ACK
; - соединение переходит в состояние
ESTABLISHED
и перемещается вAccept Queue
; - приложение асинхронно вызывает
accept()
и начинает обслуживать
Подробно процесс описан в блоге alibaba cloud.
Наблюдение:
SYN Queue
netstat -s | grep -i "listen"
node_netstat_TcpExt_ListenDrops
метрика в node_exporter
Accept Queue
ss -ntl
- текущий размер очереди (Recv-Q
) и максимальный размер очереди (Send-Q
)netstat -s | grep -i "listen"
node_netstat_TcpExt_ListenOverflows
метрика в node_exporter
Тюнинг
SYN Queue
В ядрах версий 2.6.20+ вычислить значение непросто:
backlog = min(somaxconn, backlog)
nr_table_entries = backlog
nr_table_entries = min(backlog, sysctl_max_syn_backlog)
nr_table_entries = max(nr_table_entries, 8)
// roundup_pow_of_two: 将参数向上取整到最小的 2^n,注意这里存在一个 +1
nr_table_entries = roundup_pow_of_two(nr_table_entries + 1)
max_qlen_log = max(3, log2(nr_table_entries))
max_queue_length = 2^max_qlen_log
Для упрощения вычисления можно по-прежнему ориентироваться на файл /proc/sys/net/ipv4/tcp_max_syn_backlog
, но о нюансах стоит помнить.
Accept Queue
min(somaxconn, backlog,sysctl_max_syn_backlog)
, гдеsomaxconn
-/proc/sys/net/core/somaxconn
backlog
- параметр системного вызоваint listen(int sockfd, int backlog)
- например в nginx существует одноименная директива
backlog
.
- например в nginx существует одноименная директива
если соединение уже установлено…
Напомню, что TCP протокол гарантирует надежную доставку и правильный порядок следования пакетов. Для этого проверяется их порядковый номер - sequence number или SYN
.
Если порядок следования не нарушается, то пакет помещается напрямую в буфер сокета (recvQ
). В противном случае в OFO Queue
(out of order), где он дожидается запоздавших соседей и восстановления порядка следования. Только после этого пакет отправят в recvQ
.
Out of order пакеты негативно влияют на производительность:
- провоцируют механизм fast retransmits, а значит понижают объем полезного трафика, т.н. goodput;
- повышают latency - тратится время на ожидание и восстановление правильного порядка следования пакетов.
Причины возникновения могут быть в нескольких маршрутов в рамках одного TCP потока и/или при потерях на сетевом оборудовании.
Проблематика out of order пакетов обсуждалась в докладе Тюним память и сетевой стек в Linux / Дмитрий Самсонов (Одноклассники), советую ознакомиться.
Наблюдение
OFO Queue
netstat -s | grep -E 'TCPOFOQueue|TCPOFODrop'
- метрики node_exporter:
node_netstat_TcpExt_TCPOFOQueue
- объем поступающихOFO
пакетов;node_netstat_TcpExt_TCPOFODrop
- дропы пакетов при переполнении;
в релизе node_exporter 1.8.0 версии метрика
TCPOFOQueue
стала дефолтной для коллектора netstat.
recvQ
netstat -s | grep -E 'TCPRcvQDrop|TCPRcvCollapsed'
- дропы пакетов при переполнении сокета;- метрики node_exporter:
node_netstat_TcpExt_TCPRcvQDrop
- дропы пакетов при переполнении сокета;node_netstat_TcpExt_TCPRcvCollapsed
- прежде чем дропать при переполнении сокета, ядро “из последних сил” пытается стабилизировать ситуацию.
В свое время ребята из Cloudflare провели исследование как TCP collapse
влияет на производительность, must read.
Тюнинг
OFO Queue
значение задается при создании сокета через системный вызов sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_REORDERING, 65536)
recvQ
net.ipv4.tcp_mem
- объем памяти всех tcp буферов в системеnet.core.rmem_max
- объем памяти tcp буферов приема в системеnet.ipv4.tcp_rmem
- объем конкретного экземпляра tcp буфера
В следующей части соберем всё воедино и сделаем дашборд для удобного наблюдения за каждым из компонентов.
Удачи!