Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Стриминг в Rails 4

Anna | 20.06.2014 | нет комментариев

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

Streaming и Live — два отдельных модуля, реализованных внутри ActionController’а. Streaming включен по умолчанию, в то время как Live должен быть очевидно добавлен непринужденно в контроллере.

Стержневой api стриминга использует класс Fiber (доступен с версии ruby 1.9.2). Файберы предоставляют инструментарий для потоко-сходственного параллелизма в ruby. Fiber дает вероятность потокам приостанавливаться и возобновлять работу по желанию программиста, а не быть по сути упреждающими.

Стриминг образцов

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

Разглядим action тот, что делает много запросов, скажем:

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

Способ render stream: true неохотно загрузит все запросы и даст им вероятность выполняться позже того как ассеты и лэйаут будут отрендерены. Стриминг работает с образцами и только с ними (но не с json либо xml). Это дает отличный метод передать приоритет образцам базируясь на типе страницы и её содержимом.

Добавим чего-нибудь вовнутрь

Стриминг изменяет метод ренеринга образца и лэйаута, что приводит к логичному вопросу: а как же применение инстансных переменных в образцах?

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

Следственно, Дабы загрузить такие аттрибуты как title и meta необходимо применять content_for взамен привычного yield‘а. Тем не менее, yield все еще будет трудиться для body.

Ранее наш способ выглядел как-то так:

<%= yield :title %>

Сейчас же он будет выглядеть так:

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

Становимся живее с Live API

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

Так как мы трудимся в контексте стриминга и параллелизма, WEBrick нам здесь не товарищ. Будем применять Puma потому как она может трудиться с потоками.

Добавим пуму в Gemfile и запустим bundle.

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

Добавим роут в routes.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 создает новейший поток, в котором изготавливает стриминг данных для отдельного заказчика. По умолчанию Puma сконфигурирована чтоб обрабатывать до 16 параллельных потоков, что значит 16 единовременно подключенных клентов. Безусловно же это число дозволено увеличить, но это несколько увеличит расход памяти.

Давайте-ка сделаем форму и посмотрим можем ли мы послать какие-нибудь данные на view:

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 %>

И настроим роут:

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

Как только вы введете сообщение и нажмете «Post Message», браузер получит потоковый результат в виде загружаемого текстового файла, тот, что содержит ваше сообщение, залогированное 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

События на стороне сервера (Server Side Events)

HTML5 предоставляет способ, называемый Server Side Events (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

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

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

require 'server_side/sse'

class MessagingController < ApplicationController
  include ActionController::Live

  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"}

Подводные камни

Будьте внимательны, есть пара подводных камней (куда ж без них то):

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

Завершение

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

Некоторые теснее проводят бенчмарки, сопоставляют, но я думаю что это только самое предисловие и должно пройти время (читай несколько релизов) Дабы эта фича, так сказать, дозрела. Теперь же это отличный старт и волнующая вероятность испробовать всё в деле.

PS: это мой 1-й навык перевода, буду дюже признателен за примечания в личку. Спасибо.

Оригинал здесь.

Сумеет ли Rails через n месяцев/лет стать полновесной заменой Node.js в реал-тайм приложениях?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Проголосовало 212 человек. Воздержалось 92 человека.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB