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

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *