LINUX.ORG.RU
ФорумAdmin

HTTP/2 замедляет сайт на порядок

 ,


0

2

Всё внутри kubernetes, но в целом он тут, кажется, не очень важен, я грешу на nginx. В качестве ингресса - ingress-nginx.

Общая архитектура: на входе стоит ingress-nginx, за ним несколько сервисов. Статика в отдельном контейнере с nginx, также есть несколько сервисов.

Сейчас ingress-nginx настроен с отключенным HTTP/2. Загрузка выглядит так. Сгруппированные ресурсы начинают загружаться одновременно.

/researches   2.4 kB   24 ms

  /static/js/main.dea63410.chunk.js    124 kB    108 ms
  /static/js/4.596e458a.chunk.js      1700 kB   1070 ms

    /api/dictionaries/recommendationsWithServiceNames   3.8 kB   40 ms
    /api/dictionaries/structuredConclusionResults       1.6 kB   36 ms
    /api/dictionaries/structuredDescriptions            2.4 kB   47 ms

      /static/js/10.8d651677.chunk.js   15.6 kB   59 ms
...

Как видно, в этом примере основное время уходит на /static/js/4.596e458a.chunk.js, остальное грузится условно-моментально. Дальше грузятся другие ресурсы, но пока остановимся тут, для демонстрации проблемы этого достаточно.

Я включаю http-2 в ingress-nginx, больше ничего не делаю. Насколько я понимаю, это добавляет директиву http2 on во все локации сгенерированного nginx.conf.

И теперь смотрю загрузку:

/researches   2.2 kB   34 ms

  /static/js/main.dea63410.chunk.js    124 kB   778 ms
  /static/js/4.596e458a.chunk.js      1700 kB   732 ms

    /api/dictionaries/recommendationsWithServiceNames   3.6 kB   832 ms
    /api/dictionaries/structuredConclusionResults       1.4 kB   832 ms
    /api/dictionaries/structuredDescriptions            2.2 kB   832 ms

      /static/js/10.8d651677.chunk.js   15.5 kB   975 ms

Первое, что заметно - размер загрузки чуть поменьше у каждого ресурса. Вероятно заголовки чуть меньше весят.

Второе, что заметно - файл /static/js/main.dea63410.chunk.js грузится очень долго. В целом это не проблема, т.к. грузится параллельно со /static/js/4.596e458a.chunk.js но всё же отличие большое.

И самая фатальная проблема - мелкие эндпоинты и мелкие статические файлики начинают грузиться очень долго.

Суммарно с HTTP/1.1 страница появляется примерно через 2-3 секунды, с HTTP/2 через 10-15 секунд.

Собственно всё время теряется непонятно где - в мелких файликах, которые реально из проксируемого сервиса отдаются абсолютно моментально.

Все вышеперечисленные измерения проведены на практически неиспользуемом сайте, кеширование отключено, замеры произведены многократно и тд и тп, воспроизводится в 100% случаев.

Я это явление увидел примерно год назад, разбираться не стал, просто отключил HTTP/2. Сейчас всё же хочу разобраться, т.к. версия с тем, что в какой-то версии nginx был какой-то баг, уже не принимается, с той поры обновлялся не раз и постоянно пробую включать HTTP/2 и постоянно наблюдаю эту ситуацию.

Размер сгенерированного файла nginx.conf больше 3000 строк, полагаю, никто его читать не будет, если интересуют конкретные директивы - спрашивайте, посмотреть недолго.

Если я из пода с ingress-nginx-ом дёргаю curl-ом запрашиваемые URL-ы у проксируемых сервисов, всё отрабатывает абсолютно моментально. В тех местах тормозить просто нечему.

Честно говоря я пока не понимаю, в чём может быть проблема. Я бы понял, если бы разница была в десятки процентов даже, но когда время загрузки файла вырастает с 40 ms до 840 ms, это мне непонятно.

Никаких особых настроек у ingress-nginx я не делал, практически все настройки «по умолчанию», те, что не по умолчанию - я пробовал менять.

  allow-snippet-annotations: "false"
  enable-brotli: "false"
  proxy-buffering: "off"
  proxy-real-ip-cidr: 10.160.32.0/20
  use-gzip: "false"
  use-http2: "true"
  use-proxy-protocol: "true"

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

Сайт за HTTPS, терминируется в самом ingress-nginx, поэтому грешить на что-либо до него я тоже не могу, перед ним, конечно, стоит балансировщик нагрузки, но для него это просто бинарный непрозрачный поток.

★★★★

Последнее исправление: vbr (всего исправлений: 1)

При HTTP/2 у тебя вся передача между веб-обозревателем и сервером идет по одному TCP-соединению - все запросы мультиплексируются внутри этого соединения. Число мультиплексируемых запросов для каждого сайта можно настроить. Не знаю, как это делается в ingress-nginx, а в обычном Nginx для этой цели можно использовать директиву http2_max_concurrent_streams.

При HTTP/1.1 веб-обозреватель устанавливает параллельно сразу несколько TCP-соединений, по которым на сервер по инету доставляются параллельно отдельные HTTP-запросы - ну т.е. необходимые данные действительно могут закачаться в веб-обозреватель быстрее чем при HTTP/2, поскольку передаются реально параллельно.

Для начала сравни скорость закачки просто одного файла - какой-нибудь картинки. Если будет сильно различаться, то можно уже искать какие-то более конкретные причины тормозов. В противном случае просто сам сервер или инет может тормозить.

Ну а вообще, если у тебя используется managed Kubernetes от какого-нибудь облачного провайдера или есть иная возможность использовать просто TCP-LoadBalancer в виде соответствующего Service-объекта, то можно просто подвязать его к Deployment, в Pod-ах которого крутится обычный Nginx, и настраивать этот Nginx как тебе удобно. HTTPS будет терминироваться непосредственно в соответствующих Nginx Pod-ах.

vinvlad ★★
()
Последнее исправление: vinvlad (всего исправлений: 1)
Ответ на: комментарий от vinvlad

http2_max_concurrent_streams там стоит 128. У меня запросов гораздо меньше. В это упираться не должно.

Скорость закачки одного файла нормальная. Сервер и инет не тормозят.

vbr ★★★★
() автор топика
Ответ на: комментарий от vbr

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

Если скорости закачки одного файла /static/js/4.596e458a.chunk.js одинаковые и там и там, то возможно проблема возникает из-за одновременной закачки слишком большого количества файлов. Ну и надо четко понимать, что HTTP/2 экономит число TCP-соединений - но уменьшает реальную параллельность приема данных, относящихся к отдельным HTTP-запросам.

vinvlad ★★
()
Ответ на: комментарий от Elyas

Читал, буферизация у меня включена. У меня проблема не со throughput-ом, а с латентностью (ну или со скоростью), когда крохотные файлы начинают скачиваться секундами. И там не в полтора раза проседание, а в десятки.

Пока у меня рабочая версия - в nginx просто кривая поддержка HTTP/2, но может ещё чего найдётся…

vbr ★★★★
() автор топика
Ответ на: комментарий от vbr

Там это, смотри где ты хостишься и откуда у куда у тебя лаги идут. А то может быть что с тобой рядом на одном айпишнике/подсети что-то интересное РКН-у лежит и тебя замедляют за компанию в пределах РФ.

anonymous
()
Ответ на: комментарий от vbr

Уменьшение параметра дало какой-нибудь эффект?
На самом деле значение «6» - это грубый эквивалент HTTP/1.1 по скорости закачки, но чуть помедленнее за счет передачи всех данных по одному соединению.

Просто почитай, как работает протокол HTTP/2 - и станет ясно, что надо настраивать.

vinvlad ★★
()
Последнее исправление: vinvlad (всего исправлений: 1)
Ответ на: комментарий от vel

Статический файл, отдаётся из другого nginx-а.

Блокировок сессии нигде не должно быть, серверной сессии, как таковой, вроде нет нигде, только авторизация через заголовки (но это в /api/ эндпоинтах).

vbr ★★★★
() автор топика
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от vbr

Мой вам совет: поскольку вы предпочитаете терминировать HTTPS внутри кластера не полагаясь на кастомные k8s-компроненты конкретных облачных платформ (что весьма практично и верно в плане переносимости), не мучайтесь и настройте на уровне ingress (ingress-nginx) просто прозрачный TCP-Ingress. Ну и как-нибудь оптимально подружите его со входным балансировщиком чтобы избежать лишних накладных расходов. Всю непосредственную логику HTTPS-сервера (Nginx) вынесите в отдельный Deployment, где и будет осуществляться терминирование HTTPS.

У меня такая схема работает в течении уже нескольких лет и никаких проблем с HTTP/2 не возникает.

vinvlad ★★
()