Загрузка большого файла не работает в медленной сети при использовании HTTP/2 и AWS ALB

Учитывая следующую архитектуру:

client <-> AWS ALB <-> uwsgi <-> Django

Клиент не может загрузить файл размером 12 МБ при использовании HTTP/2 (по умолчанию), но работает при использовании HTTP/1.1. Файл передается через Django в целях аутентификации (он получен из стороннего сервиса).

Вот пример неудачи (я использую прокси-сервер Socks для ограничения полосы пропускания):

$ curl -x socks5://localhost:1080 https://example.com/file.pdf --output "file.pdf"

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
 79 12.6M   79 10.0M    0     0  61254      0  0:03:36  0:02:51  0:00:45 64422
curl: (92) HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)

Однако та же команда с флагом --http1.1 работает нормально.

Это не удается, когда я ограничиваю загрузку до 512 кбит/с - она работает при 1024 кбит/с. Я не искал «сладкую точку», она не важна.

Примечания:

  • Это также не работает в браузере, это не проблема curl
  • .
  • Использование curl с -v не дает никакой дополнительной информации.
  • uwsgi не выдает никаких ошибок. Насколько можно судить, он выполнил свою работу. Вот вывод:
    [pid: 44|app: 0|req: 2603/9176] [ip] () {34 vars in 639 bytes} [Wed Oct 16 09:29:29 2024] GET /file.pdf => generated 13243923 bytes in 2425 msecs (HTTP/1.1 200) 8 headers in 314 bytes (103471 switches on core 0)
    
  • Аналогично, в журналах ALB нет никаких проблем. Запрос регистрируется как успешный, но с количеством байт меньше ожидаемого.

Хотелось бы понять, почему он не работает при HTTP/2 и медленной сети. Я подозреваю, что это как-то связано с ALB.

Есть две вещи, которые можно было бы сделать и которые люди могли бы предложить:

  • Поместите Nginx между ALB и uwsgi
  • .
  • Не передавайте файл через Django

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

Итак, я нашел три вещи, которые устраняют проблему:

  • Попросите клиента использовать HTTP/1.1
  • Настройте ALB на использование HTTP/1.1
  • Увеличьте таймаут простоя ALB до 120 секунд

Последний пункт (это решение, которое я собираюсь принять) я попробовал только в крайнем случае - поскольку та же загрузка удалась с HTTP/1.1 (то же количество байт, те же условия сети), я не ожидал, что таймаут или буферизация могут быть проблемой.

Однако, похоже, ALB обрабатывает таймауты в HTTP/2 более агрессивно, чем в HTTP/1.1

Если бы я рискнул предположить:

  • uwsgi/nginx отправляют все байты в ALB, который их буферизирует [*]
  • Другое устройство (NLB, прокси, маршрутизаторы, брандмауэры и т.д.) между ALB и клиентом буферизирует часть данных - но не все
  • .
  • Клиент читает с этого промежуточного устройства. К тому времени, когда клиент прочитал достаточно данных, чтобы устройство-посредник захотело получить еще немного из ALB, мы достигли таймаута в 60 секунд, поэтому ALB закрыл соединение
  • .
  • Это происходит на HTTP/2, а не на HTTP/1.1, возможно, потому что ALB более агрессивно закрывает потоки на HTTP/2, потому что один и тот же сокет используется несколькими загрузками (общее предположение).

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

[*] в интернете нет однозначного ответа на вопрос, делают ли ALB буферизацию - некоторые говорят, что делают, но так как это закрытый источник и не указано в документации, у нас нет однозначного ответа

Вернуться на верх