Комментарии 62
Чем мне еще понравилась получившаяся схема — один файловый кеш, системный. И он будет использовать всю доступную память, уменьшаясь при необходимости. Если же часть запросов (т.е. чтений диска) будут кешироваться в nginx, а другая в системном кеше, то это лишнее дублирование. Памяти, думаю, потребует больше.
Кстати, при некоторых настройках nginx (много и большие буферы) он у меня отжирал почти всю память, даже свопиться система начинала.
worker_rlimit_nofile 16384;
thread_pool moovies threads=8 max_queue=65536;
...
sendfile on;
sendfile_max_chunk 1m;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
open_file_cache max=100000 inactive=30s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
...
location ~ \.mp4$ {
aio threads=moovies;
directio 16M;
output_buffers 2 1M;
}
Подобные настройки дают отличный результат на высоконагруженном тьюб-сайте. Также стоит потюнинговать sysctl.
Похожее (но не совсем так) делал, ничего хорошего не получил. Только памяти nginx съел много.
В документации: «Невыравненный конец файла будет читаться блокированно. То же относится к запросам с указанием диапазона запрашиваемых байт (byte-range requests)». По моим оценкам так и получается.
Читать все через «прокладку» mhddfs/aufs, по моим оценкам, «не гуд». Во всяком случае, тесты показали скорость чтения на много меньше чем читать напрямую.
Поэтому все запросы rewrit(ом) приводил к нужному мне виду и переключался на нужный диск при помощи try_files. До некоторых пор это помогало. Но трафик подрос, клиентов онлайн стало больше и появились «тормоза». Вот и начал экспериментировать.
По факту в выходные было 850 Mb. На используемых дисках await выше 10 ms не поднимается. Обычно 2 — 5.
Склеивать у меня не выход. Надо уметь отдавать произвольную часть файла. проблема именно в этом. И в том, что практически все запросы такие.
Все постить много…
Linux backup 3.16.0-4-amd64 #1 SMP Debian 3.16.7-ckt20-1+deb8u2 (2016-01-02) x86_64 GNU/Linux
#cat /etc/debian_version
8.3
Добавлено в sysctl.conf:
net.ipv4.tcp_keepalive_time = 180
net.netfilter.nf_conntrack_tcp_timeout_established = 3000
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 10
net.netfilter.nf_conntrack_generic_timeout = 300
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 60
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 60
net.netfilter.nf_conntrack_tcp_timeout_established = 300
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 10
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 10
net.netfilter.nf_conntrack_tcp_timeout_last_ack = 30
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_close = 10
net.netfilter.nf_conntrack_tcp_timeout_max_retrans = 120
net.netfilter.nf_conntrack_tcp_timeout_unacknowledged = 120
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 180
net.netfilter.nf_conntrack_icmp_timeout = 10
net.netfilter.nf_conntrack_events_retry_timeout = 15
net.ipv4.netfilter.ip_conntrack_generic_timeout = 120
net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_sent = 60
net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_sent2 = 60
net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_recv = 60
net.ipv4.netfilter.ip_conntrack_tcp_timeout_established = 300
net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait = 10
net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait = 10
net.ipv4.netfilter.ip_conntrack_tcp_timeout_last_ack = 30
net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait = 60
net.ipv4.netfilter.ip_conntrack_tcp_timeout_close = 10
net.ipv4.netfilter.ip_conntrack_tcp_timeout_max_retrans = 120
net.ipv4.netfilter.ip_conntrack_udp_timeout = 30
net.ipv4.netfilter.ip_conntrack_udp_timeout_stream = 180
net.ipv4.netfilter.ip_conntrack_icmp_timeout = 10
net.ipv4.ipfrag_time = 5
net.ipv4.tcp_timestamps = 1
net.ipv4.ipfrag_time = 1
vm.swappiness=20
vm.vfs_cache_pressure=500
Надо тюнить настройки тредпулов и смотреть, во что упирается (возможно, в sysctl-и).
Еще есть радикальный вариант — перейти на FreeBSD. :-)
Еще осложнило эксперименты то, то все это происходит на боевом сервере. Лишний раз клиентов дергать не хочется. А сымитировать такую нагрузку сложно.
// Еще есть радикальный вариант — перейти на FreeBSD. :-)
Да, это уже понял. Там AIO вроде без проблем. Но ради одного сервиса изучать FreeBSD???
Хотя, если «упремся», возможно так и придется.
А, ну я-то FreeBSD изучил раньше линукса в свое время. Как раз единственное, для чего продолжаю ее использовать, — для нагруженных файлопомоек.
Еще можно обратиться в поддержку nginx. Полагаю, они заинтересованы в подобных кейсах (не зря же с тредпулом старались). Не факт, конечно, что помогут бесплатно. Но если купить коммерческую подписку, то точно должны — возможно, это для вас окажется экономически обосновано.
Но ради одного сервиса изучать FreeBSD???
Фряха — как велосипед (в хорошем смысле этого слова): сел и поехал. Все системные настройки находятся в паре файлов в /etc, все пользовательские — в /usr/local/etc. При небольшом навыке установка и настройка нового сервера занимает считанные минуты. Очень user-friendly система.
Не во всем. Пакетный менеджер очень специфический, а недавно его вообще не было (и это, полагаю, одна из причин потери приличной до этого доли рынка; другая весомая причина — отсутствие полноценной контейнерной виртуализации, и это при том, что jail-ы появились давным-давно).
Плюс всякая местная специфика типа "чтобы узнать настоящий размер бэклога, надо умножить циферку на 1.5" :-)
По железу. Нет цели «забить весь канал». Но думаю, 2-3 Gb в итоге потребуется.
CPU хватит за глаза. Диски… 8 штук, каждый гарантированно выдаст 50 МБ/с. В реальности больше (dd if=xxx.avi of=/dev/null выдает всегда больше 100 МБ/с). Итого в битах 8*50*8=3200 Мб.
Памяти бы добавить для кеша — это планируется.
Почему не возможно? Видео каждый раз приходит в разных форматах? Можно сделать автоматическую конвертацию. Но это будет оправдано, если клиенты смогут в HLS. Возможно стоит клиентов которые поддерживают, перевести на HLS, остальным отдавать файл сразу?
Каким образом зоопарк?.. Так сложилось. Такова жизнь. Слава богу клиенты этот зоопарк понимают. Хм… wmv вроде не понимают, но такого и не видел.
Вообще-то написал тут не особо заморачиваясь на видео.
Есть задача: раздавать произвольные части больших бинарных файлов. Так уж получилось, что это видео. В принципе, без разницы что это. Ну и как эту задачу лучше выполнить.
Всегда считал, что nginx для этого «самое то». Ну и когда уперся, начал экспериментировать. Получилось, что раздавать части файлов эффективнее через fastcgi. И даже php не принципиально, использовал что было «под рукой».
Т.е. связка nginx (без буферизации) — fastcgi оказалась эффективной для такой задачи.
Ну и решил поделиться своим удивлением ))
SAS-3, 12Gb/s, контроллер соответствующий.
И таких 8 штук. Пишется на них через «прокладку» aufs примерно равномерно. Так что чтение тоже примерно равномерное.
По гигабиту больше 950 Мб и не получится. Когда скорость отдачи подошла к 500 Мб (и кратковременные всплески до 800) поставили сетевую на 10 Гб. На тестах (1 URL * 200 потоков) получал 2,8 Гб выхлоп. Но это 1 URL, вероятно читает из кеша.
Сколько получится в реальности буду посмотреть.
await дисков мониторится, больше 10 мс не бывает.
Я сознательно не стал собирать RAID, хотя железо вполне позволяет.
Данные не настолько критичные, но потерять все равно жалко. Ну и было у меня несколько случаев, когда из-за смерти диска/контроллера терялся весь массив.
Может вам лучше иметь каждый диск отдельно, писать на него через mhddfs/aufs, а при считывании находить где находится на самом деле и читать от-туда? Можно nginx(ом) это разрулить.
То есть RAID0 не спасает от высокой утилизации?
Вот только большой трафик через эту прослойку гонять накладно для CPU.
Обычно на раздаче файлов много чтений и мало записей, поэтому писать в общую точку с автоматическим распределением, а читать из настоящего месторасположения — самое то.
LVM это тот-же RAID0, только чуть по-другому. Все равно «прослойка». И все плюсы с минусами от этого. LVM несколько удобнее — позволяет легко расширять дисковое пространство.
У обоих невозможно определить, где находится файл «по-настоящему», у обоих при смерти одного диска помирает все. И в обоих случаях, когда диск занят чем-то своим (это особенно касается SSD), доступ ко всему массиву в ступоре.
RAID0 (особенно аппаратный) хорошо помогает поднять IOPS если используются HDD. С SSD все на много печальнее. SSD периодически сами наводят у себя «порядок». Ну и весь массив будет недоступен пока какой-то диск занят своими делами.
О! Нашел статью: habrahabr.ru/company/webzilla/blog/227927
1) >1 — отдается всегда целиком (файл полностью);
Посмотрите в сторону раздачи mp4 через псевдо-стриминг (`ngx_http_mp4_module`).
```nginx
location ~ \.1080\.mp4$ {
mp4;
mp4_buffer_size 20m;
mp4_max_buffer_size 40m;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
expires max;
directio 10m;
limit_rate 1024m;
limit_rate_after 10m;
}
```
Получаем все плюшки nginx, в том числе range-bytes и контроль скорости отдачи.
Полный пример: habrahabr.ru/post/265897/?#2-razdayuschiy-server
2) >2 — отдается одним «виртуальным файлом» вида [конец первого файла]+[некоторое к-во файлов полностью]+[начало последнего файла]. Формат mpegts, каждый набор закодирован одинаково, поэтому можно просто склеивать части.
Посмотрите в сторону hls. В плейлист `playlist.m3u8` добавляете любые фрагменты, а потом nginx отдает все как маленькие статические файлы.
Пример `playlist.m3u8`:
```
#EXTM3U
#EXT-X-TARGETDURATION:13
#EXT-X-ALLOW-CACHE:YES
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:10.000,
cdn.example.com/video/1/part1.ts
#EXTINF:10.000,
cdn.example.com/video/1/part2.ts
#EXTINF:10.000,
cdn.example.com/video/1/part-100.ts
#EXT-X-ENDLIST
```
И уже уточнял. Проигрывается это (в том числе) на старых железяках типа ТВ-приставки. Так что надеяться на запросы с "?start=***" не приходится. Во всяком случае, в логах таких запросов нет. И далеко не факт, что эти железяки поймут playlist.m3u8.
2. hls позволит склеить [первый файл с 123457 байта до конца]+[второй файл до 7777 байта]?
А ffmpeg не решит всех ваших хотелок?
Или раздавать через ffmpeg?
А какие были исходные требования?
- в каких форматах файлы исходники;
- в каких форматах нужно отдавать;
- какая пропускная способность канала.
http://ffmpeg.org/ffserver-all.html#http
Ставьте, мотайте, продолжайте.
Ответил ниже https://habrahabr.ru/post/343346/#comment_10541018
- А ngx_http_mp4_module поймет mkv? А avi? А прочие?
Только mp4.
Так что надеяться на запросы с "?start=***"
Можно сделать подмену или редирект. А если пришли заголовки range-bytes, то сразу редиректить. ngx_http_mp4_module
умеет отрабатывать range-bytes.
hls позволит склеить [первый файл с 123457 байта до конца]+[второй файл до 7777 байта]
Позволяет. В playlist.m3u8
вставляете произвольные куски фрагментов любых видео какpart.ts
(MPEG-TS).
#EXTM3U
#EXT-X-TARGETDURATION:13
#EXT-X-ALLOW-CACHE:YES
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:10.000,
http://cdn.example.com/video/1/v1-1.ts
#EXTINF:10.000,
http://cdn.example.com/video/1/v1-2.ts
#EXTINF:10.000,
http://cdn.example.com/video/1/v2-1.ts
#EXTINF:10.000,
http://cdn.example.com/video/1/v2-2.ts
#EXTINF:10.000,
http://cdn.example.com/video/1/v2-3.ts
#EXT-X-ENDLIST
Сейчас имеется много контента, который этими модулями не поддерживаются.
hls тоже не в тему — клиенты не поддерживают. И изменить что-то у клиента невозможно.
Проблема у меня возникла именно с блокирующим чтением диска при обработке range-bytes запросов. Если кто предложит способ обойти именно эту проблему — подсказывайте плиз.
А вместо вот этого
while(!feof($fp)){ ... echo fread($fp, $buf) ... }
Можно попробовать "yield". Память есть в разы меньше
Что то типа такого
function getLines($file) { $f = fopen($file, 'r'); if (!$f) throw new Exception(); while ($line = fgets($f)) { yield $line; } fclose($f); }
Нафиг-нафиг, ночью бежать до серверной чтобы ребутнуть сервер.
Больше подошел бы stream_copy_to_stream, но в нем сложно копировать произвольную часть файла.
И этот stream_copy_to_stream в указанной статье дал примерно тот-же memory_get_peak_usage что и получилось у меня с passthru('/bin/dd ....').
Но у меня еще в скрипте дополнительно куча логики, которая ест память.
Так что не то. Функционала мало.
В моем же варианте «fpassthru($fd)» или «passthru('/bin/dd ...')» на 1000 клиентов придется максимум 1 GB.
Затраты на сам /bin/dd + «прокладка» sh — это мелочи. Приватной памяти они потребляют мизер (меньше 2К вместе). Затраты памяти на сам исполняемый код можно не учитывать — они в памяти в одном экземпляре.
Затраты времени на вызов системной команды маленькие — все нужное уже в памяти.
pm.max_children = 500 // пока хватает. расчеты и мониторинг показывают, что можно довести до 2000
pm.start_servers = 20 // т.е. 20 штук всегда будут ожидать подключение. обеспечивается скорость ответа
pm.min_spare_servers = 20
pm.max_spare_servers = 100
pm.max_requests = 50 // боялся утечек памяти, вроде нормально.
Памяти на борту 20 G. Вот сейчас (в nginx еще остались настройки на собственную отдачу, пока не убираю, жду выходных). 100 соединений, 300 Мб выхлоп:
# free -m
total used free shared buffers cached
Mem: 20114 19382 732 410 374 16318
-/+ buffers/cache: 2689 17425
Swap: 16363 0 16363
..../status:
pool: www
process manager: dynamic
start time: 27/Nov/2017:11:39:48 +1000
start since: 167057
accepted conn: 92179
listen queue: 0
max listen queue: 0
listen queue len: 0
idle processes: 39
active processes: 65
total processes: 104
max active processes: 151
max children reached: 0
slow requests: 0
Цинус в том, что эти child-потоки потребляют очень мало ресурсов. Собственной памяти порядка 0,5-1 МБ каждый. Затраты на CPU тоже мизерные. Там главный процесс — чтение диска.
В php.ini для php-fpm отключены все дополнительные модули (mysql, mysqli и прочее).
Т.е. практически «голый» php. По-идее, можно было бы вообще пересобрать php5-fpm без cripto, ssl, xml и прочего, в данном случае не нужного. В результате потребление памяти сократилось бы еще. Но и так, 1 МБ на процесс, меня вполне устраивает.
Способ занимательный, но боюсь вы просто не настроили nginx должным образом.
250 Mbit/s считается маленькой нагрузкой, если говорить про отдачу видео с диска.
Все интересное начинается от 1 Gbit/s, а на таких скоростях php может стать узким горлышком.
Очень возможно, что у меня просто не хватало worker_rlimit_nofile в nginx.
Но почему тогда fastcgi_pass помог? Ведь это тоже «файл» для воркера.
php не принципиально. Наверное, лучше было бы сделать на питоне.
Но даже в том виде что сейчас (раздача nginx — fastcgi — php) все свистит и не грузит систему. Мне самое главное, что получился очень быстрый коннект. Т.е. отдача на запрос начинается очень быстро. По большому счету, на соединении большой скорости и не нужно. Но этих соединений много.
В вашем конфиге я не увидел "aio on;", а его надо явно включать (по умолчанию он выключен).
Просто может быть ситуация, когда вы его вкомпилили, но не включили.
Поэтому я бы рекомендовал на досуге попробовать настройки из коммента выше, только добавить явно "aio on;" (что-то он там тоже не указан).
А вообще, да. И aio и thread_pool, и много чего еще пробовал ))
Числа были несколько другие только.
Все-таки, думаю, было мало worker_rlimit_nofile.
Было 4К то-ли из коробки, то-ли сам делал, не помню уже. Хотя, 4к на воркер должно бы хватать чисто умозрительно. Но вот сейчас сделал 64к и на запросы сервер стал отвечать стабильнее.
worker_processes 32;
worker_rlimit_nofile 65535;
pid /var/run/nginx.pid;
events {
worker_connections 10240;
multi_accept on;
use epoll;
}
# file handle caching / aio
open_file_cache max=100000 inactive=5m;
open_file_cache_valid 5m;
open_file_cache_min_uses 2;
open_file_cache_errors on;
aio threads;
Раздача видео. Засада: nginx или php?