Как стать автором
Обновить

Комментарии 17

Вызов sendfile проходит успешно и отправляет 5 мегабайт данных в буфер отправки.

Я не очень силен во внутренностях nginx, но 5МБ в буфер отправки — слишком расточительно. Сколько одновременных медленных клиентов потянет такой сервер? Даже при уменьшении буфера до мегабайта. Или здесь идет расчет на то, что клиентов не сильно много?
К примеру, выдержит ли сервер простую атаку на открытие ~1000 одновременных медленных curl с единственного хоста? Да и файл может быть не 5МБ, а куда больше.
Файл положится в буфер. По поводу 1000 соединений: они, скорее всего будут заблокированы или попадут в очередь (точно не 1000 параллельно открытых файлов для 1 сервера). И 5 Гб оперативной памяти под буферизацию — это совсем мало.

Теоретически, можно на 1 сервер попробовать атаку slow download с 100 000 клиентов, которые будут качать разные большие файлы (500 Гб оперативной памяти точно нет на сервере). Но в архитектуре же не единственный сервер используется, а десятки и сотни. Т.е. для практической реализации этого вектора нужно несколько (десятков) миллионов разных клиентов, которые медленно «тянут» (и быстро бросают) разные большие файлы (чтобы забить буфера). При ботнете такого масштаба эффективнее использовать уже другие методы (хотя бы тот же DNS Flood).
И 5 Гб оперативной памяти под буферизацию — это совсем мало.

5ГБ под буфферизацию конкретно 1000 соединений — это очень много, а если эта 1000 соединений ещё и с ограничением по скорости — то это очень-очень много.
Гораздо оптимальнее будет использовать эту память под кеширование файлов, например.
Это внутренности ядра. net.ipv4.tcp_wmem это именно ядро.
Такой буфер создаётся под каждое соединение и размер файла не важен. Но и память будет невыгружаемая.
Больше того, ядро может раздуть буффер до 32 мегов (как написано в примере), но раздует оно его только если клиент будет быстрым.

Скорее всего объём RAM будет одним из ограничений на количество возможных соединений

Мне вот интересно, понимает ли ядро Linux что 100 одинаковых sendfile посылают один и тот же файл (можно же отследить теоретически) и используют одну копию в памяти или делают 100 буферов.
насколько я помню sendfile, созданный специально чтобы не гонять данные из буфера в буфер, там память может вообще не расходоваться на буфер, используя чтение через mmap
НЛО прилетело и опубликовало эту надпись здесь
Здесь, возможно, проблема приложения, а не сервера с яндекс-картами.
Насколько я понял из моих небольших познаний в написании приложения для Android, если требуется продолжение какого-либо действия приложения при заблокированном телефоне, то, во-первых, пишется сервис, выполняющий нужные действия, во-вторых, приложению выдается разрешение WAKE_LOCK, оно через PowerManager говорит, что не надо блочить процессор или экран (или все вместе), а, в-третьих, надо отдельно прописать, что не блочить WiFi — тоже не надо. Обычная мобильная сеть, судя по-всему, не блочится, если процессор не спит.
НЛО прилетело и опубликовало эту надпись здесь
Крис Бранч подготовил патч для NGINX, в котором реализован вариант с опцией send_minimum_rate, которая позволяет определить насколько медленное скачивание разрешено клиенту.

Все же лучше просто сделать workaround для этой особенности Linux ядра, тем более что это можно сделать без особенного оверхеда на проверку текущего размера буффера через ioctl(TIOCOUTQ):
— устанавливаем таймаут в половину от заданного. Для быстрых клиентов это не будет иметь значения.
— по достижению таймаута проверяем размер буффера, запоминаем, повторно устанавливаем таймаут (в половину от заданного).
— по достижению повторного таймаута делаем ещё раз проверку, если размер буффера не изменился (клиент не читает данные) — закрываем соединение, иначе ещё раз запоминаем новый размер, ставим опять половинный таймаут и повторяем.
Удивительно, что корни этой проблемы нашли только сейчас, где же они были во времена диалапа и безлимиток со скоростями 64-128 кбит/с?
До сих пор хорошо помнится то чувство 8-10 летней давности, когда скачивание какого-нибудь объемного архива через несколько часов внезапно и типа нормально завершалось в районе 90%, а по факту оказывался «неожиданный конец архива» и необходимость качать заново. К сожалению за давностью лет уже не помню, почему не пользовался всякими менеджерами закачек типа Регета, вроде они не особо дружили со всякими калечными файлообменниками типа покойной рапидшары, ну и я сам тогда скорее всего «не умел их готовить», как говорится.
Возможно, буферы были меньше.
А может что-то поменялось в реализации.
Ну сейчас пользователи бесплатной Йоты с 64кбит тоже получается в пролёте, если будут качать два файла с двух разных серверов одновременно, получая всего лишь 32кбит с каждого.

Немного поправленный патч для nginx-1.8

Есть ли эта проблема на FreeBSD? На какие sysctl обратить внимание, чтобы проверить это?

% sysctl net.inet.tcp
net.inet.tcp.rfc1323: 1
net.inet.tcp.mssdflt: 536
net.inet.tcp.keepidle: 7200000
net.inet.tcp.keepintvl: 75000
net.inet.tcp.sendspace: 32768
net.inet.tcp.recvspace: 65536
net.inet.tcp.keepinit: 75000
net.inet.tcp.delacktime: 100
net.inet.tcp.v6mssdflt: 1220
net.inet.tcp.nolocaltimewait: 0
net.inet.tcp.maxtcptw: 27767
net.inet.tcp.per_cpu_timers: 0
net.inet.tcp.v6pmtud_blackhole_mss: 1220
net.inet.tcp.pmtud_blackhole_mss: 1200
net.inet.tcp.pmtud_blackhole_failed: 0
net.inet.tcp.pmtud_blackhole_activated_min_mss: 0
net.inet.tcp.pmtud_blackhole_activated: 0
net.inet.tcp.pmtud_blackhole_detection: 0
net.inet.tcp.rexmit_drop_options: 0
net.inet.tcp.keepcnt: 8
net.inet.tcp.finwait2_timeout: 60000
net.inet.tcp.fast_finwait2_recycle: 0
net.inet.tcp.always_keepalive: 1
net.inet.tcp.rexmit_slop: 200
net.inet.tcp.rexmit_min: 30
net.inet.tcp.msl: 30000
net.inet.tcp.persmax: 60000
net.inet.tcp.persmin: 5000
net.inet.tcp.syncache.rst_on_sock_fail: 1
net.inet.tcp.syncache.rexmtlimit: 3
net.inet.tcp.syncache.hashsize: 512
net.inet.tcp.syncache.count: 0
net.inet.tcp.syncache.cachelimit: 15375
net.inet.tcp.syncache.bucketlimit: 30
net.inet.tcp.syncookies_only: 0
net.inet.tcp.syncookies: 1
net.inet.tcp.soreceive_stream: 0
net.inet.tcp.isn_reseed_interval: 0
net.inet.tcp.icmp_may_rst: 1
net.inet.tcp.pcbcount: 234
net.inet.tcp.do_tcpdrain: 1
net.inet.tcp.tcbhashsize: 131072
net.inet.tcp.log_debug: 0
net.inet.tcp.minmss: 216
net.inet.tcp.sack.globalholes: 0
net.inet.tcp.sack.globalmaxholes: 65536
net.inet.tcp.sack.maxholes: 128
net.inet.tcp.sack.enable: 1
net.inet.tcp.reass.overflows: 0
net.inet.tcp.reass.cursegments: 0
net.inet.tcp.reass.maxsegments: 45800
net.inet.tcp.sendbuf_max: 2097152
net.inet.tcp.sendbuf_inc: 8192
net.inet.tcp.sendbuf_auto: 1
net.inet.tcp.tso: 1
net.inet.tcp.path_mtu_discovery: 1
net.inet.tcp.recvbuf_max: 2097152
net.inet.tcp.recvbuf_inc: 16384
net.inet.tcp.recvbuf_auto: 1
net.inet.tcp.insecure_rst: 0
net.inet.tcp.ecn.maxretries: 1
net.inet.tcp.ecn.enable: 0
net.inet.tcp.abc_l_var: 2
net.inet.tcp.rfc3465: 1
net.inet.tcp.experimental.initcwnd10: 1
net.inet.tcp.rfc3390: 1
net.inet.tcp.rfc3042: 1
net.inet.tcp.do_pipe: 0
net.inet.tcp.drop_synfin: 0
net.inet.tcp.delayed_ack: 1
net.inet.tcp.blackhole: 0
net.inet.tcp.log_in_vain: 0
net.inet.tcp.hostcache.purge: 0
net.inet.tcp.hostcache.prune: 300
net.inet.tcp.hostcache.expire: 3600
net.inet.tcp.hostcache.count: 87
net.inet.tcp.hostcache.bucketlimit: 30
net.inet.tcp.hostcache.hashsize: 512
net.inet.tcp.hostcache.cachelimit: 15360
net.inet.tcp.cc.available: newreno
net.inet.tcp.cc.algorithm: newreno
В качестве workaround можно для начала попробовать уменьшить sendfile_max_chunk
Не работает
Зарегистрируйтесь на Хабре, чтобы оставить комментарий