Rails 4, Puma, Nginx — ActionController::Live Streaming умирает после отправки первого фрагмента

Вот простой проект Rails 4, который я создал для устранения моей проблемы:

https://github.com/rejacobson/rails4-streamtest

У меня есть маршрут, настроенный в /home/stream, который должен передавать строку текста 5 раз с интервалом в 1 секунду.

def stream
  5.times do |n|
    puts "Streaming: #{n}"
    response.stream.write "Streaming: #{n+1}"
    sleep 1
  end
rescue IOError => e
  puts 'Connection closed'
ensure
  response.stream.close
end

Когда я запускаю puma с использованием tcp:// без nginx, потоковая передача работает отлично.

curl -N http://localhost:3000/home/stream

И я получаю обратно 5 строк, без проблем.

Когда я ввожу nginx, curl выводит первую строку, но сразу после этого завершает работу. Я продолжаю видеть результаты вызовов puts на сервере и в журналах, поэтому я знаю, что запрос все еще обрабатывается в цикле 5.times.

Он также не генерирует исключение IOError, как если бы пользователь разорвал соединение.

Вот что я пробовал до сих пор:

  • Различные комбинации директив nginx в основном файле conf и файле conf сервера.
  • Изменение настроек рельсов.
  • Различные настройки конфигурации пумы.
  • Установка всевозможных заголовков в методе контроллера в надежде, что это проблема с кэшированием.

Поиск в интернете тоже мало чем помог.

Я в растерянности и требую направления.

Вот результат обоих вызовов curl, с nginx и без него.

Без nginx

~/projects/streamtest ▰ master ▰
ryan mirage ▰▰▰▰ curl -i -N http://192.168.1.100:3000/home/stream
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-UA-Compatible: chrome=1
Cache-Control: no-cache
Content-Type: text/html; charset=utf-8
Set-Cookie: request_method=GET; path=/
X-Request-Id: 9ce86358-4476-404a-97e5-769c16ec7b0c
X-Runtime: 0.978099
Transfer-Encoding: chunked

Streaming: 1Streaming: 2Streaming: 3Streaming: 4Streaming: 5

puma.stdout

Streaming: 0
Streaming: 1
Streaming: 2
Streaming: 3
Streaming: 4
[8048] 192.168.1.100 - - [14/Mar/2014 21:04:50] "GET /home/stream HTTP/1.1" 200 - 6.0661

С нгинкс

~/projects/streamtest ▰ master ▰
ryan mirage ▰▰▰▰ curl -i -N http://192.168.1.100:3000/home/stream
HTTP/1.1 200 OK
Server: nginx/1.4.5
Date: Sat, 15 Mar 2014 04:02:40 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-UA-Compatible: chrome=1
ETag: "a505e0aa3b11b25301a9a704252a519a"
Cache-Control: max-age=0, private, must-revalidate
Set-Cookie: request_method=GET; path=/
X-Request-Id: 8983d199-026b-4082-a5f1-f1d6c886a3d6
X-Runtime: 0.016516

Streaming: 1

puma.stdout

Streaming: 0
[7558] 192.168.1.100 - - [14/Mar/2014 21:02:40] "GET /home/stream HTTP/1.0" 200 - 0.0214
Streaming: 1
Streaming: 2
Streaming: 3
Streaming: 4

Что интересно, и я только что это заметил, так это то, что расположение строки журнала запросов на получение:

"ПОЛУЧИТЬ /home/поток HTTP/1.0" 200

различается в каждом вызове curl и размещается в зависимости от того, сколько текста фактически передается в потоковом режиме.

Любые идеи относительно того, что здесь происходит? Почему рельсы не могут транслировать все это при использовании nginx?


person ooog    schedule 15.03.2014    source источник


Ответы (1)


Решено

Оказывается, все, что мне было нужно, это эта строка в моей директиве местоположения, чтобы заставить потоковую передачу работать через nginx:

прокси_http_версия 1.1;

Я обнаружил это исправление при исследовании директивы keepalive в восходящем модуле nginx:

http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive

Этот фрагмент текста меня особенно натолкнул на мысль:

For HTTP, the proxy_http_version directive should be set to “1.1” and the “Connection” header field should be cleared:

upstream http_backend {
    server 127.0.0.1:8080;

    keepalive 16;
}

server {
    ...

    location /http/ {
        proxy_pass http://http_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        ...
    }
}

Похоже, что по умолчанию nginx использует proxy_http_version 1.0;

Согласно Википедии, http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol

В HTTP/1.1 введено кодирование передачи по частям, позволяющее передавать содержимое в постоянных соединениях, а не буферизовать.

Итак, была моя проблема.

до

person ooog    schedule 15.03.2014
comment
Чего я до сих пор не понимаю, так это того, что до того, как я исправил это, заголовки ответов, о которых сообщил curl, показали, HTTP / 1.1 200 OK, Transfer-Encoding: chunked и Connection: keep-alive - person ooog; 16.03.2014
comment
Я получаю только первое событие, после чего поток закрывается и curl выдает мне это: curl: (18) transfer closed with outstanding read data remaining, и идея, в чем может быть проблема? - person Sheharyar; 04.09.2014
comment
Вот более подробная информация: stackoverflow.com/questions/25660399/ - person Sheharyar; 04.09.2014
comment
Спасибо! Вот именно такая проблема у меня была. - person geronime; 29.11.2016