Статьи

Потоковая передача с Rails 4

Значок приложения Media Player Что такое потоковое?

Потоковая передача существует в Rails начиная с версии 3.2, но она ограничена потоковой передачей шаблонов . Rails 4 поставляется с гораздо более зрелой функциональностью потоковой передачи в реальном времени. По сути, это означает, что Rails теперь может обрабатывать объекты ввода-вывода и отправлять данные клиенту в режиме реального времени.

StreamingLiveActionController StreamingLive

Основной потоковый API использует класс Fiber Fiber Потоки, вызванные Fiber

Потоковое шаблонов

Потоковая передача инвертирует обычный Rails-процесс рендеринга макета и шаблона. По умолчанию Rails сначала отображает шаблон, а затем макет. Первый метод, который он запускает, — yield Затем активы и макеты отображаются.

Рассмотрим представление с интенсивным запросом, такое как общесистемная временная шкала нескольких классов, например:

 class TimelineController
  def index
    @users = User.all
    @tickets = Ticket.all
    @attachments = Attachment.all
  end
end

В этом случае потоковое вещание кажется подходящим. В типичном сценарии Rails эта страница загружается дольше, чем обычно, потому что сначала она должна извлечь все данные.

Давайте добавим потоковую передачу:

 class TimelineController
  def index
    @users = User.all
    @tickets = Ticket.all
    @attachments = Attachment.all

    render stream: true
  end
end

Метод Rails render stream: true Потоковая передача работает только с шаблонами, а не с любыми другими формами (такими как json или xml). Это добавляет умный метод, чтобы приложение расставило приоритеты шаблонов в зависимости от типа страницы и содержимого.

Передача вещи между

Потоковая передача изменяет метод рендеринга шаблона и макета. Это порождает логическую проблему: шаблоны, использующие переменные экземпляра.

Поскольку вызовы базы данных не выполнялись при визуализации шаблонов, ссылки на переменные экземпляра не будут выполнены.

Следовательно, чтобы загрузить такие атрибуты, как titlemetacontent_foryield yield

Ранее наш метод выглядел так:

 <%= yield :title %>

Теперь это будет выглядеть так:

 <%= content_for :title, "My Awesome Title" %>

Начало работы с Live API

Live Это позволяет Rails открывать и закрывать поток явным образом. Давайте создадим простое приложение и посмотрим, как его включить и получить к нему доступ извне.

Мы смотрим на концепции потокового вещания и параллелизма, и WEBrick не очень хорошо с такими вещами. В результате мы будем использовать Puma для обработки параллелизма и потоков в нашем приложении.

Добавить Puma в Gemfile и связать.

 gem "puma"
 :~/testapp$ bundle install

Puma хорошо интегрируется с Rails, поэтому, как только вы запустите `rails s` (требуется перезапуск сервера, если вы уже его запускаете), Puma загружается с того же номера порта, что и WEBrick.

 :~/testapp$ rails s
=> Booting Puma
=> Rails 4.0.0 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
Puma 2.3.0 starting...
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://0.0.0.0:3000

Давайте быстро сгенерируем контроллер для отправки сообщений.

 :~/testapp$ rails g controller messaging

Также добавьте базовый метод для потоковой передачи сообщений на стандартный выход.

 class MessagingController < ApplicationController
  include ActionController::Live

  def send_message
    response.headers['Content-Type'] = 'text/event-stream'
    10.times {
      response.stream.write "This is a test Messagen"
      sleep 1
    }
    response.stream.close
  end
end

и маршрут вways.rb

 get 'messaging' => 'messaging#send_message'

Мы можем получить доступ к этому методу через curl следующим образом:

 ~/testapp$ curl -i http://localhost:3000/messaging
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
Content-Type: text/event-stream
Cache-Control: no-cache
Set-Cookie: request_method=GET; path=/
X-Request-Id: 68c6b7c7-4f5f-46cc-9923-95778033eee7
X-Runtime: 0.846080
Transfer-Encoding: chunked

This is a test message
This is a test message
This is a test message
This is a test message

Когда мы вызываем метод send_message Стандартная конфигурация Puma допускает 16 одновременных потоков, что означает 16 клиентов. Конечно, это можно увеличить, но не без некоторых накладных расходов памяти.

Давайте создадим форму и посмотрим, сможем ли мы отправить некоторые данные в наше представление:

 def index
  end

  def send_message
    response.headers['Content-Type'] = 'text/event-stream'
    10.times {
      response.stream.write "#{params[:message]}n"
      sleep 1
    }
    response.stream.close
  end

Создайте форму для отправки данных в поток.

 <%= form_tag messaging_path, :method => 'get' do %>

    <%= text_field_tag :message, params[:message] %>
    <%= submit_tag "Post Message" %>

<% end %>

И маршрут не сделать звонок.

 root 'messaging#index'
  get  'messaging' => 'messaging#send_message', :as => 'messaging'

Как только вы наберете сообщение и нажмете «Опубликовать сообщение», браузер получит ответ потока в виде загружаемого текстового файла, который содержит сообщение, зарегистрированное 10 раз.

ручей

Здесь, однако, поток не знает, куда отправлять данные или в каком формате. Таким образом, он пишет в текстовый файл на сервере.

Вы также можете проверить это, отправив параметры через curl .

 :~/testapp$ curl -i http://localhost:3000/messaging?message="awesome"

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
Content-Type: text/event-stream
Cache-Control: no-cache
Set-Cookie: request_method=GET; path=/
X-Request-Id: 382bbf75-7d32-47c4-a767-576ec59cc364
X-Runtime: 0.055470
Transfer-Encoding: chunked

awesome
awesome

Серверные события (SSE)

HTML5 представил метод, называемый серверными событиями (SSE). SSE — это метод, доступный в браузере, который распознает и запускает событие всякий раз, когда сервер отправляет данные.

Вы можете использовать SSE в сочетании с Live API для обеспечения полнодуплексной связи.

По умолчанию Rails обеспечивает односторонний процесс связи, записывая поток клиенту, когда данные доступны. Однако, если мы можем добавить SSE, мы можем включить события и ответы, таким образом делая это двусторонним.

Простой SSE выглядит следующим образом:

 require 'json'

module ServerSide
  class SSE
    def initialize io
      @io = io
    end

    def write object, options = {}
      options.each do |k,v|
        @io.write "#{k}: #{v}n"
      end
      @io.write "data: #{object}nn"
    end

    def close
      @io.close
    end
  end
end

Этот модуль назначает объект потока ввода / вывода хешу и преобразует его в пару ключ-значение, чтобы его можно было легко прочитать, сохранить и отправить обратно в формате JSON.

Теперь вы можете обернуть ваш потоковый объект в класс SSE. Сначала включите ваш модуль SSE Теперь открытие и закрытие соединений обрабатываются модулем SSE Кроме того, если явно не завершено, цикл будет продолжаться бесконечно, и соединение будет открыто навсегда, поэтому мы добавляем условие ensure

 require 'server_side/sse'

class MessagingController < ApplicationController
  include ActionController::Live

  def index
  end

  def stream
    response.headers['Content-Type'] = 'text/event-stream'
    sse = ServerSide::SSE.new(response.stream)
    begin
      loop do
        sse.write({ :message => "#{params[:message]}" })
        sleep 1
      end
    rescue IOError
    ensure
      sse.close
    end
  end
end

Вот как выглядит ответ:

 :~/testapp$ curl -i http://localhost:3000/messaging?message="awesome"
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
Content-Type: text/event-stream
Cache-Control: no-cache
Set-Cookie: request_method=GET; path=/
X-Request-Id: b922a2eb-9358-429b-b1bb-015421ab8526
X-Runtime: 0.067414
Transfer-Encoding: chunked

data: {:message=>"awesome"}

data: {:message=>"awesome"}

Gotchas

Есть несколько ошибок (всегда есть …)

  1. Все потоки должны быть закрыты, иначе они будут открыты навсегда.
  2. Вы должны будете убедиться, что ваш код является потокобезопасным, так как контроллер всегда создает новый поток при вызове метода.
  3. После первой фиксации ответа заголовок нельзя переписать в writeclose

Вывод

Эту функцию многие с нетерпением ждут, поскольку она значительно повысит производительность приложений Rails (потоковая передача шаблонов), а также создаст сильную конкуренцию для node.js (Live).

Есть люди, уже сравнивающие различия, но я чувствую, что это только начало и потребуется время (читай дальше), чтобы эта функция стала зрелой. Пока это хорошее начало и интересно получить эти функции в Rails.