(Не) очевидные особенности настроек TCP сокетов. Часть 2.
Начало тут.
Есть мнение, что механизмы autotuning TCP в Linux не просто так придумали и в норме стоит пользоваться именно ими, а хардкод стоит избегать.
Потому разберемся с настройкой
net.ipv4.tcp_rmem и узнаем оказывает ли на него влияние
net.core.rmem_max:
# man 7 tcp
...
tcp_rmem (since Linux 2.4)
This is a vector of 3 integers: [min, default, max]. These parameters are used by TCP to regulate receive buffer sizes. ...
min minimum size of the receive buffer used by each TCP socket. The default value is the system page size. (On Linux 2.4, the default value is 4 kB, lowered to PAGE_SIZE bytes in low-memory systems.)...
default the default size of the receive buffer for a TCP socket. This value overwrites the initial default buffer size from the generic global net.core.rmem_default defined for all protocols...
max the maximum size of the receive buffer used by each TCP socket. This value does not override the global net.core.rmem_max. This is not used to limit the size of the receive buffer declared using SO_RCVBUF on a socket...
Перефразирую:
- дефолтное значение
tcp_rmem перезаписывает
net.core.rmem_default — это мы заметили в самом начале, до использования
rcvbuf;
- максимальное значение
tcp_rmem НЕ перезаписывает
net.core.rmem_max и
НЕ используется при выставлении
SO_RCVBUF.
Делаем промежуточные вывод, что:
1.
net.core.rmem_max задает жесткий лимит на размер TCP буфера;
2.
tcp_rmem не участвует в игре при выставлении
SO_RCVBUF.
Проверим!
Начнем с конца:
# sysctl -a | grep rmem
net.core.rmem_default = 212992
net.core.rmem_max = 6291456
net.ipv4.tcp_rmem = 4096 131072 18874368
### Выставляем максимальное значение tcp_rmem ниже rmem_max
# sysctl net.ipv4.tcp_rmem="4096 131072 851968"
net.ipv4.tcp_rmem = 4096 131072 851968
# grep listen /etc/nginx/nginx.conf
listen 80 rcvbuf=6291456;
# systemctl restart nginx
# ss -ntlmO | grep '0.0.0.0:80 '
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* skmem:(r0,rb12582912,t0,tb16384,f0,w0,o0,bl0,d0)
Действительно, максимальный
tcp_rmem не сыграл.
Теперь проверим, что
net.core.rmem_max задает жесткий лимит над размером TCP сокетов:
# sysctl -a | grep rmem
net.core.rmem_default = 212992
net.core.rmem_max = 6291456
net.ipv4.tcp_rmem = 4096 131072 851968
### Делаем net.core.rmem_max ниже чем дефолтный tcp_rmem, значение которого поднимем
# sysctl net.core.rmem_max=212992
net.core.rmem_max = 212992
# sysctl net.ipv4.tcp_rmem="4096 524288 851968"
net.ipv4.tcp_rmem = 4096 524288 851968
### Убираем директиву rcvbuf
# grep listen /etc/nginx/nginx.conf
listen 80;
# systemctl restart nginx
# ss -ntlmO | grep '0.0.0.0:80 '
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* skmem:(r0,rb524288,t0,tb16384,f0,w0,o0,bl0,d0)
Размер буфера выставляется равным дефолтному
tcp_rmem (кстати без удвоений), который больше
net.core.rmem_max.
——
Особо пытливым для перепроверки можно обратиться к исходникам:
1. net.core.rmem_max участвует либо в
обработке опции SO_RCVBUF:
...
case SO_RCVBUF:
...
__sock_set_rcvbuf(sk, min_t(u32, val, READ_ONCE(sysctl_rmem_max)));
2. либо в определении
начального TCP окна:
...
space = max_t(u32, space, READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_rmem[2]));
space = max_t(u32, space, READ_ONCE(sysctl_rmem_max));
space = min_t(u32, space, *window_clamp);
*rcv_wscale = clamp_t(int, ilog2(space) - 15,
0, TCP_MAX_WSCALE);
что все таки не бьется с документацией.
Выходит обманывают нас разработчики или я так интерпретирую тексты :)
Окончательные выводы:1.
net.core.rmem_max играет только при ручном выставлении размеров сокетов (
SO_RCVBUF);
2.
net.ipv4.tcp_rmem напротив, не участвует в
SO_RCVBUF, зато позволяет использовать автоподстройку, что в большинстве случаев будет более гибким решением.
В итоге решение задачи может быть следующим:
# sysctl net.ipv4.tcp_rmem="4096 1048576 12582912"
tags: #tcp #linux #kernel