Итак
Есть сервис, раздает видео-контент для просмотра (т.е. именно скачивание не предусматривается). При этом, весь контент делится на 2 категории:
1 — отдается целиком (файл полностью) / пауза, перемотка впред-назад;
2 — отдается одним «виртуальным файлом» вида [конец первого файла]+[некоторое к-во файлов полностью]+[начало последнего файла]. Формат mpegts, каждый набор закодирован одинаково, поэтому можно просто склеивать части.
Две эти категории отличаются логически (совершенно разные вещи), имеют четко отличающиеся URI и физически расположены в разных местах.
Как это предлагалось изначально
Связка nginx+apache.
Первая категория — банальная раздача контента в nginx с небольшим тюнингом.
Вторая часть — через скрипт apache-php при помощи цикла
while(!feof($fp)){
...
echo fread($fp, $buf)
...
}
где $fp — указатель на файл с выполненным fseek() куда нужно.
Что не понравилось
Как оказалось, nginx мало подходит для раздачи статики с большим количеством range-bytes запросов (а именно такие запросы в основном и получаются при онлайн-просмотре). Нет возможности использовать AIO для обслуживания таких запросов. В результате образуется длинная очередь к диску и у клиентов просмотр видео часто сопровождается «тормозами». Задавать большое количество буферов бесполезно — только зря расходовать память.
Пробовал последнюю версию nginx (1.12.2 на текущий момент), и --with-file-aio, и --with-threads, и всякое-разное. Эффекта не получил.
Ну и связка «echo fread()» в php тоже весьма сомнительная. Вывод fread идет в промежуточный буфер php и соответственно скрипт потребляет памяти не меньше этого буфера. Если читать файл маленькими кусочками, то увеличивается нагрузка на CPU и падает скорость отдачи. Если читать большими кусками, то на каждый запрос тратится слишком много памяти.
Что в итоге получилось
Ну, в первую очередь отказался от apache. Вместо него php5-fpm. Это дало существенную прибавку быстродействия (скорости ответа) + снизило потребление памяти.
Первую категорию
контента, ради эксперимента, решил попробовать отдавать своим скриптом.
В nginx:
location ~* /media/.*\..* {
fastcgi_pass unix:/var/run/php5-fpm.sock;
include /etc/nginx/sites-available/fastcgi_params;
fastcgi_param SCRIPT_FILENAME /var/www/m_download.php;
root /var/www;
send_timeout 1h;
}
m_download.php полностью приводить не буду. Основной функционал:
fpassthru($fd);
где $fd — указатель на файл. Предварительно, конечно, надо разобрать заголовок HTTP_RANGE, открыть файл и установить в нем смещение.
fpassthru() отдает файл «от текущего до конца». В данной ситуации это вполне подошло. Все проигрыватели корректно воспроизводят.
К моему большому удивлению, именно этот способ отдачи дал нужный мне результат. Очереди к диску нет (точнее используется системная, а с моими SAS-3 12Gb/sec и await<10ms вообще хорошо). Соответственно нет и ожидания обработки запроса. Скорость отдачи файла (если скачивать) — порядка 250 Mbit/s. «Тормоза» у клиентов совсем пропали.
При этом, использование памяти сильно уменьшилось, следовательно больше остается для файлового кеша. Сам скрипт потребляет порядка 0,5 MB приватной памяти при выполнении. Исполняемый код все равно существует в памяти в единственном экземпляре, поэтому его размер не имеет значения.
Вторая категория
(это где надо слепить несколько кусков разных файлов) тоже изменилась.
Отказался от связки «echo fread()».
К сожалению, в php нет функции для прямого вывода произвольного куска файла. В fpassthru() нет параметра «сколько выводить», он всегда выводит «до конца».
Попробовал вызов системного dd при помощи passthru().
Т.е.:
passthru('/bin/dd status=none if='.$fffilename.' iflag="skip_bytes,count_bytes" skip='.$ffseek.' count='.$buf_size);
И… о чудо! Потребление памяти скриптом чуть больше 0,5 MB, размер буфера могу задать любой (на память не влияет). Скорость отдачи (при буфере 4 MB)… свистит (те же 250 Mbit/s).
Вот такая история. В результате пришлось отказаться от раздачи контента просто nginx. Он используется, но только для перенаправления запросов на php5-fpm.
Если кратко, то мое ИМХО: nginx хорошо отдает статику, но плохо читает с диска.
Никогда бы не подумал, что раздача файлов через скрипт php может оказаться эффективнее.
Ну и добавлю, что искал какой-либо httpd с AIO для range-bytes запросов «из коробки». Вроде бы lighttpd 2й версии может, но версия пока нестабильная… Другого ничего подходящего не нашел.
Комментарии (62)
XaosSintez
28.11.2017 09:04+1Я бы предположил, что Вам нужен nginx модуль для pseudo streaming
Yagoda123 Автор
28.11.2017 09:24Смотрел это дело, но отказался. Контент в первой категории всяких-разных форматов. Воспроизведение предусматривается на (в том числе) старых железяках (типа ТВ-приставки), поэтому надеяться на URL c ?start=nn не приходится. А значит будет блокирующее чтение диска.
Чем мне еще понравилась получившаяся схема — один файловый кеш, системный. И он будет использовать всю доступную память, уменьшаясь при необходимости. Если же часть запросов (т.е. чтений диска) будут кешироваться в nginx, а другая в системном кеше, то это лишнее дублирование. Памяти, думаю, потребует больше.
Кстати, при некоторых настройках nginx (много и большие буферы) он у меня отжирал почти всю память, даже свопиться система начинала.
Jokerjar
28.11.2017 09:28Попробуйте сделать что-то типа:
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.Yagoda123 Автор
28.11.2017 09:39Хм… Надо именно такие параметры попробовать.
Похожее (но не совсем так) делал, ничего хорошего не получил. Только памяти nginx съел много.
В документации: «Невыравненный конец файла будет читаться блокированно. То же относится к запросам с указанием диапазона запрашиваемых байт (byte-range requests)». По моим оценкам так и получается.Yagoda123 Автор
28.11.2017 10:13Добавлю. Весь контент у меня находится на 8ми SAS-3 12Gbit/s дисках.
Читать все через «прокладку» mhddfs/aufs, по моим оценкам, «не гуд». Во всяком случае, тесты показали скорость чтения на много меньше чем читать напрямую.
Поэтому все запросы rewrit(ом) приводил к нужному мне виду и переключался на нужный диск при помощи try_files. До некоторых пор это помогало. Но трафик подрос, клиентов онлайн стало больше и появились «тормоза». Вот и начал экспериментировать.Jokerjar
28.11.2017 11:10У меня на серверах с подобной задачей все упирается в скорость чтения с дисков или производительность канала связи. В остальном, Nginx отлично справляется с настроенным aio, как я описывал выше, и тюнингом sysctl (тут уже индивидуально нужно тестировать). Если и использовать скрипты в схеме с отдачей видео, то только с x-accel-redirect, как я считаю. На счет второго пункта, можно подготавливать склеенный файл (на ssd или tmpfs) и x-accel-redirect'ить на него (продумав при этом периодическую чистку).
Yagoda123 Автор
28.11.2017 11:27У меня в диски и канал пока не упирается. Канал 10 Gb.
По факту в выходные было 850 Mb. На используемых дисках await выше 10 ms не поднимается. Обычно 2 — 5.
Склеивать у меня не выход. Надо уметь отдавать произвольную часть файла. проблема именно в этом. И в том, что практически все запросы такие.
jtiq
28.11.2017 12:27А можно пример настроек sysctl?
Yagoda123 Автор
28.11.2017 12:30Какие именно интересуют?
Все постить много…jtiq
28.11.2017 14:32Ну хотя бы основные, что стоит поменять после запуска сервера с HDD дисками и 1 гбит каналом.
Yagoda123 Автор
29.11.2017 02:22#uname -a
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
symbix
28.11.2017 10:36Надо тюнить настройки тредпулов и смотреть, во что упирается (возможно, в sysctl-и).
Еще есть радикальный вариант — перейти на FreeBSD. :-)
Yagoda123 Автор
28.11.2017 10:44Игрался с thread_pool. Даже несколько делал (на каждый диск свой). Не особо помогало.
Еще осложнило эксперименты то, то все это происходит на боевом сервере. Лишний раз клиентов дергать не хочется. А сымитировать такую нагрузку сложно.
// Еще есть радикальный вариант — перейти на FreeBSD. :-)
Да, это уже понял. Там AIO вроде без проблем. Но ради одного сервиса изучать FreeBSD???
Хотя, если «упремся», возможно так и придется.symbix
28.11.2017 11:44А, ну я-то FreeBSD изучил раньше линукса в свое время. Как раз единственное, для чего продолжаю ее использовать, — для нагруженных файлопомоек.
Еще можно обратиться в поддержку nginx. Полагаю, они заинтересованы в подобных кейсах (не зря же с тредпулом старались). Не факт, конечно, что помогут бесплатно. Но если купить коммерческую подписку, то точно должны — возможно, это для вас окажется экономически обосновано.
TrueRedRat
28.11.2017 11:56Но ради одного сервиса изучать FreeBSD???
Фряха — как велосипед (в хорошем смысле этого слова): сел и поехал. Все системные настройки находятся в паре файлов в /etc, все пользовательские — в /usr/local/etc. При небольшом навыке установка и настройка нового сервера занимает считанные минуты. Очень user-friendly система.symbix
28.11.2017 14:42Не во всем. Пакетный менеджер очень специфический, а недавно его вообще не было (и это, полагаю, основная причина потери приличной до этого доли рынка). Плюс всякая местная специфика типа "чтобы узнать настоящий размер бэклога, надо умножить циферку на 1.5" :-)
xXxSPYxXx
28.11.2017 22:55Сколько траффика отдаете?
Yagoda123 Автор
29.11.2017 02:25Интерфейс 10Gb. Зафиксированный максимум 850 Mb. На тестах (1 URL * 200 потоков) выдал 2,8 Gb.
xXxSPYxXx
29.11.2017 11:15Вопрос был Jokerjar.
Вам я бы посоветовал привести видео к одному формату и стримить через DASH, HDS, HLS, MSS. Конечно нужно смотреть что клиенты поддерживают. Ну и железо слабовато для утилизации 10G канала.Yagoda123 Автор
29.11.2017 12:40Приводить к одному формату, к сожалению, невозможно. Там реально зоопарк.
По железу. Нет цели «забить весь канал». Но думаю, 2-3 Gb в итоге потребуется.
CPU хватит за глаза. Диски… 8 штук, каждый гарантированно выдаст 50 МБ/с. В реальности больше (dd if=xxx.avi of=/dev/null выдает всегда больше 100 МБ/с). Итого в битах 8*50*8=3200 Мб.
Памяти бы добавить для кеша — это планируется.xXxSPYxXx
29.11.2017 13:26Попробуйте померять скорость так hdparm -Tt /dev/sda
Почему не возможно? Видео каждый раз приходит в разных форматах? Можно сделать автоматическую конвертацию. Но это будет оправдано, если клиенты смогут в HLS. Возможно стоит клиентов которые поддерживают, перевести на HLS, остальным отдавать файл сразу?Yagoda123 Автор
30.11.2017 03:22Timing cached reads: 15548 MB in 2.00 seconds = 7780.56 MB/sec
Timing buffered disk reads: 624 MB in 3.00 seconds = 207.74 MB/sec
Конвертировать неоправданно. «Продвинутых» клиентов сильно мало.
Jokerjar
30.11.2017 04:27Каким образом у вас зоопарк просматривают «онлайн»? Для веба (и каждого браузера) есть определенный конкретный список форматов. Как, допустим, проиграть онлайн wmv?
Yagoda123 Автор
30.11.2017 04:47Хм… Хотя отдача и по http, но это не Веб. Т.е. клиенты не используют какой-либо браузер. Используются специализированные «железяки» или специализированный софт (ПК, Smart-TV, Android). Список доступного контента и ссылки на него получают тоже через http с другого сервера. Этот просто раздает.
Каким образом зоопарк?.. Так сложилось. Такова жизнь. Слава богу клиенты этот зоопарк понимают. Хм… wmv вроде не понимают, но такого и не видел.
Вообще-то написал тут не особо заморачиваясь на видео.
Есть задача: раздавать произвольные части больших бинарных файлов. Так уж получилось, что это видео. В принципе, без разницы что это. Ну и как эту задачу лучше выполнить.
Всегда считал, что nginx для этого «самое то». Ну и когда уперся, начал экспериментировать. Получилось, что раздавать части файлов эффективнее через fastcgi. И даже php не принципиально, использовал что было «под рукой».
Т.е. связка nginx (без буферизации) — fastcgi оказалась эффективной для такой задачи.
Ну и решил поделиться своим удивлением ))
Jokerjar
30.11.2017 04:26Сервера по гигабиту, отдают в пик примерно 937 мегабит, упирается в скорость чтения с дисков (обычные HDD 4Tb).
Yagoda123 Автор
30.11.2017 04:59Ну, у меня не совсем обычные HDD.
SAS-3, 12Gb/s, контроллер соответствующий.
И таких 8 штук. Пишется на них через «прокладку» aufs примерно равномерно. Так что чтение тоже примерно равномерное.
По гигабиту больше 950 Мб и не получится. Когда скорость отдачи подошла к 500 Мб (и кратковременные всплески до 800) поставили сетевую на 10 Гб. На тестах (1 URL * 200 потоков) получал 2,8 Гб выхлоп. Но это 1 URL, вероятно читает из кеша.
Сколько получится в реальности буду посмотреть.
await дисков мониторится, больше 10 мс не бывает.
xXxSPYxXx
30.11.2017 08:58У меня 20Г интерфейс, 8 SSD Дисков в RAID0, но мы упираемся в IOPS. Я думаю упремся при 15Г. Какие параметры ядра стоит подкрутить? Или лучше использовать каждый диск отдельно? Сохранность данных не важна.
Yagoda123 Автор
30.11.2017 09:46Как-то тут на хабрахабре проскакивала статья про RAID из SSD. Человек делал эксперименты, замерял результаты. Получилась плохая латентность. Как раз IOPS сильно падал.
Я сознательно не стал собирать RAID, хотя железо вполне позволяет.
Данные не настолько критичные, но потерять все равно жалко. Ну и было у меня несколько случаев, когда из-за смерти диска/контроллера терялся весь массив.
Может вам лучше иметь каждый диск отдельно, писать на него через mhddfs/aufs, а при считывании находить где находится на самом деле и читать от-туда? Можно nginx(ом) это разрулить.xXxSPYxXx
30.11.2017 09:56Не разу не использовал эти ФС. А если использовать LVM? И как я узнаю где лежит файл? =)
То есть RAID0 не спасает от высокой утилизации?Yagoda123 Автор
30.11.2017 11:31mhddfs/aufs, в принципе неплохая штука если надо логически объединить несколько каталогов/дисков. Из плюсов — каждый файл целиком лежит в одном месте. И при гибели одного диска остальное все целое.
Вот только большой трафик через эту прослойку гонять накладно для CPU.
Обычно на раздаче файлов много чтений и мало записей, поэтому писать в общую точку с автоматическим распределением, а читать из настоящего месторасположения — самое то.
LVM это тот-же RAID0, только чуть по-другому. Все равно «прослойка». И все плюсы с минусами от этого. LVM несколько удобнее — позволяет легко расширять дисковое пространство.
У обоих невозможно определить, где находится файл «по-настоящему», у обоих при смерти одного диска помирает все. И в обоих случаях, когда диск занят чем-то своим (это особенно касается SSD), доступ ко всему массиву в ступоре.
RAID0 (особенно аппаратный) хорошо помогает поднять IOPS если используются HDD. С SSD все на много печальнее. SSD периодически сами наводят у себя «порядок». Ну и весь массив будет недоступен пока какой-то диск занят своими делами.
О! Нашел статью: habrahabr.ru/company/webzilla/blog/227927
Jokerjar
30.11.2017 11:23Иногда единственное решение — расширение на несколько серверов. Учитывая, что аренда серверов с очень широким каналом стоит немалых денег, иногда даже выходит экономичнее взять несколько серверов и «запараллелить» их. У меня балансировка происходит прямо в логике сайта — скрипт по определенным правилам отдает контент клиенту с определенного сервера. Каждый сервер содержит одинаковые данные (зеркализируются rsync'ом). Заодно и резервная копия имеется.
prodex
28.11.2017 10:12Nginx отлично справляется с раздачей видео из коробки.
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
```Yagoda123 Автор
28.11.2017 10:231. А ngx_http_mp4_module поймет mkv? А avi? А прочие?
И уже уточнял. Проигрывается это (в том числе) на старых железяках типа ТВ-приставки. Так что надеяться на запросы с "?start=***" не приходится. Во всяком случае, в логах таких запросов нет. И далеко не факт, что эти железяки поймут playlist.m3u8.
2. hls позволит склеить [первый файл с 123457 байта до конца]+[второй файл до 7777 байта]?Shtucer
28.11.2017 10:34А ffmpeg не решит всех ваших хотелок?
Yagoda123 Автор
28.11.2017 10:36Конвертировать все? ООООООО Только не это.
Или раздавать через ffmpeg?Shtucer
28.11.2017 10:39Стримить через ffmpeg.
Yagoda123 Автор
28.11.2017 10:53Мне надо отдать по запросу HTTP. Поставить на паузу, перемотать вперед-назад, продолжить.
prodex
28.11.2017 11:03А какие были исходные требования?
- в каких форматах файлы исходники;
- в каких форматах нужно отдавать;
- какая пропускная способность канала.
Yagoda123 Автор
28.11.2017 11:09Формат всякий-разный. Воспроизводит специализированный софт или железо. Изменить его не могу.
Отдавать как есть.
Канал 10 Gb.
prodex
28.11.2017 10:42- А 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
Yagoda123 Автор
28.11.2017 11:04Модули nginx http_flv_module и http_mp4_module не подходят.
Сейчас имеется много контента, который этими модулями не поддерживаются.
hls тоже не в тему — клиенты не поддерживают. И изменить что-то у клиента невозможно.
Проблема у меня возникла именно с блокирующим чтением диска при обработке range-bytes запросов. Если кто предложит способ обойти именно эту проблему — подсказывайте плиз.
rebel_web
28.11.2017 11:13А вместо вот этого
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); }
Yagoda123 Автор
28.11.2017 11:19Это из питона? В принципе можно и на нем делать. Но тут принципиальной разницы нет. Все равно "$line = fgets($f)" сожрет кучу памяти. Если делать на рабочей машине — нормально. А на сервере, да на каждый запрос…
Нафиг-нафиг, ночью бежать до серверной чтобы ребутнуть сервер.rebel_web
28.11.2017 12:43нет это php. Вот тут человек пробовал разные варианты чтения.
Yagoda123 Автор
28.11.2017 12:58Там читался текстовый файл. У меня бинарные.
Больше подошел бы stream_copy_to_stream, но в нем сложно копировать произвольную часть файла.
И этот stream_copy_to_stream в указанной статье дал примерно тот-же memory_get_peak_usage что и получилось у меня с passthru('/bin/dd ....').
Но у меня еще в скрипте дополнительно куча логики, которая ест память.
Так что не то. Функционала мало.
kenbo
29.11.2017 02:25Для вывода в PHP можно писать напрямую в stdout: fwrite(STDOUT, fread($fd, $buf_size));
Yagoda123 Автор
29.11.2017 02:44Это тоже самое что и «echo fread()». Читаются данные в свой некоторый буфер php. Затем эти данные выплевываются в STDOUT. PHP, прежде чем прочитать данные, запросит у системы память в объеме указанного буфера. Соответственно, скрипт будет потреблять приватной памяти не меньше этого буфера. Если читаем по 100К, то вроде не страшно. Но скорость отдачи получается низкая. В ходе экспериментов выяснил, что делать буфер меньше 2М нельзя. Т.е. скрипт будет съедать памяти 2М под буфер + ~0,5М для своих нужд. Итого, 1000 клиентов съедят 2,5 GB.
В моем же варианте «fpassthru($fd)» или «passthru('/bin/dd ...')» на 1000 клиентов придется максимум 1 GB.
Затраты на сам /bin/dd + «прокладка» sh — это мелочи. Приватной памяти они потребляют мизер (меньше 2К вместе). Затраты памяти на сам исполняемый код можно не учитывать — они в памяти в одном экземпляре.
Затраты времени на вызов системной команды маленькие — все нужное уже в памяти.
Jokerjar
29.11.2017 02:52А какие настройки пула php-fpm? Подозреваю, что с такой схемой child-потоков плодится немереное количество?
Yagoda123 Автор
29.11.2017 03:05pm = dynamic
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: 0Yagoda123 Автор
29.11.2017 03:54Добавлю.
В php.ini для php-fpm отключены все дополнительные модули (mysql, mysqli и прочее).
Т.е. практически «голый» php. По-идее, можно было бы вообще пересобрать php5-fpm без cripto, ssl, xml и прочего, в данном случае не нужного. В результате потребление памяти сократилось бы еще. Но и так, 1 МБ на процесс, меня вполне устраивает.
lexore
30.11.2017 11:55Способ занимательный, но боюсь вы просто не настроили nginx должным образом.
250 Mbit/s считается маленькой нагрузкой, если говорить про отдачу видео с диска.
Все интересное начинается от 1 Gbit/s, а на таких скоростях php может стать узким горлышком.Yagoda123 Автор
30.11.2017 12:06Вероятно Вы правы.
Очень возможно, что у меня просто не хватало worker_rlimit_nofile в nginx.
Но почему тогда fastcgi_pass помог? Ведь это тоже «файл» для воркера.
php не принципиально. Наверное, лучше было бы сделать на питоне.
Но даже в том виде что сейчас (раздача nginx — fastcgi — php) все свистит и не грузит систему. Мне самое главное, что получился очень быстрый коннект. Т.е. отдача на запрос начинается очень быстро. По большому счету, на соединении большой скорости и не нужно. Но этих соединений много.lexore
30.11.2017 12:44+1В вашем конфиге я не увидел "aio on;", а его надо явно включать (по умолчанию он выключен).
Просто может быть ситуация, когда вы его вкомпилили, но не включили.
Поэтому я бы рекомендовал на досуге попробовать настройки из коммента выше, только добавить явно "aio on;" (что-то он там тоже не указан).Yagoda123 Автор
30.11.2017 12:52Полностью конфиг не приводил. Указал только location.
А вообще, да. И aio и thread_pool, и много чего еще пробовал ))
Числа были несколько другие только.
Все-таки, думаю, было мало worker_rlimit_nofile.
Было 4К то-ли из коробки, то-ли сам делал, не помню уже. Хотя, 4к на воркер должно бы хватать чисто умозрительно. Но вот сейчас сделал 64к и на запросы сервер стал отвечать стабильнее.xXxSPYxXx
30.11.2017 12:57user www-data;
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;
Waki
Интересный эксперимент. Думаю сейчас придут люди которые хорошо разбираются с тюнингом nginx и расскажут почему у вас с ним ничего не получилось :)
zuborg
Гадание без конфигов — неблагодарное дело.