рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдХреНрдпрд╛ рд╣реИ?
рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ 3.2 рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рдмрд╛рдж рд╕реЗ рд░реЗрд▓ рдХреЗ рдЖрд╕рдкрд╛рд╕ рд░рд╣реА рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ
рдЯреЗрдореНрдкрд▓реЗрдЯ рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рддрдХ рд╕реАрдорд┐рдд рдереАред рд░реЗрд▓ 4 рдЕрдзрд┐рдХ рдкрд░рд┐рдкрдХреНрд╡ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╕рдордп рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХреЗ рд╕рд╛рде рдЖрдпрд╛ рдерд╛ред рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ, рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рд░реЗрд▓ рдЕрдм I / O рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЛ рдореВрд▓ рд░реВрдк рд╕реЗ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдФрд░ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╕рдордп рдореЗрдВ рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЛ рдбреЗрдЯрд╛ рднреЗрдЬрдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реИред
рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдФрд░ рд▓рд╛рдЗрд╡ рдПрдХреНрд╢рдирдХрдВрдЯреНрд░реЛрд▓рд░ рдХреЗ рдЕрдВрджрд░ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рджреЛ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдореЙрдбреНрдпреВрд▓ рд╣реИрдВред рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рд╕рдХреНрд╖рдо рд╣реИ, рдЬрдмрдХрд┐ рд▓рд╛рдЗрд╡ рдХреЛ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдирд┐рдпрдВрддреНрд░рдХ рд╕реЗ рд╕реАрдзреЗ рдЬреЛрдбрд╝рд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред
рдореБрдЦреНрдп рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдПрдкреАрдЖрдИ рдлрд╛рдЗрдмрд░ рд╡рд░реНрдЧ (рд╕рдВрд╕реНрдХрд░рдг рдорд╛рдгрд┐рдХ 1.9.2 рд╕реЗ рдЙрдкрд▓рдмреНрдз) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИред рд░реЗрд╢реЗ рдорд╛рдгрд┐рдХ рдореЗрдВ рдереНрд░реЗрдб-рдЬреИрд╕реЗ рдХрдВрд╕реАрд▓рд░ рдХреЗ рд▓рд┐рдП рдЙрдкрдХрд░рдг рдкреНрд░рджрд╛рди рдХрд░рддреЗ рд╣реИрдВред рдлрд╛рдЗрдмрд░ рдЕрдирд┐рд╡рд╛рд░реНрдп рд░реВрдк рд╕реЗ рд╕рдХреНрд░рд┐рдп рд╣реЛрдиреЗ рдХреЗ рдмрдЬрд╛рдп рдкреНрд░реЛрдЧреНрд░рд╛рдорд░ рдХреЗ рдЕрдиреБрд░реЛрдз рдкрд░ рдереНрд░реЗрдб рдХреЛ рдерд╛рдордиреЗ рдФрд░ рдлрд┐рд░ рд╕реЗ рд╢реБрд░реВ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред
рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдЯреЗрдореНрдкрд▓реЗрдЯреНрд╕
рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдПрдХ рд▓реЗрдЖрдЙрдЯ рдФрд░ рдПрдХ рдЯреЗрдореНрдкрд▓реЗрдЯ рдкреНрд░рджрд╛рди рдХрд░рдиреЗ рдХреА рд╕рд╛рдорд╛рдиреНрдп рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рдирд┐рд╖реНрдХреНрд░рд┐рдп рдХрд░ рджреЗрддреА рд╣реИред рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ, рд░реЗрд▓ рдкрд╣рд▓реЗ рдЯреЗрдореНрдкрд▓реЗрдЯ рдФрд░ рдлрд┐рд░ рд▓реЗрдЖрдЙрдЯ рдХреЛ рдкреНрд░рд╕реНрддреБрдд рдХрд░рддрд╛ рд╣реИред рдкрд╣рд▓реА рдЪреАрдЬ рдЬреЛ рд╡рд╣ рдХрд░рддрд╛ рд╣реИ рд╡рд╣
yield
рдФрд░ рдЯреЗрдореНрдкрд▓реЗрдЯ рд▓реЛрдб рдХрд░рддрд╛ рд╣реИред рдЙрд╕рдХреЗ рдмрд╛рдж, рд╕рдВрдкрддреНрддрд┐ рдФрд░ рд▓реЗрдЖрдЙрдЯ рдХрд╛ рдкреНрд░рддрд┐рдкрд╛рджрди рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред
рдПрдХ рдХрд╛рд░реНрд░рд╡рд╛рдИ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВ, рдЬреЛ рдмрд╣реБрдд рд╕рд╛рд░реЗ рдЕрдиреБрд░реЛрдз рдХрд░рддрд╛ рд╣реИ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП:
class TimelineController def index @users = User.all @tickets = Ticket.all @attachments = Attachment.all end end
рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдЕрдЪреНрдЫреА рд╣реИред рдпрд╣ рд░реЗрд▓ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рд╛рдорд╛рдиреНрдп рд╕реНрдерд┐рддрд┐ рд╣реИ, рдкреГрд╖реНрда рд╕рд╛рдорд╛рдиреНрдп рд╕реЗ рдЕрдзрд┐рдХ рд╕рдордп рддрдХ рд▓реЛрдб рд╣реЛрддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЖрдкрдХреЛ рдкрд╣рд▓реЗ рд╕рднреА рдбреЗрдЯрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред
рдЖрдЗрдП рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдЬреЛрдбрд╝реЗрдВ:
class TimelineController def index @users = User.all @tickets = Ticket.all @attachments = Attachment.all render stream: true end end
render stream: true
рдореЗрдердб рдЖрд▓рд╕реА рд╕рднреА рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЛ рд▓реЛрдб рдХрд░реЗрдЧрд╛ рдФрд░ рдПрд╕реЗрдЯреНрд╕ рдФрд░ рд▓реЗрдЖрдЙрдЯ рд░реЗрдВрдбрд░ рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж рдЙрдиреНрд╣реЗрдВ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрдЧрд╛ред рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдЯреЗрдореНрдкрд▓реЗрдЯреНрд╕ рдФрд░ рдХреЗрд╡рд▓ рдЙрдирдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ (рд▓реЗрдХрд┐рди json рдпрд╛ xml рдХреЗ рд╕рд╛рде рдирд╣реАрдВ)ред рдпрд╣ рдкреГрд╖реНрда рдХреЗ рдкреНрд░рдХрд╛рд░ рдФрд░ рдЗрд╕рдХреА рд╕рд╛рдордЧреНрд░реА рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдЯреЗрдореНрдкрд▓реЗрдЯреНрд╕ рдХреЛ рдкреНрд░рд╛рдердорд┐рдХрддрд╛ рджреЗрдиреЗ рдХрд╛ рдПрдХ рдЕрдЪреНрдЫрд╛ рддрд░реАрдХрд╛ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИред
рдЕрдВрджрд░ рдХреБрдЫ рдЬреЛрдбрд╝реЗрдВ
рд╕реНрдЯреНрд░реАрдо рдХрд░рдиреЗ рд╕реЗ рдЯреЗрдореНрдкрд▓реЗрдЯ рдФрд░ рд▓реЗрдЖрдЙрдЯ рдХреЗ рддрд░реАрдХреЗ рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрди рд╣реЛрддрд╛ рд╣реИ, рдЬреЛ рддрд╛рд░реНрдХрд┐рдХ рдкреНрд░рд╢реНрди рдХреА рдУрд░ рдЬрд╛рддрд╛ рд╣реИ: рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП рдЯреЗрдореНрдкреНрд▓реЗрдЯ рдореЗрдВ рд╡реЗрд░рд┐рдПрдмрд▓ рдХреЗ рдЙрдкрдпреЛрдЧ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХреНрдпрд╛?
рджрд░рдЕрд╕рд▓, рдЪреВрдВрдХрд┐ рдбреЗрдЯрд╛рдмреЗрд╕ рдиреЗ рдЯреЗрдореНрдкреНрд▓реЗрдЯ рд░реЗрдВрдбрд░ рдХрд░рдиреЗ рдХреЗ рд╕рдордп рдЬрд╡рд╛рдм рдЕрднреА рддрдХ рдирд╣реАрдВ рд▓реМрдЯрд╛рдпрд╛ рд╣реИ, рдЙрджрд╛рд╣рд░рдг рдЪрд░ рддрдХ рдкрд╣реБрдВрдЪрдиреЗ рдХреЗ рдкреНрд░рдпрд╛рд╕ рдореЗрдВ рддреНрд░реБрдЯрд┐ рд╣реЛрдЧреАред
рдЗрд╕рд▓рд┐рдП, рд╢реАрд░реНрд╖рдХ рдФрд░ рдореЗрдЯрд╛ рдЬреИрд╕реА рд╡рд┐рд╢реЗрд╖рддрд╛рдУрдВ рдХреЛ рд▓реЛрдб рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ рд╕рд╛рдорд╛рдиреНрдп
yield
рдмрдЬрд╛рдп
content_for
рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рд╣рд╛рд▓рд╛рдВрдХрд┐,
yield
рдЕрднреА рднреА рд╢рд░реАрд░ рдХреЗ рд▓рд┐рдП рдХрд╛рдо рдХрд░реЗрдЧреАред
рдкрд╣рд▓реЗ, рд╣рдорд╛рд░реА рд╡рд┐рдзрд┐ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддреА рдереА:
<%= yield :title %>
рдЕрдм рдпрд╣ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦреЗрдЧрд╛:
<%= content_for :title, "My Awesome Title" %>
рд▓рд╛рдЗрд╡ рдПрдкреАрдЖрдИ рдХреЗ рд╕рд╛рде рд▓рд╛рдЗрд╡ рд╣реЛ рд░рд╣реА рд╣реИ
рд▓рд╛рдЗрд╡ рдПрдХреНрд╢рдирдХрдВрдЯреНрд░реЛрд▓рд░ рдореЗрдВ рд╢рд╛рдорд┐рд▓ рдПрдХ рд╡рд┐рд╢реЗрд╖ рдореЙрдбреНрдпреВрд▓ рд╣реИред рдпрд╣ рд░реЗрд▓ рдХреЛ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдЦреЛрд▓рдиреЗ рдФрд░ рдмрдВрдж рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред рдЖрдЗрдП рдПрдХ рд╕рд░рд▓ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд▓рд┐рдЦреЗрдВ рдФрд░ рджреЗрдЦреЗрдВ рдХрд┐ рдпрд╣ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ рдФрд░ рдмрд╛рд╣рд░ рд╕реЗ рд╕реНрдЯреНрд░реАрдо рдХреИрд╕реЗ рдкрд╣реБрдВрдЪрддрд╛ рд╣реИред
рдЪреВрдБрдХрд┐ рд╣рдо рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдФрд░ рдХреЙрдиреНрд╕реЗрдкреНрдЯ рдХреЗ рд╕рдВрджрд░реНрдн рдореЗрдВ рдХрд╛рдо рдХрд░рддреЗ рд╣реИрдВ, WEBrick рдпрд╣рд╛рдБ рд╣рдорд╛рд░рд╛ рдорд┐рддреНрд░ рдирд╣реАрдВ рд╣реИред рд╣рдо рдкреНрдпреВрдорд╛ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗ рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдЬрд╛рдирддрд╛ рд╣реИ рдХрд┐ рдХреИрд╕реЗ рдзрд╛рд░рд╛рдУрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдирд╛ рд╣реИред
Gemfile рдореЗрдВ рдПрдХ рдкреНрдпреВрдорд╛ рдЬреЛрдбрд╝реЗрдВ рдФрд░ рдмрдВрдбрд▓ рдЪрд▓рд╛рдПрдВред
gem "puma"
:~/testapp$ bundle install
рдкреНрдпреВрдорд╛ рд░реЗрд▓ рдХреЗ рд╕рд╛рде рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рдПрдХреАрдХреГрдд рдХрд░рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдпрджрд┐ рдЖрдк рдЕрдм
rails s
рдЪрд▓рд╛рддреЗ рд╣реИрдВ, рддреЛ рдкреНрдпреВрдорд╛ рд╡реЗрдмрдмреНрд░рд┐рдХ рдХреЗ рд╕рдорд╛рди рдкреЛрд░реНрдЯ рдкрд░ рд╢реБрд░реВ рд╣реЛрдЧрд╛ред
:~/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 Message\n" sleep 1 } response.stream.close end end
рдорд╛рд░реНрдЧреЛрдВ рдореЗрдВ рдПрдХ рдорд╛рд░реНрдЧ рдЬреЛрдбрд╝реЗрдВред
get 'messaging' => 'messaging#send_message'
рдЕрдм рд╣рдо рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП рдХрд░реНрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╕реНрдЯреНрд░реАрдо рддрдХ рдкрд╣реБрдВрдЪ рд╕рдХрддреЗ рд╣реИрдВ:
:~/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
рд╡рд┐рдзрд┐
send_message
, рддреЛ рдкреНрдпреВрдорд╛ рдПрдХ рдирдИ рд╕реНрдЯреНрд░реАрдо рдмрдирд╛рддрд╛ рд╣реИ рдЬрд┐рд╕рдореЗрдВ рдпрд╣ рдПрдХ рд╡реНрдпрдХреНрддрд┐рдЧрдд рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЗ рд▓рд┐рдП рдбреЗрдЯрд╛ рд╕реНрдЯреНрд░реАрдо рдХрд░рддрд╛ рд╣реИред рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ, рдкреНрдпреВрдорд╛ рдХреЛ 16 рд╕рдорд╛рдирд╛рдВрддрд░ рдереНрд░реЗрдб рддрдХ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдЬрд┐рд╕рдХрд╛ рдЕрд░реНрде рд╣реИ 16 рдПрдХ рд╕рд╛рде рдЬреБрдбрд╝реЗ рдХреНрд▓рд╛рдЗрдВрдЯред рдмреЗрд╢рдХ, рдЗрд╕ рд╕рдВрдЦреНрдпрд╛ рдХреЛ рдмрдврд╝рд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдореЗрдореЛрд░реА рдХреА рдЦрдкрдд рдХреЛ рдереЛрдбрд╝рд╛ рдмрдврд╝рд╛ рджреЗрдЧрд╛ред
рдЖрдЗрдП рдПрдХ рдлрд╝реЙрд░реНрдо рдмрдирд╛рдПрдВ рдФрд░ рджреЗрдЦреЗрдВ рдХрд┐ рдХреНрдпрд╛ рд╣рдо рдХрд┐рд╕реА рдбреЗрдЯрд╛ рдХреЛ рджреГрд╢реНрдп рдореЗрдВ рднреЗрдЬ рд╕рдХрддреЗ рд╣реИрдВ:
def send_message response.headers['Content-Type'] = 'text/event-stream' 10.times { response.stream.write "
рд╣рдо рдПрдХ рд╕реНрдЯреНрд░реАрдо рдореЗрдВ рдбреЗрдЯрд╛ рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдлреЙрд░реНрдо рдмрдирд╛рддреЗ рд╣реИрдВ:
<%= 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'
рдЬреИрд╕реЗ рд╣реА рдЖрдк рдПрдХ рд╕рдВрджреЗрд╢ рджрд░реНрдЬ рдХрд░рддреЗ рд╣реИрдВ рдФрд░ "рдкреЛрд╕реНрдЯ рд╕рдВрджреЗрд╢" рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░рддреЗ рд╣реИрдВ, рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдХреЛ рдПрдХ рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рдиреЗ рдпреЛрдЧреНрдп рдкрд╛рда рдлрд╝рд╛рдЗрд▓ рдХреЗ рд░реВрдк рдореЗрдВ рдПрдХ рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдорд┐рд▓реЗрдЧреА рдЬрд┐рд╕рдореЗрдВ рдЖрдкрдХрд╛ рд╕рдВрджреЗрд╢ 10 рдмрд╛рд░ рд▓реЙрдЧ рдЗрди рд╣реЛрддрд╛ рд╣реИред

рдпрд╣рд╛рдВ, рд╣рд╛рд▓рд╛рдВрдХрд┐, рд╕реНрдЯреНрд░реАрдо рдХреЛ рдпрд╣ рдирд╣реАрдВ рдкрддрд╛ рд╣реИ рдХрд┐ рдбреЗрдЯрд╛ рдХрд╣рд╛рдВ рдФрд░ рдХрд┐рд╕ рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рднреЗрдЬрд╛ рдЬрд╛рдП, рдЗрд╕рд▓рд┐рдП рд╡рд╣ рдЗрд╕реЗ рд╕рд░реНрд╡рд░ рдкрд░ рдПрдХ рдЯреЗрдХреНрд╕реНрдЯ рдлрд╛рдЗрд▓ рдкрд░ рд▓рд┐рдЦрддрд╛ рд╣реИред
рдЖрдк рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рдкрд╛рд░ рдХрд░рдХреЗ рдХрд░реНрд▓ рдХреЗ рд╕рд╛рде рдСрдкрд░реЗрд╢рди рдХрд╛ рдкрд░реАрдХреНрд╖рдг рднреА рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:
:~/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
рд╕рд░реНрд╡рд░ рд╕рд╛рдЗрдб рдЗрд╡реЗрдВрдЯ
HTML5 рд╕рд░реНрд╡рд░ рд╕рд╛рдЗрдб рдЗрд╡реЗрдВрдЯреНрд╕ (SSE) рдирд╛рдордХ рдПрдХ рд╡рд┐рдзрд┐ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИред SSE рдПрдХ рдмреНрд░рд╛рдЙрдЬрд╝рд░-рдЖрдзрд╛рд░рд┐рдд рд╡рд┐рдзрд┐ рд╣реИ рдЬреЛ рд╕рд░реНрд╡рд░ рджреНрд╡рд╛рд░рд╛ рдбреЗрдЯрд╛ рднреЗрдЬрдиреЗ рдкрд░ рд╣рд░ рдмрд╛рд░ рдШрдЯрдирд╛рдУрдВ рдХреЛ рдкрд╣рдЪрд╛рдирддрд╛ рд╣реИ рдФрд░ рдЪрд▓рд╛рддрд╛ рд╣реИред
рд╣рдо рд╕рд░реНрд╡рд░ рдФрд░ рдХреНрд▓рд╛рдЗрдВрдЯ рдХреЗ рдмреАрдЪ рджреЛ-рддрд░рдлрд╝рд╛ рд╕рдВрдЪрд╛рд░ рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП Live API рдХреЗ рд╕рд╛рде рд╕рдВрдпреЛрдЬрди рдореЗрдВ SSE рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред
рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ, рд░реЗрд▓ рдХреЗрд╡рд▓ рдПрдХрддрд░рдлрд╛ рд╕рдВрдЪрд╛рд░ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИ - рдпрд╣ рдЖрдкрдХреЛ рдЧреНрд░рд╛рд╣рдХ рдХреЗ рд▓рд┐рдП рдбреЗрдЯрд╛ рдЙрдкрд▓рдмреНрдз рдХрд░рд╛рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИ рдЬреИрд╕реЗ рд╣реА рдпрд╣ рдЙрдкрд▓рдмреНрдз рд╣реЛ рдЬрд╛рддрд╛ рд╣реИред рд╣рд╛рд▓рд╛рдБрдХрд┐, рдпрджрд┐ рд╣рдо 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 "
рдпрд╣ рдореЙрдбреНрдпреВрд▓ рдПрдХ рд╣реИрд╢ рдореЗрдВ рдПрдХ I / O рд╕реНрдЯреНрд░реАрдо рдСрдмреНрдЬреЗрдХреНрдЯ рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рдПрдХ рдХреБрдВрдЬреА-рдореВрд▓реНрдп рдЬреЛрдбрд╝реА рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд░рддрд╛ рд╣реИ рддрд╛рдХрд┐ рдЗрд╕реЗ JSON рдкреНрд░рд╛рд░реВрдк рдореЗрдВ рдЖрд╕рд╛рдиреА рд╕реЗ рдкрдврд╝рд╛, рд╕рдВрдЧреНрд░рд╣реАрдд рдФрд░ рднреЗрдЬрд╛ рдЬрд╛ рд╕рдХреЗред
рдЕрдм рд╣рдо рдЕрдкрдиреА рд╕реНрдЯреНрд░реАрдо рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЛ SSE рдореЗрдВ рд▓рдкреЗрдЯ рд╕рдХрддреЗ рд╣реИрдВред рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, SSE рдореЙрдбреНрдпреВрд▓ рдХреЛ рдХрдВрдЯреНрд░реЛрд▓рд░ рд╕реЗ рдХрдиреЗрдХреНрдЯ рдХрд░реЗрдВред рдЕрдм рдПрд╕рдПрд╕рдИ рдореЙрдбреНрдпреВрд▓ рджреНрд╡рд╛рд░рд╛ рд╕реНрдЯреНрд░реАрдо рдХреЗ рдЙрджреНрдШрд╛рдЯрди рдФрд░ рд╕рдорд╛рдкрди рдХреЛ рд╡рд┐рдирд┐рдпрдорд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдпрджрд┐ рдЖрдк рдЗрд╕реЗ рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рд╕рдорд╛рдкреНрдд рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдЪрдХреНрд░ рдЕрдирд┐рд╢реНрдЪрд┐рдд рдХрд╛рд▓ рддрдХ рджреЛрд╣рд░рд╛рдПрдЧрд╛ рдФрд░ рдХрдиреЗрдХреНрд╢рди рд╣рдореЗрд╢рд╛ рдЦреБрд▓рд╛ рд░рд╣реЗрдЧрд╛, рдЗрд╕рд▓рд┐рдП рд╣рдо рдпрд╣
ensure
рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ
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 => "
рдпрд╣ рдХреЛрдб рдЗрд╕ рддрд░рд╣ рд╕реЗ рдПрдХ рдЬрд╡рд╛рдм рджреЗрдЧрд╛:
:~/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"}
рдиреБрдХрд╕рд╛рди
рд╕рд╛рд╡рдзрд╛рди рд░рд╣реЗрдВ, рдХреБрдЫ рдиреБрдХрд╕рд╛рди рд╣реИрдВ (рдЬрд╣рд╛рдВ рддрдм рдЙрдирдХреЗ рдмрд┐рдирд╛):
- рд╕рднреА рд╕реНрдЯреНрд░реАрдо рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рдмрдВрдж рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдП, рдЕрдиреНрдпрдерд╛ рд╡реЗ рд╣рдореЗрд╢рд╛ рдЦреБрд▓реА рд░рд╣реЗрдВрдЧреАред
- рдЖрдкрдХреЛ рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП рдХрд┐ рдЖрдкрдХрд╛ рдХреЛрдб рдереНрд░реЗрдб рд╕реБрд░рдХреНрд╖рд┐рдд рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╡рд┐рдзрд┐ рд╢реБрд░реВ рд╣реЛрдиреЗ рдкрд░ рдирд┐рдпрдВрддреНрд░рдХ рд╣рдореЗрд╢рд╛ рдПрдХ рдирдпрд╛ рдереНрд░реЗрдб рдмрдирд╛рддрд╛ рд╣реИред
- рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХреЗ рдкрд╣рд▓реЗ рднрд╛рдЧ рдХреЗ рдмрд╛рдж, рд╢реАрд░реНрд╖рдХреЛрдВ рдХреЛ
write
рдпрд╛ close
рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдирд╣реАрдВ рдмрджрд▓рд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ close
рдирд┐рд╖реНрдХрд░реНрд╖
рдпрд╣ рдПрдХ рдРрд╕рд╛ рдЕрд╡рд╕рд░ рд╣реИ рдЬреЛ рдХрдИ рд▓реЛрдЧ рд▓рдВрдмреЗ рд╕рдордп рд╕реЗ рд░реЗрд▓ рдХреА рддрд▓рд╛рд╢ рдореЗрдВ рд╣реИрдВ, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдкреНрд░рджрд░реНрд╢рди (рд╕реНрдЯреНрд░реАрдорд┐рдВрдЧ рдЯреЗрдореНрдкреНрд▓реЗрдЯ) рдХреЛ рдХрд╛рдлреА рдмрдврд╝рд╛ рд╕рдХрддрд╛ рд╣реИ рдФрд░ рдиреЛрдб.рдЬреЗрдПрд╕ (рд▓рд╛рдЗрд╡) рдХреЗ рд╕рд╛рде рдЧрдВрднреАрд░рддрд╛ рд╕реЗ рдкреНрд░рддрд┐рд╕реНрдкрд░реНрдзрд╛ рдХрд░ рд╕рдХрддрд╛ рд╣реИред
рдХреБрдЫ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдмреЗрдВрдЪрдорд╛рд░реНрдХ рдХрд╛ рд╕рдВрдЪрд╛рд▓рди рдХрд░ рд░рд╣реЗ рд╣реИрдВ, рддреБрд▓рдирд╛ рдХрд░ рд░рд╣реЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдХреЗрд╡рд▓ рд╢реБрд░реБрдЖрдд рд╣реИ рдФрд░ рдЗрд╕ рд╕реБрд╡рд┐рдзрд╛ рдХреЛ рд╕рдорд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╕рдордп (рдХреБрдЫ рд░рд┐рд▓реАрдЬ рдкрдврд╝рдирд╛) рдкрд╛рд╕ рдХрд░рдирд╛ рд╣реЛрдЧрд╛, рдЗрд╕рд▓рд┐рдП рдмреЛрд▓рдирд╛ рд╣реИред рдЕрдм рдпрд╣ рдПрдХ рдЕрдЪреНрдЫреА рд╢реБрд░реБрдЖрдд рд╣реИ рдФрд░ рдХрд╛рд░реНрд░рд╡рд╛рдИ рдореЗрдВ рд╕рдм рдХреБрдЫ рдХрд░рдиреЗ рдХрд╛ рдПрдХ рд░реЛрдорд╛рдВрдЪрдХ рдЕрд╡рд╕рд░ рд╣реИред
рдкреБрдирд╢реНрдЪ: рдпрд╣ рдореЗрд░рд╛ рдкрд╣рд▓рд╛ рдЕрдиреБрд╡рд╛рдж рдЕрдиреБрднрд╡ рд╣реИ, рдореИрдВ рдкреАрдПрдо рдХреА рдЯрд┐рдкреНрдкрдгрд┐рдпреЛрдВ рдХреЗ рд▓рд┐рдП рдмрд╣реБрдд рдЖрднрд╛рд░реА рд░рд╣реВрдВрдЧрд╛ред рдЖрдкрдХрд╛ рдзрдиреНрдпрд╡рд╛рдж
рдореВрд▓
рдпрд╣рд╛рдБ рд╣реИ ред