поправки к "100-200 тысяч соединений" для FreeBSD 7 ( i386 и amd64 )

Alexey V. Karagodov kav at karagodov.name
Sun Jun 1 05:09:54 MSD 2008


Keywords: freebsd tcp optimization tune speed socket mbuf sendfile  
sysctl
From: Сысоев Игорь Владимирович <http:// 
www.sysoev.ru>
Date: Mon, 1 Oct 2007 14:31:37 +0000 (UTC)
Subject: FreeBSD для обслуживания 100-200 тысяч  
соединений

[[http://rutube.ru/tracks/125719.html?v=23351111db90ae8beace6a841dd2a8f4%EE%C1%D3%D4%D2%CF%CA%CB%C1 
  Видеоролик доклада]]

######## мои дополнения отмечены ######## в  
начале строки
######## в самом низу даны "итоговые" файлы  
конфигурации /boot/loader.conf /etc/sysctl.conf и ядер  
для i386 и amd64

Стенограмма выступления Игоря Сысоева  
с конференции РИТ-2007.


mbuf clusters

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=65536

Для более ранних версий 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 оказалось недостаточно  
указать vm.kmem_size_max=1G в /boot/loader.conf,
######## (параметр был просто игнорирован)  
по-этому это надо сделать в файле  
конфигурации ядра:
######## options VM_KMEM_SIZE=1073741824
######## options VM_KMEM_SIZE_MAX=1073741824
На платформе amd64 KVA всегда 2G, увеличить  
к сожалению в настоящее время нельзя.

Кроме увеличения виртуального  
адресного пространства можно  
увеличить лимит
физической памяти, которую может  
использовать ядро (по умолчанию 320Мб).  
Увеличим
до 1Гб:

	/boot/loader.conf:
	    vm.kmem_size=1G
######## для обеих платформ добавить  
vm.kmem_size_max=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




######## /usr/src/sys/amd64/conf/CUSTOM
include		GENERIC

device		pf
device		pflog
device		pfsync
device		carp

options		ALTQ
options		ALTQ_CBQ
options		ALTQ_RED
options		ALTQ_RIO
options		ALTQ_HFSC
options		ALTQ_CDNR
options		ALTQ_PRIQ
options		ALTQ_NOPCC

#options		QUOTA

options		IPSEC
#options		IPSEC_FILTERGIF
device		crypto
device		cryptodev

options		DEVICE_POLLING
#options		HZ=1000

#options		SCHED_ULE

#options		KVA_PAGES=512

options 	VM_KMEM_SIZE=1073741824
options 	VM_KMEM_SIZE_MAX=1073741824

options		PANIC_REBOOT_WAIT_TIME=60

ident		CUSTOM

######## /usr/src/sys/i386/conf/CUSTOM

include		GENERIC

device		pf
device		pflog
device		pfsync
device		carp

options		ALTQ
options		ALTQ_CBQ
options		ALTQ_RED
options		ALTQ_RIO
options		ALTQ_HFSC
options		ALTQ_CDNR
options		ALTQ_PRIQ
options		ALTQ_NOPCC

#options		QUOTA

options		IPSEC
#options		IPSEC_FILTERGIF
device		crypto
device		cryptodev

options		DEVICE_POLLING
#options		HZ=1000

#options		SCHED_ULE

options		KVA_PAGES=512

options 	VM_KMEM_SIZE=1073741824
options 	VM_KMEM_SIZE_MAX=1073741824

options		PANIC_REBOOT_WAIT_TIME=60

ident		CUSTOM


######## /boot/loader.conf
verbose_loading="YES"
loader_logo="beastie"
#ng_ether_load="YES"
#linux_load="YES"
accf_data_load="YES"
accf_http_load="YES"

net.inet.tcp.syncache.hashsize=1024
net.inet.tcp.syncache.bucketlimit=100
net.inet.tcp.tcbhashsize=4096
kern.ipc.nsfbufs=10240
vm.kmem_size=1G
vm.kmem_size_max=1G

######## /etc/sysctl.conf
net.inet.tcp.blackhole=1
net.inet.udp.blackhole=1
kern.ipc.nmbclusters=262144
kern.ipc.somaxconn=4096
kern.ipc.maxsockets=204800
kern.maxfiles=204800
kern.maxfilesperproc=200000
net.inet.ip.portrange.first=1024
net.inet.ip.portrange.last=65535
net.inet.ip.portrange.randomized=0
net.inet.tcp.recvspace=8192
net.inet.tcp.sendspace=16384
net.inet.tcp.maxtcptw=40960
net.inet.tcp.msl=30000
net.inet.tcp.syncookies=1
net.inet.tcp.nolocaltimewait=1
net.inet.tcp.fast_finwait2_recycle=1


######## после всех мытарств, работа  
системы весьма порадовала.
######## комменты только приветствуются.
######## огромное пожелание к Игорю  
включить сей опус с поправками и  
комментариями (кои лично меня ОЧЕНЬ  
интересуют) в документацию на своём  
сайте.

######## данные конфиги являются  
результатом печального опыта. если не  
указывать vm.kmem_size_max, ядру нехватит  
памяти, для всей этой красоты.
######## успехов

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://nginx.org/pipermail/nginx-ru/attachments/20080601/6e73546c/attachment.html>


More information about the nginx-ru mailing list