Загрузка большого файла не работает в медленной сети при использовании 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 буферизацию - некоторые говорят, что делают, но так как это закрытый источник и не указано в документации, у нас нет однозначного ответа