Наш сервер пользовательского интерфейса должен сделать доступными пользователям определенные загружаемые файлы. Файлы живут на специализированном сервере хранения. Сервер пользовательского интерфейса применяет некоторые разрешения и использует пользователя HTTP Basic Auth / pwd для получения файлов с сервера хранения, но пользователь никогда не должен знать эти учетные данные хранения, поэтому файлы передаются пользователям через наш сервер пользовательского интерфейса.
Нам нужно передать их через наш интерфейсный сервер, на котором запущены Rails 4.2.11 и Ruby 2.4.9. До сих пор мы присваивали перечислитель телу ответа, например Ruby on Rails 3: потоковая передача данных через Rails на клиент , но это было довольно ненадежно, и у нас было много отрезанных или неполных загрузок.
Оказалось, что метод стоечного захвата (https://blog.chumakoff.com/en/posts/rails_sse_rack_hijacking_api) был более новым, и мы надеялись, что он будет более надежным, но он также обрезает загрузки, когда более медленные клиенты подключаются к загрузке, и это большой файл. .
Похоже, что буферы ввода-вывода заполняются и создают EOF или другие ошибки, поскольку загрузка передается с сервера хранения быстрее, чем пользователи обычно передают через пользовательский интерфейс.
Я получаю поток ввода-вывода в стойку (PhusionPassenger::Utils::UnseekableSocket
) и записываю результаты фрагментов из ответа GET на него.
Я пробовал многоразличные гемы http-клиентов, и все они имеют похожие проблемы, я думаю, потому что он заполняет буфер вывода при медленной загрузке клиентов, а не потому, что у них есть проблемы, хотяошибки выглядят таким образом.
Когда у клиентов http возникают проблемы в этом сценарии, они по-разному жалуются либо на EOF, либо на error reading from socket: Could not parse data entirely (1 != 0)
.
I 'Я пробовал разные версии Passenger и пробовал разные общие решения.
Я видел похожий вопрос на Rails: Как отправить файл с S3 на удаленный сервер ,В ответ он предлагает использовать <obj>.stat.size
, чтобы проверить буфер и не допустить его переполнения, чтобы он больше соответствовал скорости загрузки пользователя. Тем не менее, поток, который мне дал rack-hijack
, похоже, никак не может проверить размер или буфер, который я могу найти (PhusionPassenger::Utils::UnseekableSocket
). Если бы это раскрыло способ сделать это, я, вероятно, мог бы сделать это.
Я просмотрел документацию Passenger для информации о буферизации и изменил некоторые настройки буферизации, но это не помогло.
Я изучил методы в потоке, которые я получаю от Пассажира, а также на настройках .to_io
, но мне трудно найти инструменты, которые мне нужны, и имеющиепроблема с поиском большого количества документации.
Если я сопоставлю скорости загрузки с обоих концов, то проблем не будетТакие вещи, как wget --limit-rate=2m http://localhost:3000/download_test/stream1
, позволяют мне снизить скорость клиента, и я обычно получаю ошибку около 180 МБ при загрузке.
Он работает на Amazon Linux, но работает либо намного лучше, либо безмного проблем на локальной машине для разработки Mac. Трудно сказать точно, если это все еще проблематично или просто не так много, так как проблема может немного зависеть от размера и других факторов.
# a simplified version of the code, using a public URL
def stream1
url = 'https://www.spacetelescope.org/static/archives/images/publicationtiff40k/heic1502a.tif'
response.headers['Content-Type'] = 'image/tiff'
response.headers['Content-Disposition'] = 'attachment; filename="funn.tif"'
response.headers["X-Accel-Buffering"] = 'no'
response.headers["Cache-Control"] = 'no-cache'
response.headers["Last-Modified"] = Time.zone.now.ctime.to_s
response.headers["rack.hijack"] = proc do |stream|
Thread.new do
begin
response = HTTP.get(url)
response.body.each do |chunk|
stream.write(chunk)
end
rescue HTTP::Error => ex
logger.error("while streaming: #{ex}")
logger.error("while streaming: #{ex.backtrace.join("\n")}")
ensure
stream.close
end
end
end
head :ok
end
Результаты, которые я ищудолжны сделать загрузку надежной независимо от (разумной) скорости загрузки клиента из пользовательского интерфейса.
Спасибо за любую помощь, которую вы можете оказать, так как я чувствую себя застрявшим.