Bsdadmin.ru

Записки администратора FreeBSD

Путь на сайте

Домашняя FreeBSD FreeBSD для обслуживания 100-200 тысяч соединений

FreeBSD для обслуживания 100-200 тысяч соединений

FreeBSD хранит сетевые данные в mbuf clusters, размер каждого 2Кб, но из них используется только около 1500 байт (по размеру Ethernet пакета). 
 
mbufs 
Для каждого mbuf кластера нужен "mbuf", который имеет размер 256 байт и нужен для организации связи цепочек из mbuf кластеров. В mbuf можно поместить полезную информацию в районе 100 байт, но это не всегда используется. 
Если в машине 1Гб и больше памяти, то по умолчанию будет создано 25 тыс. mbuf кластеров, что не всегда достаточно. 
При ситуации исчерпания числа свободных mbuf кластеров FreeBSD попадает в состояние zonelimit и перестает отвечать на запросы по сети, в top это выглядит как "zoneli". Единственная возможность как-то повлиять на ситуацию - это зайти с локальной консоли и перезагрузить систему, уничтожить процесс находящийся в состоянии "zoneli" невозможно. Для Linux 2.6.x данная проблема тоже характерна, причем работать переставала даже консоль.  Читать далее
 
PID USERNAME  THR PRI NICE   SIZE    RES STATE    TIME   WCPU COMMAND 
13654   nobody      1   4    0 59912K 59484K zoneli 209:26  0.00% nginx 
  
Для выхода из этой ситуации существует патч возвращающий приложению ошибку ENOBUFS, сигнализирующий о попадании в состояние "zoneli", после чего программа может закрыть лишние соединения. К сожалению патч пока не принят в состав FreeBSD. 
 
Состояние задействованных mbuf кластеров можно посмотреть командой: 
$netstat -m 
 4/1421/1425 mbufs in use (current/cache/total) 
 0/614/614/25600 mbuf clusters in use (current/cache/total/max) 
 
Увеличение числа mbuf кластеров во FreeBSD 6.2 можно произвести в любой момент через параметр  kern.ipc.nmbclusters: 
sysctl kern.ipc.nmbclusters=65563
 
Для более ранних версий FreeBSD число mbuf кластеров можно было установить толькона этапе загрузки: 
/boot/loader.conf: 
 kern.ipc.nmbclusters=65536 
  
 25000 mbuf clusters = 55M 
 32768 mbuf clusters = 74M 
 65536 mbuf clusters = 144M 
  
25000 mbuf кластеров занимают примерно 50Мб памяти, 32000 - 74 Мб, 65000 - 144Мб (рост по степени 2). 65000 - пограничное значение, превышать которое не рекомендуется, без предварительного расширения адресного пространства доступного ядру. 
 
Увеличение памяти, доступной ядру 
 Увеличение адресного пространства ядра, которое на i386 платформе - 1Гб. 
Для увеличения до 2Гб, в файле конфигурации ядра необходимо указать: 
options KVA_PAGES=512
 
На платформе amd64 KVA всегда 2G, увеличить к сожалению в настоящее время нельзя.
 
Кроме увеличения виртуального адресного пространства можно увеличить лимит физической памяти, которую может использовать ядро (по умолчанию 320Мб). Увеличим до 1Гб: 
/boot/loader.conf: 
 vm.kmem_size=1G 
 
Затем выделим из него 575Мб под mbuf кластера: 
sysctl -w kern.ipc.nmbclusters=262144
 
Установление соединения. syncache и syncookies 
Примерно 1000 байт расходуется на одно соединение. 
Примерно 100 байт для одной записи на незаконченное соединение в syncache. 
Всего можно держать в памяти информацию об около 15000 соединениях. 
 
Параметры syncache можно посмотреть через "sysctl net.inet.tcp.syncache" (доступен в режиме только для чтения): 
# sysctl net.inet.tcp.syncache 
       net.inet.tcp.syncache.bucketlimit: 30 
       net.inet.tcp.syncache.cachelimit: 15359 
       net.inet.tcp.syncache.count: 29 
       net.inet.tcp.syncache.hashsize: 512 
       net.inet.tcp.syncache.rexmtlimit: 3 
 
Изменить параметры syncache можно только на этапе загрузки ядра: 
/ boot/loader.conf: 
       net.inet.tcp.syncache.hashsize=1024 
       net.inet.tcp.syncache.bucketlimit=100 
 
 Когда новое соединение не помещается в переполненный syncache, FreeBSD переходит в   режим "syncookies" (TCP SYN cookies). Включается возможность такого перехода через: 
sysctl net.inet.tcp.syncookies=1
 
Заполненность syncache и статистику по syncookies можно посмотреть через команду: 
netstat -s -p tcp 
           ... 
           2079088720 syncache entries added 
           ... 
           > 0 dropped 
           2058506523 completed 
           > 0 bucket overflow 
           > 0 cache overflow 
           ... 
           0 cookies sent 
           0 cookies received 
 
После того как соединение принято оно попадает в "listen socket queue". Статистику можно посмотреть командой 
$netstat -Lan 
           Current listen queue sizes (qlen/incqlen/maxqlen) 
           Proto Listen         Local Address         
           tcp4  43/0/4096      *.80                   
           tcp4  0/0/128        *.22 
 
 
4096 - размер очереди (максимум 65тыс.) 
43 - заполненность очереди в данный момент (приложение не вытащило из очереди). 
 
Увеличение размера очереди производится через: 
sysctl kern.ipc.somaxconn=4096
 
После того как соединение принято для него FreeBSD создает структуры связанные с сокетами (sockets).  
Увеличить максимальное число открытых сокетов во FreeBSD 6.2, во время работы, можно через: 
sysctl kern.ipc.maxsockets=204800
 
В более ранних версиях: 
/boot/loader.conf: 
           kern.ipc.maxsockets=204800 
 
Посмотреть состояние можно командой: 
 
$vmstat -z 
       ITEM      SIZE     LIMIT      USED      FREE    REQUESTS  FAILURES 
       ... 
       socket:    356,   204809,    48041,   114869, 4292783585,        0 
       ... 
       inpcb:     180,   204820,    63956,   121460, 4283258030,        0 
       tcpcb:     464,   204800,    48015,   114897, 4283258030,        0 
 
 
tcb hash 
Если машина обрабатывает несколько десятков тысяч соединений то tcb hash позволяет быстро 
определять принадлежность пришедшего пакета к определенному соединению. 
 
По умолчанию размер tcb hash - 512 элементов. 
Текущий размер можно посмотреть через sysctl (только для чтения): 
sysctl net.inet.tcp.tcbhashsize
 
Изменить можно на этапе загрузки: 
/boot/loader.conf: 
           net.inet.tcp.tcbhashsize=4096 
 
Файлы 
 
Приложения работают не с сокетами, а с файлами. По этому для каждого сокета нужна структура, которая описывает файл. Увеличить можно через: 
sysctl kern.maxfiles=204800 
sysctl kern.maxfilesperproc=200000 
 
kern.maxfiles - всего файлов в системе 
kern.maxfilesperproc - максимальное число файлов на один процесс. 
 
Параметры можно менять на работающей системе, но они не отразятся на уже запущенных процессах. Поэтому в nginx были добавлены директивы позволяющие менять число открытых файлов для уже запущенных процессов (после инициирования операции переконфигурации) 
 
nginx.conf: 
           worker_rlimit_nofile  200000; 
           events { 
             worker_connections  200000; 
           } 
 
receive buffers  
Буферы для приема данных. По умолчанию 64Kб, если нет загрузки больших объемов данных, то можно уменьшить до 8Кб (меньше вероятность переполнения при DoS атаке). 
 
sysctl net.inet.tcp.recvspace=8192
 
Для nginx: 
 
nginx.conf: 
           listen 80 default rcvbuf=8k; 
 
 
send buffers 
 Буферы для отправки данных. По умолчанию 32K. Если скачиваются данные небольшого объема 
или недостаток mbuf кластеров, можно уменьшить:  
sysctl net.inet.tcp.sendspace=16384
 
 
Для nginx: 
 
nginx.conf: 
           listen 80 default sndbuf=16k; 
 
 В ситуации когда сервер записал в сокет данные, но клиент не хочет их забирать, после таймаута по закрытию соединения в ядре данные будут держаться еще несколько минут. В nginx если директива для принудительного сброса всех данных после закрытия по таймауту. 
 
nginx.conf: 
               reset_timedout_connections  on; 
 
 
 
sendfile 
 
Еще один механизм для экономии mbuf кластеров - это sendfile, он использует память буферов ядра с данными файлов одновременно для передачи этих данных в сетевую карту, без промежуточного заполнения лишних буферов. 
 
В nginx включается: 
nginx.conf: 
           sendfile on; 
 
 
На i386 по умолчанию для систем с 1 или более Гб памяти будет выделено 6656 sendfile буферов. Этого вполне достаточно. На платформе amd64 более оптимальный вариант реализации и sfbufs буферы не нужны. 
 
Статистика: 
 
i386>netstat -m 
       ... 
       190/510/6656 sfbufs in use (current/peak/max) 
        
       amd64>netstat -m 
       ... 
       0/0/0 sfbufs in use (current/peak/max) 
 
 
При переполнении sendfile буфера процесс замирает в состоянии "sfbufa", но ситуация достаточно быстро приходит в норму после увеличения размера буфера. 
 
PID USERNAME  THR PRI NICE   SIZE    RES STATE    TIME   WCPU COMMAND 
13654 nobody      1   4    0 59912K 59484K sfbufa 209:26  5.00% nginx 
 
Увеличение размера через: 
/ boot/loader.conf: 
           kern.ipc.nsfbufs=10240 
 
 
 
TIME_WAIT 
 После того как соединение закрывается сокет переходит в состояние TIME_WAIT 
В этом состоянии он может находится по умолчанию в течение 60 секунд. Время можно изменить через sysctl (в миллисекундах деленных на 2, 2 x 30000 MSL = 60 секунд): 
 
sysctl net.inet.tcp.msl=30000
 
Во FreeBSD 6.2 TIME_WAIT сокеты обрабатываются отдельно (нужна лишь часть информации 48 байт из 1 Кб. Ограничение вне лимита kern.ipc.maxsockets), число их регулируется параметром: 
 
sysctl net.inet.tcp.maxtcptw=40960
 
Статистика: 
 
>vmstat -z  
       ITEM      SIZE     LIMIT      USED      FREE    REQUESTS  FAILURES 
       ... 
       tcptw:      48,    41028,    15941,    25087, 1045159949,   438573 
 
 
TCP/IP ports 
 По умолчанию исходящие соединения инициируются с диапазона портов 49152-65535 (16 тыс.). 
Их неплохо увеличить (1024-65535): 
 
sysctl net.inet.ip.portrange.first=1024 
sysctl net.inet.ip.portrange.last=65535
 
Для использования портов по порядку, вместо случайной выборки (для исключения ошибки повторного коннекта с одного порта до отработки TIME_WAIT): 
 
sysctl net.inet.ip.portrange.randomized=0
 
 Во FreeBSD 6.2 появилась возможность не создания состояния TIME_WAIT для соединений в рамках localhost:  
sysctl net.inet.tcp.nolocaltimewait=1

Домашняя FreeBSD FreeBSD для обслуживания 100-200 тысяч соединений