• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Оптимизация веб-серверов для обеспечения высокой пропускной способности и низкой задержки

вавилонец

CPU register
Пользователь
Регистрация
17.06.2021
Сообщения
1 116
Реакции
1 265
1664116324894.png

ОРИГИНАЛЬНАЯ СТАТЬЯ
ПЕРЕВЕДЕНО СПЕЦИАЛЬНО ДЛЯ xss.pro
$600 на SSD для Solidity hacking by Jolah Milovsky---> 0x5B1f2Ac9cF5616D9d7F1819d1519912e85eb5C09

Это расширенная версия моего выступления на NginxConf 2017 6 сентября 2017 года. Как SRE в команде Dropbox Traffic, я отвечаю за нашу сеть Edge: ее надежность, производительность и эффективность. Сеть Dropbox — это прокси-уровень на основе nginx, предназначенный для обработки чувствительных к задержкам транзакций метаданных и передачи данных с высокой пропускной способностью. В системе, которая обрабатывает десятки гигабит в секунду при одновременной обработке десятков тысяч чувствительных к задержке транзакций, оптимизация эффективности / производительности осуществляется во всем стеке прокси, от драйверов и прерываний, через TCP/IP и ядро, до библиотеки и приложения. уровневые тюнинги. В этом посте мы обсудим множество способов настройки веб-серверов и прокси. Пожалуйста, не культивируйте их. Ради научного метода применяйте их один за другим, измеряйте их эффект и решайте, действительно ли они полезны в вашей среде.
Это не пост о производительности Linux, хотя я буду делать много ссылок на инструменты bcc, eBPF и perf, это ни в коем случае не исчерпывающее руководство по использованию инструментов профилирования производительности. Если вы хотите узнать о них больше, вы можете прочитать блог Брендана Грегга .
Это также не пост о производительности браузера. Я коснусь производительности на стороне клиента, когда расскажу об оптимизации, связанной с задержкой, но ненадолго. Если вы хотите узнать больше, вам следует прочитать «Высокопроизводительные браузерные сети » Ильи Григорика.
И это также не сборник лучших практик TLS. Хотя я буду упоминать библиотеки TLS и их настройки несколько раз, вы и ваша команда безопасности должны оценить влияние каждой из них на производительность и безопасность. Вы можете использовать Qualys SSL Test , чтобы проверить свою конечную точку на соответствие текущему набору лучших практик, а если вы хотите узнать больше о TLS в целом, рассмотрите возможность подписки на информационный бюллетень Feisty Duck Bulletproof TLS .

Аппаратное обеспечение

Процессор

Для хорошей асимметричной производительности RSA/EC вам нужны процессоры с поддержкой по крайней мере AVX2 (avx2 в /proc/cpuinfo) и предпочтительно с большие целочисленные арифметические операции (bmi и adx). Для симметричных случаев вам следует искать AES-NI для шифров AES и AVX512 для ChaCha+Poly. У Intel есть сравнение производительности различных поколений оборудования с OpenSSL 1.0.2, которое иллюстрирует эффект разгрузки оборудования. Варианты использования, чувствительные к задержкам, такие как маршрутизация, выиграют от меньшего количества узлов NUMA и отключения HT. Задачи с высокой пропускной способностью лучше справляются с большим количеством ядер и выиграют от Hyper-Threading (если они не привязаны к кешу) и, как правило, не слишком заботятся о NUMA. В частности, если вы идете по пути Intel, вы ищете как минимум процессоры Haswell/Broadwell и в идеале Skylake. Если вы выбираете AMD, EPYC имеет довольно впечатляющую производительность.
Здесь вам нужно хотя бы 10G, а лучше даже 25G. Если вы хотите больше через один сервер по TLS, описанной здесь настройки будет недостаточно, и вам может потребоваться кадрирование TLS на уровень ядра (например FreeBSD , Linux ). Что касается программного обеспечения, вам следует искать драйверы с открытым исходным кодом с активными списками рассылки и сообществами пользователей. Это будет очень важно, если (но, скорее всего, когда) вы будете отлаживать проблемы, связанные с драйверами.

Память

Эмпирическое правило здесь заключается в том, что для задач, чувствительных к задержке, требуется более быстрая память, а для задач, чувствительных к пропускной способности, требуется больше памяти.

Жесткий диск

Это зависит от ваших требований к буферизации / кешированию, но если вы собираетесь много буферизовать или кэшировать, вам следует выбрать хранилище на основе флэш-памяти. Некоторые заходят так далеко, что используют специализированную файловую систему для флэш-памяти (обычно лог-структурированную), но они не всегда работают лучше, чем обычные ext4/xfs. В любом случае, будьте осторожны, чтобы не сжечь флэш-память из-за того, что вы забыли включить TRIM или обновить прошивку.


Операционные системы: Низкий уровень



Прошивка

Вы должны обновлять прошивку, чтобы избежать мучительных и длительных сеансов устранения неполадок. Старайтесь быть в курсе последних версий микрокода ЦП, материнской платы, сетевых карт и прошивок твердотельных накопителей. Это не означает, что вы всегда должны запускать последнюю версию — эмпирическое правило здесь состоит в том, чтобы запускать вторую версию с последней прошивкой, если только в последней версии не исправлены критические ошибки, но не отставать слишком далеко.


Драйверы

Правила обновления здесь почти такие же, как и для прошивки. Старайтесь оставаться ближе к текущему. Одно предостережение здесь — попытаться отделить обновления ядра от обновлений драйверов, если это возможно. Например, вы можете упаковать свои драйверы с помощью DKMS или предварительно скомпилировать драйверы для всех используемых вами версий ядра. Таким образом, когда вы обновляете ядро и что-то работает не так, как ожидалось, на одну проблему нужно устранить меньше.

Процессор

Ваш лучший друг здесь — это репозиторий ядра и инструменты, которые к нему прилагаются. В Ubuntu/Debian вы можете установить linux-tools пакет с несколькими утилитами, но сейчас мы используем только cpupower, turbostat, а также x86_energy_perf_policy. Чтобы проверить оптимизации, связанные с процессором, вы можете провести стресс-тестирование вашего программного обеспечения с помощью вашего любимого инструмента генерации нагрузки (например, Яндекс использует Яндекс.Танк ) . Тестирование производительности ».

мощность процессора
Использование этого инструмента намного проще, чем сканирование /proc/. Чтобы увидеть информацию о вашем процессоре и его регуляторе частоты, вы должны запустить:
Код:
$ cpupower frequency-info
...
  driver: intel_pstate
  ...
  available cpufreq governors: performance powersave
  ...           
  The governor "performance" may decide which speed to use
  ...
  boost state support:
    Supported: yes
    Active: yes

Убедитесь, что Turbo Boost включен, а для процессоров Intel убедитесь, что вы работаете с intel_pstate, не acpi-cpufreq, или даже pcc-cpufreq. Если вы все еще используете acpi-cpufreq, то вам следует обновить ядро или, если это невозможно, убедиться, что вы используете performanceгубернатор. При работе с intel_pstate, даже powersaveГубернатор должен работать хорошо, но вам нужно убедиться в этом самостоятельно.
Говоря о холостом ходу, чтобы увидеть, что на самом деле происходит с вашим процессором, вы можете использовать turbostatчтобы напрямую просмотреть MSR процессора и получить информацию о мощности, частоте и состоянии простоя:

Код:
# turbostat --debug -P
... Avg_MHz Busy% ... CPU%c1 CPU%c3 CPU%c6 ... Pkg%pc2 Pkg%pc3 Pkg%pc6 ...

Здесь вы можете увидеть реальную частоту процессора (да, /proc/cpuinfoлжет вам), и состояния бездействия ядра/пакета . Если даже с intel_pstateдрайвера ЦП проводит больше времени в режиме ожидания, чем вы думаете, вы можете:
  • Установите governor на performance.
  • Установите x86_energy_perf_policy на производительность.
Или, только для критически важных задач с очень большой задержкой, вы можете:
Вы можете узнать больше об управлении питанием процессора в целом и о P-состояниях в частности в презентации Технологического центра Intel OpenSource « Балансировка мощности и производительности в ядре Linux » на LinuxCon Europe 2015.

Соответствие ЦП

Вы можете дополнительно уменьшить задержку, применив привязку ЦП к каждому потоку/процессу, например, nginx имеет worker_cpu_affinity, которая может автоматически привязывать каждый процесс веб-сервера к его собственному ядру. Это должно устранить миграцию ЦП, уменьшить промахи кэша и ошибки страниц и немного увеличить количество инструкций за цикл. Все это проверяется через perf stat.
К сожалению, включение сходства также может негативно сказаться на производительности, увеличивая время, которое процесс тратит на ожидание освобождения ЦП. Это можно отслеживать, запустив runqlat на одном из ваших nginxPID:
Код:
usecs               : count     distribution
    0 -> 1          : 819      |                                        |
    2 -> 3          : 58888    |******************************          |
    4 -> 7          : 77984    |****************************************|
    8 -> 15         : 10529    |*****                                   |
   16 -> 31         : 4853     |**                                      |
   ...
 4096 -> 8191       : 34       |                                        |
 8192 -> 16383      : 39       |                                        |
16384 -> 32767      : 17       |                                        |

Если вы видите многомиллисекундные хвостовые задержки, то, вероятно, на ваших серверах происходит слишком много всего, кроме nginx - сам по себе, а affinity увеличит задержку, а не уменьшит ее.

Память

Все mm/настройки обычно очень специфичны для рабочего процесса, есть только несколько вещей, которые можно порекомендовать:

NUMA

Современные ЦП на самом деле представляют собой несколько отдельных кристаллов ЦП, соединенных очень быстрым межсоединением и совместно использующих различные ресурсы, начиная с кэш-памяти L1 на ядрах HT, через кэш-память L3 в корпусе, до каналов памяти и PCIe в сокетах. Это в основном то, чем является NUMA: несколько единиц исполнения и хранения с быстрым соединением.
Подробный обзор NUMA и его последствий можно найти в « Серии NUMA Deep Dive Series » Фрэнка Деннемана .
Но, короче говоря, у вас есть выбор:
  • Игнорируя его , отключив в BIOS или запустив программное обеспечение под numactl --interleave=all, вы можете получить посредственную, но несколько стабильную производительность.
  • С помощью серверов с одним узлом, как это делает Facebook с платформой OCP Yosemite .
  • Принимая его, оптимизируя размещение ЦП/памяти как в пространстве пользователя, так и в пространстве ядра.
Поговорим о третьем варианте, так как для первых двух особых оптимизаций не требуется.
Чтобы правильно использовать NUMA, вам нужно рассматривать каждый узел numa как отдельный сервер, для этого вы должны сначала проверить топологию, что можно сделать с помощью numactl --hardware:

Код:
$ numactl --hardware
available: 4 nodes (0-3)
node 0 cpus: 0 1 2 3 16 17 18 19
node 0 size: 32149 MB
node 1 cpus: 4 5 6 7 20 21 22 23
node 1 size: 32213 MB
node 2 cpus: 8 9 10 11 24 25 26 27
node 2 size: 0 MB
node 3 cpus: 12 13 14 15 28 29 30 31
node 3 size: 0 MB
node distances:
node   0   1   2   3
  0:  10  16  16  16
  1:  16  10  16  16
  2:  16  16  10  16
  3:  16  16  16  10

О чем нужно заботиться:
  • количество узлов.
  • размеры памяти для каждого узла.
  • количество процессоров для каждого узла.
  • расстояния между узлами.
Это плохой пример, поскольку он имеет 4 узла, а также узлы без подключенной памяти. Здесь невозможно рассматривать каждую ноду как отдельный сервер, не жертвуя при этом половиной ядер в системе.
Мы можем убедиться в этом, используя numastat:

Код:
$ numastat -n -c
                  Node 0   Node 1 Node 2 Node 3    Total
                -------- -------- ------ ------ --------
Numa_Hit        26833500 11885723      0      0 38719223
Numa_Miss          18672  8561876      0      0  8580548
Numa_Foreign     8561876    18672      0      0  8580548
Interleave_Hit    392066   553771      0      0   945836
Local_Node       8222745 11507968      0      0 19730712
Other_Node      18629427  8939632      0      0 27569060

Вы также можете использовать numastat для вывода статистики использования памяти для каждого узла в /proc/meminfo:

Код:
$ numastat -m -c
Node 0 Node 1 Node 2 Node 3 Total
------ ------ ------ ------ -----
MemTotal 32150  32214      0      0 64363
MemFree 462   5793      0      0  6255
MemUsed 31688  26421      0      0 58109
Active 16021   8588      0      0 24608
Inactive 13436  16121      0      0 29557
Active(anon) 1193    970      0      0  2163
Inactive(anon) 121    108      0      0   229
Active(file) 14828   7618      0      0 22446
Inactive(file) 13315  16013      0      0 29327
...
FilePages 28498  23957      0      0 52454
Mapped 131    130      0      0   261
AnonPages 962    757      0      0  1718
Shmem 355    323      0      0   678
KernelStack 10      5      0      0    16


Теперь давайте рассмотрим пример более простой топологии.


Код:
$ numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23
node 0 size: 46967 MB
node 1 cpus: 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31
node 1 size: 48355 MB


Поскольку узлы в основном симметричны, мы можем привязать экземпляр нашего приложения к каждому узлу NUMA с помощью numactl --cpunodebind=X --membind=Xа затем разверните его на другом порту, таким образом вы можете повысить пропускную способность, используя оба узла, и уменьшить задержку, сохранив локальность памяти.
Вы можете проверить эффективность размещения NUMA по задержке ваших операций с памятью, например, используя bcc funclatencyдля измерения задержки операции с большим объемом памяти, например memmove.
На стороне ядра вы можете наблюдать эффективность, используя perf statи ищем соответствующие события памяти и планировщика:


# perf stat -e sched:sched_stick_numa,sched:sched_move_numa,sched:sched_swap_numa,migrate:mm_migrate_pages,minor-faults -p PID
...
1 sched:sched_stick_numa
3 sched:sched_move_numa
41 sched:sched_swap_numa
5,239 migrate:mm_migrate_pages
50,161 minor-faults


Последняя часть оптимизации, связанной с NUMA, для рабочих нагрузок с большой нагрузкой на сеть связана с тем фактом, что сетевая карта является устройством PCIe, и каждое устройство привязано к своему собственному узлу NUMA, поэтому некоторые процессоры будут иметь меньшую задержку при общении с сетью. Мы обсудим оптимизации, которые можно применить там, когда будем обсуждать привязку NIC→CPU, а пока давайте переключимся на PCI-Express…

PCIe

Обычно вам не нужно слишком углубляться в устранение неполадок PCIe, если у вас нет какой-либо аппаратной неисправности. Поэтому обычно стоит потратить минимум усилий, просто создав “link width”, “link speed” и, возможно, RxErr/ BadTLP оповещения для ваших устройств PCIe. Это сэкономит вам часы на устранении неполадок из-за неисправного оборудования или неудачного согласования PCIe. Вы можете использовать lspci для этого:



Код:
# lspci -s 0a:00.0 -vvv
...
LnkCap: Port #0, Speed 8GT/s, Width x8, ASPM L1, Exit Latency L0s <2us, L1 <16us
LnkSta: Speed 8GT/s, Width x8, TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-
...
Capabilities: [100 v2] Advanced Error Reporting
UESta: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- ...
UEMsk: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- ...
UESvrt: DLP+ SDES+ TLP- FCP+ CmpltTO- CmpltAbrt- ...
CESta:  RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr-
CEMsk:  RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+


Однако PCIe может стать узким местом, если у вас есть несколько высокоскоростных устройств, конкурирующих за пропускную способность (например, когда вы сочетаете быструю сеть с быстрым хранилищем), поэтому вам может потребоваться физически разделить ваши устройства PCIe между процессорами, чтобы получить максимальную пропускную способность.

1664118095750.png


Также см. статью « Понимание конфигурации PCIe для максимальной производительности » на веб-сайте Mellanox, в которой более подробно рассматривается конфигурация PCIe, которая может быть полезна на более высоких скоростях, если вы наблюдаете потерю пакетов между картой и ОС.
Intel предполагает, что иногда управление питанием PCIe (ASPM) может привести к более высоким задержкам и, следовательно, к более высокой потере пакетов. Вы можете отключить его, добавив pcie_aspm=off в командную строку ядра.
Прежде чем мы начнем, стоит упомянуть, что и Intel и Mellanox имеют свои собственные руководства по настройке производительности, и независимо от того, какого поставщика вы выберете, полезно прочитать их оба. Также к драйверам обычно прилагается файл README и набор полезных утилит.
Следующим местом, где можно проверить рекомендации, являются руководства по вашей операционной системе, например, Руководство по настройке производительности сети Red Hat Enterprise Linux , в котором объясняется большинство оптимизаций, упомянутых ниже, и даже больше.
У Cloudflare также есть хорошая статья о настройке этой части сетевого стека в их блоге, хотя она в основном нацелена на варианты использования с низкой задержкой.
При оптимизации сетевых карт ethtool будет твоим лучшим другом.
Небольшое примечание: если вы используете более новое ядро (и вам действительно следует это сделать!), вам также следует изменить некоторые части вашего пользовательского пространства, например, для сетевых операций, которые вам, вероятно, нужны более новые версии: ethtool, iproute2, и возможно iptables / nftables
Ценную информацию о том, что происходит с вашей сетевой картой, можно получить через ethtool -S:

Код:
       $ ethtool -S eth0 | egrep 'miss|over|drop|lost|fifo'
rx_dropped: 0
tx_dropped: 0
port.rx_dropped: 0
port.tx_dropped_link_down: 0
port.rx_oversize: 0
port.arq_overflows: 0


Проконсультируйтесь с производителем вашей сетевой карты для подробного описания статистики, например, у Mellanox есть специальная вики-страница для них .
Со стороны ядра вещей, на которые вы будете смотреть /proc/interrupts, /proc/softirqs, а также /proc/net/softnet_stat. Здесь есть два полезных инструмента: hardirqsа также softirqs. Ваша цель в оптимизации сети состоит в том, чтобы настроить систему так, чтобы у вас было минимальное использование ЦП без потери пакетов.

Прерывание

Настройка здесь обычно начинается с распределения прерываний по процессорам. Как именно вы должны это сделать, зависит от вашей рабочей нагрузки:
  • Для максимальной пропускной способности вы можете распределить прерывания по всем NUMA-узлам в системе.
  • Чтобы свести к минимуму задержку, вы можете ограничить прерывания одним NUMA-узлом. Для этого вам может понадобиться уменьшить количество очередей, чтобы они поместились в один узел (обычно это означает сокращение их количества вдвое с помощью ethtool -L).
Поставщики обычно предоставляют сценарии для этого, например, у Intel есть set_irq_affinity.

Размер кольцевого буфера

Сетевые карты должны обмениваться информацией с ядром. Обычно это делается с помощью структуры данных, называемой «кольцом», текущий/максимальный размер этого кольца просматривается через ethtool -g:



Код:
  $ ethtool -g eth0
Ring parameters for eth0:
Pre-set maximums:
RX:                4096
TX:                4096
Current hardware settings:
RX:                4096
TX:                4096


Вы можете отрегулировать эти значения в пределах предварительно установленных максимумов с помощью -G. Как правило, чем больше, тем лучше (особенно, если вы используете объединение прерываний), так как это даст вам больше защиты от всплесков и икоты в ядре, тем самым уменьшая количество отброшенных пакетов из-за отсутствия буферного пространства/пропущенного прерывания. Но есть пара предостережений:
  • В старых ядрах или драйверах без BQL высокие значения могут быть связаны с большим объемом буфера на стороне передачи.
  • Буферы большего размера также увеличивают нагрузку на кэш , поэтому, если вы столкнулись с такой проблемой, попробуйте уменьшить их.
Объединение

Объединение прерываний позволяет отложить уведомление ядра о новых событиях, объединяя несколько событий в одно прерывание. Текущие настройки можно просмотреть через ethtool -c:

Код:
$ ethtool -c eth0
Coalesce parameters for eth0:
...
rx-usecs: 50
tx-usecs: 50


Вы можете либо использовать статические ограничения, жестко ограничивая максимальное количество прерываний в секунду на ядро, либо зависеть от оборудования, которое автоматически регулирует частоту прерываний в зависимости от пропускной способности. Включение объединения (с -C) увеличит задержку и, возможно, приведет к потере пакетов, поэтому вы можете избежать этого для чувствительных к задержке. С другой стороны, его полное отключение может привести к регулированию прерываний и, следовательно, ограничить вашу производительность.

Разгрузка

Современные сетевые карты относительно умны и могут переложить большую часть работы либо на аппаратное обеспечение, либо эмулировать эту разгрузку в самих драйверах.
Все возможные разгрузки можно получить с помощью ethtool -k:




Код:
 $ ethtool -k eth0
Features for eth0:
...
tcp-segmentation-offload: on
generic-segmentation-offload: on
generic-receive-offload: on
large-receive-offload: off [fixed]



В выводе все ненастраиваемые разгрузки отмечены значком [fixed].
Обо всех них можно много говорить , но вот несколько практических правил:
  • не включайте LRO, вместо этого используйте GRO.
  • будьте осторожны с TSO, так как это сильно зависит от качества ваших драйверов/прошивки.
  • не включайте TSO/GSO на старых ядрах, так как это может привести к чрезмерному раздуванию буфера. **** Управление пакетами Все современные сетевые карты оптимизированы для многоядерного оборудования , поэтому они внутренне разбивают пакеты на виртуальные очереди, обычно по одной на процессор. Когда это делается аппаратно, это называется RSS, когда ОС отвечает за балансировку нагрузки пакетов между ЦП, это называется RPS (с его аналогом TX, называемым XPS). Когда ОС также пытается быть умной и направляет потоки на процессоры, которые в данный момент обрабатывают этот сокет, это называется RFS. Когда это делает аппаратное обеспечение, это называется «ускоренная RFS» или сокращенно aRFS.
Вот пара лучших практик нашего производства:

  • Если вы используете более новое оборудование 25G+, у него, вероятно, достаточно очередей и огромной таблицы косвенных адресов, чтобы иметь возможность просто использовать RSS для всех ваших ядер. Некоторые старые сетевые карты имеют ограничения на использование только первых 16 процессоров.
  • Вы можете попробовать включить RPS , если:
    • у вас больше процессоров, чем аппаратных очередей, и вы хотите пожертвовать задержкой ради пропускной способности.
    • вы используете внутреннее туннелирование (например, GRE/IPinIP), которое NIC не может использовать RSS;
  • Не включайте RPS , если ваш ЦП довольно старый и не имеет x2APIC.
  • Привязка каждого ЦП к собственной очереди TX через XPS , как правило, является хорошей идеей.
  • Эффективность RFS сильно зависит от вашей рабочей нагрузки и от того, применяете ли вы к ней привязку ЦП.
Flow Director и ATR
Включенный директор потока (или fdir в терминологии Intel) работает по умолчанию в режиме Application Targeting Routing, который реализует aRFS путем выборки пакетов и направления потоков в ядро, где они предположительно обрабатываются. Его статистика также доступна через ethtool -S:

Код:
       $ ethtool -S eth0 | egrep 'fdir'
port.fdir_flush_cnt: 0
...

Хотя Intel утверждает, что fdir в некоторых случаях увеличивает производительность , внешние исследования показывают, что он также может привести к переупорядочению пакетов до 1% , что может сильно повредить производительности TCP. Поэтому попробуйте протестировать его сами и посмотрите, полезен ли FD для вашей рабочей нагрузки, при этом следите за TCPOFOQueue .

Операционные системы: сетевой стек



Существует бесчисленное множество книг, видеороликов и руководств по настройке сетевого стека Linux. И, к сожалению, тонны «культивирования груза sysctl.conf», которые идут с ними. Несмотря на то, что последние версии ядра не требуют такой тщательной настройки, как это было 10 лет назад, а большинство новых функций TCP/IP включены и хорошо настроены по умолчанию, люди по-прежнему копируют и вставляют свои старые. sysctls.conf которые они использовали для настройки ядер 2.6.18/2.6.32.
Чтобы проверить эффективность оптимизации, связанной с сетью, вы должны:
  • Собирать общесистемные метрики TCP через /proc/net/snmpа также /proc/net/netstat.
  • Совокупные метрики для каждого соединения, полученные либо из ss -n --extended --info, или от звонка getsockopt(TCP_INFO)/ getsockopt(TCP_CC_INFO) внутри вашего веб-сервера.
  • tcptrace (1) выборочных потоков TCP.
  • Анализируйте показатели RUM из приложения/браузера.
Что касается источников информации об оптимизации сети, мне обычно нравятся выступления на конференциях CDN-людей, поскольку они обычно знают, что делают, например, Fastly на LinuxCon Australia . Послушать, что разработчики ядра Linux говорят о сети, тоже весьма поучительно, например , доклады netdevconf и расшифровки NETCONF .
Стоит отметить хорошее подробное описание сетевого стека Linux от PackageCloud, тем более что они делают акцент на мониторинге, а не на слепой настройке:
Прежде чем мы начнем, позвольте мне сказать еще раз: обновите ядро ! Есть куча новых улучшений сетевого стека, и я даже не говорю о IW10 ( то есть о 2010 ). Я говорю о таких новых новшествах, как автоматическое определение размера TSO, FQ, темпирование, TLP и RACK, но об этом позже. В качестве бонуса при обновлении до нового ядра вы получите ряд улучшений масштабируемости, например: удаленный кеш маршрутизации , незаблокированные сокеты, SO_REUSEPORT и многое другое .

Из недавних статей по сетевым технологиям Linux особенно выделяется статья « Создание Linux TCP Fast ». Ему удается объединить многолетние улучшения ядра Linux на 4 страницах, разбив стек TCP на стороне отправителя Linux на функциональные части:

1664119491920.png

Справедливое распределение очередей и интервалов

Fair Queueing отвечает за улучшение справедливости и уменьшение блокировки между потоками TCP, что положительно влияет на скорость потери пакетов. Pacing планирует пакеты со скоростью, установленной системой контроля перегрузки, равномерно распределенной во времени, что еще больше снижает потери пакетов, увеличивая пропускную способность.

В качестве примечания: Fair Queueing и Pacing доступны в linux через fq qdisc. Некоторые из вас могут знать, что они являются требованием для BBR (теперь уже нет), но оба они могут быть использованы с CUBIC, обеспечивая до 15-20% снижения потерь пакетов и, следовательно, лучшую пропускную способность на CC, основанных на потерях. Только не используйте его в старых ядрах (< 3.19), так как в конечном итоге вы будете перегружать чистые ACK и искалечите ваши uploads/RPC.

TSO autosizing и TSQ

Обе эти функции отвечают за ограничение буферизации внутри стека TCP и, следовательно, за уменьшение задержки без снижения пропускной способности.
Контроль перегрузки
Алгоритмы управления перегрузками сами по себе являются огромной темой, и в последние годы вокруг них велась активная работа. Некоторые из них были обозначены как: tcp_cdg (CAIA), tcp_nv (Facebook) и tcp_bbr (Google). Мы не будем слишком углубляться в обсуждение их внутренней работы, скажем только, что все они больше полагаются на увеличение задержки, чем на падение пакетов для индикации перегрузки. BBR является, пожалуй, наиболее хорошо документированным, протестированным и практичным из всех новых средств контроля перегрузки. Основная идея заключается в создании модели сетевого пути на основе скорости доставки пакетов и последующем выполнении циклов управления для максимизации пропускной способности при минимизации rtt. Это именно то, что мы ищем в нашем прокси-стеке.

Предварительные данные экспериментов BBR на наших Edge PoP показывают увеличение скорости загрузки файлов:

1664119702851.png


Здесь я хочу подчеркнуть, что мы наблюдаем увеличение скорости во всех процентилях. Это не относится к изменениям бэкэнда. Обычно они приносят пользу только пользователям p90+ (те, у кого самое быстрое подключение к Интернету), поскольку мы считаем, что у всех остальных уже ограничена пропускная способность. Настройки на уровне сети, такие как изменение контроля перегрузки или включение FQ/стимуляции, показывают, что пользователи не ограничены пропускной способностью, но, если можно так выразиться, они «ограничены TCP».

Если вы хотите узнать больше о BBR, у APNIC есть хороший начальный обзор BBR (и его сравнение с контролем перегрузок на основе потерь). Для получения более подробной информации о BBR вы, вероятно, захотите прочитать архивы списков рассылки bbr-dev (вверху закреплено множество полезных ссылок). Людям, заинтересованным в контроле перегрузки в целом, может быть интересно следить Исследовательской группы по контролю перегрузки Интернета .

Обработка ACK и обнаружение потерь

Но хватит о контроле за перегрузкой, давайте поговорим об обнаружении потерь, здесь еще раз запуск последнего ядра немного поможет. Новые эвристики, такие как TLP и RACK , постоянно добавляются в TCP, а старые, такие как FACK и ER, удаляются. После добавления они включены по умолчанию, поэтому вам не нужно настраивать какие-либо системные параметры после обновления.

Приоритизация пользовательского пространства и HOL

API-интерфейсы сокетов пользовательского пространства обеспечивают неявную буферизацию и не позволяют переупорядочивать фрагменты после их отправки, поэтому в мультиплексных сценариях (например, HTTP/2) это может привести к блокировке HOL и инверсии приоритетов h2. TCP_NOTSENT_LOWATвариант сокета (и соответствующий net.ipv4.tcp_notsent_lowatsysctl) были разработаны для решения этой проблемы путем установки порога, при котором сокет считает себя доступным для записи (т.е. epollбудет лгать вашему приложению). Это может решить проблемы с приоритизацией HTTP/2, но также потенциально может отрицательно сказаться на пропускной способности, так что вы знаете, что делать — проверьте сами.

Sysctls

Нельзя просто говорить об оптимизации сети, не упоминая sysctl, которые необходимо настроить. Но позвольте мне сначала начать с вещей, которые вы не надо трогать:
Что касается sysctl, которые вы должны использовать:
Также стоит отметить, что есть черновик RFC (хотя и немного неактивный) от автора curl Дэниела Стенберга под названием TCP Tuning for HTTP , который пытается собрать все системные настройки, которые могут быть полезны для HTTP, в одном месте.

Уровень приложения: Средний уровень


Инструменты

Как и в случае с ядром, очень важно иметь актуальное пользовательское пространство. Вы должны начать с обновления своих инструментов, например, вы можете упаковать более новые версии perf, bcc, так далее.
Когда у вас есть новые инструменты, вы готовы правильно настроить и наблюдать за поведением системы. В этой части поста мы будем в основном полагаться на профилирование на процессоре с помощью perf top, FlameGraphs и специальные гистограммы из bccх funclatency.


Цепочка инструментов компилятора

Наличие современной цепочки инструментов компилятора необходимо, если вы хотите скомпилировать оптимизированную для аппаратного обеспечения сборку, которая присутствует во многих библиотеках, обычно используемых веб-серверами. Помимо производительности, новые компиляторы имеют новые функции безопасности (например, -fstack-protector-strongили же SafeStack), который вы хотите применить к краю. Другой вариант использования современных наборов инструментов — это когда вы хотите запустить свои тестовые наборы для бинарных файлов, скомпилированных с помощью санитайзеров (например AddressSanitizer и других ).

Системные библиотеки

Также стоит обновить системные библиотеки, такие как glibc, поскольку в противном случае вы можете упустить недавние оптимизации в низкоуровневых функциях из -lc, -lm, -lrtи т. д. Здесь также применимо предупреждение «Проверь сам», поскольку время от времени возникают регрессии .

ZLIB

Обычно за сжатие отвечает веб-сервер. В зависимости от того, сколько данных проходит через этот прокси, вы можете иногда видеть символы zlib в perf top, например:

Код:
perf top
...
   8.88%  nginx        [.] longest_match
   8.29%  nginx        [.] deflate_slow
   1.90%  nginx        [.] compress_block

Существуют способы оптимизации на самых низких уровнях: и Intel, и Cloudflare, и отдельный проект zlib-ng имеют свои форки zlib, которые обеспечивают более высокую производительность за счет использования новых наборов инструкций.

Malloc

До сих пор при обсуждении оптимизаций мы в основном ориентировались на процессор, но давайте переключимся и обсудим оптимизации, связанные с памятью. Если вы используете много Lua с FFI или тяжелые модули сторонних разработчиков, которые сами управляют памятью, вы можете наблюдать повышенное использование памяти из-за фрагментации. Вы можете попробовать решить эту проблему, переключившись на jemalloc или tcmalloc.

Использование malloc также имеет следующие преимущества:

  • Отделение бинарного файла nginx от окружения, так что обновление версии glibc и миграция ОС будут меньше влиять на него.
  • Лучшая интроспекция, профилирование и статистика.

PCRE

Если вы используете много сложных регулярных выражений в конфигурации nginx или сильно полагаетесь на Lua, вы можете увидеть символы, связанные с pcre, в perf top. Вы можете оптимизировать это, скомпилировав PCRE с помощью JIT, а также включив его в nginx через pcre_jit on;.

Вы можете проверить результат оптимизации, посмотрев на FlameGraphs или используя funclatency:

Код:
# funclatency /srv/nginx-bazel/sbin/nginx:ngx_http_regex_exec -u
...
     usecs               : count     distribution
         0 -&gt; 1          : 1159     |**********                              |
         2 -&gt; 3          : 4468     |****************************************|
         4 -&gt; 7          : 622      |*****                                   |
         8 -&gt; 15         : 610      |*****                                   |
        16 -&gt; 31         : 209      |*                                       |
        32 -&gt; 63         : 91       |                                        |

TLS

Если вы завершаете работу TLS на периферии без CDN, оптимизация производительности TLS может быть очень ценной. При обсуждении настроек мы будем в основном фокусироваться на эффективности на стороне сервера. Итак, в настоящее время первое, что вам нужно решить, это какую библиотеку TLS использовать: Vanilla OpenSSL , LibreSSL или BoringSSL . После выбора разновидности библиотеки TLS вам необходимо правильно ее собрать: OpenSSL, например, имеет набор встроенных эвристик, которые позволяют проводить оптимизацию на основе среды сборки; У BoringSSL есть детерминированные сборки, но, к сожалению, он гораздо более консервативен и просто отключает некоторые оптимизации по умолчанию . В любом случае, здесь выбор современного процессора должен наконец окупиться: большинство библиотек TLS могут использовать все, от AES-NI и SSE до ADX и AVX512. Вы можете использовать встроенные тесты производительности, которые поставляются с вашей библиотекой TLS, например, в случае с BoringSSL это bssl speed.
Большая часть производительности зависит не от имеющегося у вас оборудования, а от наборов шифров, которые вы собираетесь использовать, поэтому вам нужно тщательно их оптимизировать. Также знайте, что изменения здесь могут (и будут!) влиять на безопасность вашего веб-сервера — самые быстрые наборы шифров не обязательно будут лучшими. Если вы не знаете, какие параметры шифрования использовать, лучше всего начать с Mozilla SSL Configuration Generator.

Асимметричное шифрование
Если ваш сервис находится на грани, то вы можете наблюдать значительное количество рукопожатий TLS и, следовательно, значительная часть вашего ЦП потребляется асимметричным шифрованием, что делает его очевидной целью для оптимизации. Чтобы оптимизировать использование ЦП на стороне сервера, вы можете переключиться на сертификаты ECDSA , которые обычно в 10 раз быстрее, чем RSA. Кроме того, они значительно меньше, что может ускорить рукопожатие при потере пакетов. Но ECDSA также сильно зависит от качества генератора случайных чисел вашей системы, поэтому, если вы используете OpenSSL, убедитесь, что у вас достаточно энтропии (с BoringSSL вам не нужно об этом беспокоиться ). В качестве примечания стоит отметить, что больше не всегда лучше, например, использование 4096 сертификатов RSA снизит вашу производительность в 10 раз:

Код:
$ bssl speed
Did 1517 RSA 2048 signing ... (1507.3 ops/sec)
Did 160 RSA 4096 signing ...  (153.4 ops/sec)

Что еще хуже, меньший размер также не обязательно является лучшим выбором: используя необычное поле p-224 для ECDSA, вы получите на 60% худшую производительность по сравнению с более распространенным p-256:

Код:
       $ bssl speed
Did 7056 ECDSA P-224 signing ...  (6831.1 ops/sec)
Did 17000 ECDSA P-256 signing ... (16885.3 ops/sec)


Эмпирическое правило здесь заключается в том, что наиболее часто используемое шифрование, как правило, является наиболее оптимизированным.
При запуске должным образом оптимизированной библиотеки на основе OpenTLS с использованием сертификатов RSA вы должны увидеть следующtt в вашем perf top: коробки с поддержкой AVX2, но не с поддержкой ADX (например, Haswell) должны использовать AVX2:


Код:
6.42%  nginx                [.] rsaz_1024_sqr_avx2
1.61%  nginx                [.] rsaz_1024_mul_avx2

В то время как более новое оборудование должно использовать общее умножение Монтгомери с ADX:

Код:
7.08%  nginx                [.] sqrx8x_internal
2.30%  nginx                [.] mulx4x_internal

Симметричное шифрование

Если у вас есть много массовых передач, таких как видео, фотографии или более общие файлы, вы можете начать наблюдать символы симметричного шифрования в выводе профилировщика. Здесь вам просто нужно убедиться, что ваш процессор поддерживает AES-NI, и вы установите настройки на стороне сервера для шифров AES-GCM. Правильно настроенное оборудование должно иметь perf top:

Код:
8.47%  nginx                [.] aesni_ctr32_ghash_6x

Но не только вашим серверам придется иметь дело с шифрованием/дешифрованием — ваши клиенты разделят ту же нагрузку на гораздо менее мощном процессоре. Без аппаратного ускорения это может быть довольно сложно , поэтому вы можете рассмотреть возможность использования алгоритма, разработанного для быстрой работы без аппаратного ускорения, например, ChaCha20-Poly1305 . Это уменьшит TTLB для некоторых ваших мобильных клиентов.
ChaCha20-Poly1305 поддерживается в BoringSSL из коробки, для OpenSSL 1.0.2 можно рассмотреть возможность использования патчей Cloudflare . BoringSSL также поддерживает « группы шифров с равными предпочтениями », поэтому вы можете использовать следующую конфигурацию, чтобы позволить клиентам решать, какие шифры использовать в зависимости от их аппаратных возможностей (бесстыдно украдено из cloudflare/sslconfig ):

Код:
ssl_ciphers '[ECDHE-ECDSA-AES128-GCM-SHA256|ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256|ECDHE-RSA-CHACHA20-POLY1305]:ECDHE+AES128:RSA+AES128:ECDHE+AES256:RSA+AES256:ECDHE+3DES:RSA+3DES';
ssl_prefer_server_ciphers on;

Уровень приложения: верхний уровень


Чтобы проанализировать эффективность ваших оптимизаций на этом уровне, вам потребуется собрать данные RUM. В браузерах вы можете использовать API синхронизации навигации и синхронизации ресурсов . Ваши основные показатели — TTFB и TTV/TTI. Наличие этих данных в легко запрашиваемых и графических форматах значительно упростит итерацию.

Сжатие

Сжатие в nginx начинается с mime.types файла, который определяет соответствие по умолчанию между расширением файла и типом MIME - ответа. Затем вам нужно определить, какие типы вы хотите передать вашему компрессору, например gzip_types. Если вам нужен полный список, вы можете использовать mime-db для автоматического создания вашего mime.typesи добавить тех, у кого .compressible == trueк gzip_types.
При включении gzip будьте осторожны с двумя его аспектами:
  • Увеличение использования памяти. Это можно решить, ограничив gzip_buffers.
  • Увеличен TTFB из-за буферизации. Это можно решить с помощью [gzip_no_buffer].
В качестве примечания, http-сжатие не ограничивается исключительно gzip: у nginx есть сторонние ngx_brotliмодуль, который может улучшить степень сжатия до 30% по сравнению с gzip.
Что касается самих настроек сжатия, давайте обсудим два отдельных варианта использования: статические и динамические данные.
  • Для статических данных вы можете заархивировать максимальные коэффициенты сжатия, предварительно сжав свои статические ресурсы в процессе сборки. Мы довольно подробно обсуждали это в Развертывание Brotli для статического содержимого как для gzip, так и для brotli.
  • Для динамических данных необходимо тщательно сбалансировать полный круговой путь: время на сжатие данных + время на их передачу + время на распаковку на клиенте. Поэтому установка максимально возможного уровня сжатия может быть неразумной не только с точки зрения использования ЦП, но и с точки зрения TTFB.

Буферизация

Буферизация внутри прокси-сервера может сильно повлиять на производительность веб-сервера, особенно в отношении задержки. Прокси-модуль nginx имеет различные ручки буферизации, которые можно переключать в зависимости от местоположения, каждая из которых полезна для своей цели. Вы можете отдельно управлять буферизацией в обоих направлениях через proxy_request_buffering, a также proxy_buffering. Если буферизация включена, верхний предел потребления памяти устанавливается client_body_buffer_sizeа также proxy_buffers, после достижения этих пороговых значений запрос/ответ буферизуется на диск. Для ответов это можно отключить, установив proxy_max_temp_file_sizeдо 0.
Наиболее распространенные подходы к буферизации:
  • Буферизация запроса/ответа до некоторого порога в памяти, а затем переполнение на диск. Если буферизация запросов включена, вы отправляете запрос на серверную часть только после того, как он будет полностью получен, а с буферизацией ответов вы можете мгновенно освободить серверный поток, как только он будет готов с ответом. Этот подход имеет преимущества улучшенной пропускной способности и защиты серверной части за счет увеличения задержки и использования памяти/ввода-выводов (хотя, если вы используете твердотельные накопители, это может не быть большой проблемой).
  • Без буферизации. Буферизация может быть не лучшим выбором для чувствительных к задержкам маршрутов, особенно для тех, которые используют потоковую передачу. Для них вы можете отключить его, но теперь ваш сервер должен иметь дело с медленными клиентами (включая вредоносные low-read атаки типа
  • Буферизация ответов, управляемая приложением, через X-Accel-Bufferingзаголовок.
Какой бы путь вы ни выбрали, не забудьте проверить его влияние как на TTFB, так и на TTLB. Кроме того, как упоминалось ранее, буферизация может повлиять на использование операций ввода-вывода и даже на использование серверной части, поэтому следите за этим.


TLS

Теперь мы собираемся поговорить о высокоуровневых аспектах TLS и улучшении задержки, которые можно сделать, правильно настроив nginx. Большинство оптимизаций, о которых я упомяну, High Performance Browser Networking « Оптимизация для TLS » HTTPS(er) » на nginx.conf 2014. Настройки, упомянутые в этой части, повлияют как на производительность, так и на безопасность. вашего веб-сервера, если вы не уверены, обратитесь к Руководству по TLS на стороне сервера Mozilla и/или к вашей группе безопасности.
Для проверки результатов оптимизаций вы можете использовать:
Возобновление сеанса

Как любят говорить администраторы баз данных, «самый быстрый запрос — это тот, который вы никогда не сделаете». То же самое касается TLS — вы можете уменьшить задержку на один RTT, если кешируете результат рукопожатия. Есть два способа сделать это:
  • Вы можете попросить клиента сохранить все параметры сеанса (в подписанном и зашифрованном виде) и отправить их вам во время следующего рукопожатия (аналогично cookie). На стороне nginx это настраивается через ssl_session_ticketsдиректива. Это не потребляет памяти на стороне сервера, но имеет ряд недостатков:
    • Вам нужна инфраструктура для создания, ротации и распространения случайных ключей шифрования/подписи для сеансов TLS. Просто помните, что вам действительно не следует 1) использовать систему управления версиями для хранения ключей билетов 2) генерировать эти ключи из другого неэфемерного материала, например, даты или сертификата.
    • PFS будет не для каждого сеанса, а для каждого ключа билета tls, поэтому, если злоумышленник получит ключ билета, он потенциально может расшифровать любой захваченный трафик в течение срока действия билета.
    • Ваше шифрование будет ограничено размером вашего ключа билета. Нет особого смысла использовать AES256, если вы используете 128-битный ключ билета. Nginx поддерживает как 128-битные, так и 256-битные ключи билетов TLS.
    • Не все клиенты поддерживают ключи билетов (хотя все современные браузеры их поддерживают).
  • Или вы можете хранить параметры сеанса TLS на сервере и давать клиенту только ссылку (идентификатор). Это делается через ssl_session_cacheдиректива. Его преимущество заключается в сохранении PFS между сеансами и значительном ограничении поверхности атаки. Хотя ключи от билетов имеют недостатки:
    • Они потребляют ~256 байт памяти за сеанс на сервере, что означает, что вы не можете хранить многие из них слишком долго.
    • Они не могут быть легко разделены между серверами. Поэтому вам либо нужен балансировщик нагрузки, который будет отправлять одного и того же клиента на тот же сервер, чтобы сохранить локальность кеша, либо написать распределенное хранилище сеансов TLS поверх чего-то вроде ngx_http_lua_module.
В качестве примечания: если вы используете подход с тикетами сеанса, то стоит использовать 3 ключа вместо одного, например:


Код:
ssl_session_tickets on;
ssl_session_timeout 1h;
ssl_session_ticket_key /run/nginx-ephemeral/nginx_session_ticket_curr;
ssl_session_ticket_key /run/nginx-ephemeral/nginx_session_ticket_prev;
ssl_session_ticket_key /run/nginx-ephemeral/nginx_session_ticket_next;

Вы всегда будете шифровать текущим ключом, но принимать сеансы, зашифрованные как следующим, так и предыдущим ключами.

Сшивание OCSP

Вы должны скрепить свои ответы OCSP, иначе:
  • Ваше рукопожатие TLS может занять больше времени, поскольку клиенту потребуется связаться с центром сертификации, чтобы получить статус OCSP.
  • Ошибка выборки OCSP может привести к снижению доступности.
  • Вы можете поставить под угрозу конфиденциальность пользователей, поскольку их браузер свяжется со сторонней службой, указав, что они хотят подключиться к вашему сайту.
Чтобы скрепить ответ OCSP, вы можете периодически получать его из своего центра сертификации, распространять результат на свои веб-серверы и использовать его с ssl_stapling_fileдиректива:

Код:
ssl_stapling_file /var/cache/nginx/ocsp/www.der;


Размер записи TLS
TLS разбивает данные на фрагменты, называемые записями, которые вы не можете проверить и расшифровать, пока не получите их целиком. Вы можете измерить эту задержку как разницу между TTFB с точки зрения сетевого стека и приложения.
По умолчанию nginx использует фрагменты размером 16 КБ, которые даже не вписываются в окно перегрузки IW10, поэтому требуют дополнительного кругового пути. Стандартный nginx предоставляет возможность устанавливать размеры записей через ssl_buffer_sizeдиректива:
  • Чтобы оптимизировать для низкой задержки, вы должны установить что-то маленькое, например 4k. Дальнейшее его уменьшение будет более затратным с точки зрения использования ЦП.
  • Чтобы оптимизировать для высокой пропускной способности, вы должны оставить его на уровне 16 КБ.
Есть две проблемы со статической настройкой:
  • Вам нужно настроить его вручную.
  • Вы можете только установить ssl_buffer_sizeна основе конфигурации для каждого nginx или блока для каждого сервера, поэтому, если у вас есть сервер со смешанной рабочей нагрузкой по задержке/пропускной способности, вам необходимо пойти на компромисс.
Есть альтернативный подход: динамическая настройка размера записи. Существует патч nginx от Cloudflare, добавляющий поддержку динамических размеров записей . Сначала может быть сложно настроить его, но как только вы закончите с этим, он будет работать довольно хорошо.
TLS 1.3
Функции TLS 1.3 действительно звучат очень хорошо , но если у вас нет ресурсов для постоянного устранения неполадок TLS, я бы посоветовал не включать его, поскольку:

Eventloop

Nginx — это веб-сервер на основе цикла событий, что означает, что он может выполнять только одну операцию за раз. Несмотря на то, что кажется, что он делает все эти вещи одновременно, как при мультиплексировании с временным разделением, все, что делает nginx, — это просто быстро переключается между событиями, обрабатывая одно за другим . Все это работает, потому что обработка каждого события занимает всего пару микросекунд. Но если это начинает занимать слишком много времени, например, из-за того, что требуется переход на вращающийся диск, задержка может резко возрасти.
Если вы начинаете замечать, что ваш nginx проводит слишком много времени внутри ngx_process_events_and_timersфункция, а распределение бимодальное, то вы, вероятно, страдаете от задержек цикла событий.



Код:
 # funclatency '/srv/nginx-bazel/sbin/nginx:ngx_process_events_and_timers' -m
     msecs               : count     distribution
 0 -&gt; 1          : 3799  |****************************************|
 2 -&gt; 3          : 0  | |
 4 -&gt; 7          : 0  | |
 8 -&gt; 15         : 0  | |
 16 -&gt; 31         : 409  |**** |
 32 -&gt; 63         : 313  |*** |
 64 -&gt; 127        : 128  |* |


AIO и пулы потоков
Поскольку основным источником зависаний цикла событий, особенно на вращающихся дисках, является ввод-вывод, вам, вероятно, следует сначала посмотреть на него. Вы можете измерить, насколько это на вас влияет, запустив fileslower:

Код:
# fileslower 10
Tracing sync read/writes slower than 10 ms
TIME(s) COMM TID D BYTES LAT(ms) FILENAME
2.642    nginx          69097  R 5242880   12.18 0002121812
4.760    nginx          69754  W 8192      42.08 0002121598
4.760    nginx          69435  W 2852      42.39 0002121845
4.760    nginx          69088  W 2852      41.83 0002121854


Чтобы исправить это, nginx поддерживает разгрузку операций ввода-вывода в пул потоков (у него также есть поддержка AIO, но нативные AIO в Unix имеют множество особенностей, поэтому лучше избегать этого, если вы не знаете, что делаете). Базовая установка состоит из простого:

Код:
aio threads;
aio_write on;

Для более сложных случаев можно настроить пользовательские thread_pool, например, по одному на диск, так что если один диск выйдет из строя, это не повлияет на остальные запросы. Пулы потоков могут значительно сократить количество зависших процессов nginx. Dсостояние, улучшая как задержку, так и пропускную способность. Но он не устранит остановку событийного цикла полностью, так как в настоящее время не все операции ввода-вывода передаются ему.
Написание журналов также может занять значительное время, так как это касается дисков. Вы можете проверить, так ли это, запустив ext4slower и ищем ссылки на журналы доступа/ошибок:



Код:
# ext4slower 10
TIME     COMM           PID    T BYTES   OFF_KB   LAT(ms) FILENAME
06:26:03 nginx          69094  W 163070  634126     18.78 access.log
06:26:08 nginx          69094  W 151     126029     37.35 error.log
06:26:13 nginx          69082  W 153168  638728    159.96 access.log


Это можно обойти путем буферизации журналов доступа в памяти перед их записью с помощью bufferпараметр для access_logдиректива. Используя gzipКроме того, вы можете сжимать журналы перед их записью на диск, что еще больше снижает нагрузку на ввод-вывод.
Но чтобы полностью устранить задержки ввода-вывода при записи в журнал, вы должны просто писать журналы через syslog , таким образом, журналы будут полностью интегрированы с циклом событий nginx.

Открытый файловый кеш

Вызовы open(2) по своей сути блокируются, а веб-серверы регулярно открывают/читают/закрывают файлы, может быть полезно иметь кеш открытых файлов. Вы можете увидеть, сколько пользы посмотрев на ngx_open_cached_file:

Код:
# funclatency /srv/nginx-bazel/sbin/nginx:ngx_open_cached_file -u
     usecs               : count     distribution
 0 -&gt; 1          : 10219  |****************************************|
 2 -&gt; 3          : 21  | |
 4 -&gt; 7          : 3  | |
 8 -&gt; 15         : 1  | |


Если вы видите, что слишком много открытых вызовов или некоторые из них занимают слишком много времени, вы можете посмотреть на включение кеша открытых файлов:

Код:
open_file_cache max=10000;
open_file_cache_min_uses 2;
open_file_cache_errors on;


После включения open_file_cacheвы можете наблюдать все промахи кеша, посмотрев на opensnoopи решить , нужно ли вам настраивать ограничения кеша :

Код:
# opensnoop -n nginx
PID    COMM               FD ERR PATH
69435  nginx             311   0 /srv/site/assets/serviceworker.js
69086  nginx             158   0 /srv/site/error/404.html
...


Подведение итогов

Все оптимизации, описанные в этом посте, являются локальными для одного веб-сервера. Некоторые из них улучшают масштабируемость и производительность. Другие актуальны, если вы хотите обслуживать запросы с минимальной задержкой или быстрее доставлять байты клиенту. Но, по нашему опыту, значительная часть заметной пользователями производительности связана с более высокоуровневыми оптимизациями, которые влияют на поведение пограничной сети Dropbox в целом, такими как проектирование входящего/исходящего трафика и более разумная внутренняя балансировка нагрузки. Эти проблемы находятся на грани (каламбур) знаний , и индустрия только начала приближаться к ним .
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх