Ja
• opowiem wamo pewnym projekcie
• pisałem w PHP i już nie chcę (-:
• teraz piszę w Ruby
• i bardzo się staram, bo nie wiem kto siedzi
obok, a jak mówi chińskie przysłowie:
Zawsze pisz kod tak, jakby osoba która będzie
pracowała nad nim po tobie była psychopatycznym
mordercą, który wie gdzie mieszkasz.
3.
Był sobie projekt
•Panel administracyjny z podstawową
funkcjonalnością
• RESTful API
• Serwer WebSocket
• Aplikacja mobilna dla iOS-a
Damy radę, ale…
•Niska ilość potrzebnych mendejsów, a więc…
• szybki development, a więc…
• prosta architektura, a więc…
• jednorodne środowisko, a więc…
7.
Ein Reich Rubydla wszystkich…
• Mniej języków, mniej problemów
• Panel administracyjny - RoR
• Serwer WS - Ruby
• Interfejs MQ - Ruby
8.
Ruby on Rails
http://rubyonrails.org/
publicfunction indexAction()
{
return $this->render('default/index.html.twig');
}
def index
end
czy…
!ruby albo zdrowie, wybór należy do ciebie…
9.
RabbitMQ
• Message Broker
•po polsku: Mesedż Broker 😜
• po prostu działa ™
• Erlang, no trudno
• https://www.rabbitmq.com/
10.
EventMachine
• implementuje wzorzecreaktora
• jest lekka i szybka
• cięższe zadania -> Deferrable
• https://github.com/eventmachine/
eventmachine
11.
Bunny
• tunel doRabbitMQ
• prosty (bardzo)
• to już duży, dojrzały królik
• działa z… EM
• https://github.com/ruby-amqp/bunny
12.
EM-WebSockets
• Serwer WebSocketw Ruby
• prosty (bardzo)
• działa z… EM
• deja vu, deja vu
• https://github.com/igrigorik/em-websocket
Pętla główna
require "event_machine"
EventMachine.rundo
definition_of_some_useful_handlers
end
Disclaimer:
prezentowany kod to złożona forma cyfrowego odwzorowania ludzkiej
myśli. Jest obszerny i skomplikowany, może też być niezrozumiały.
żart, przecież to Ruby
15.
+= WebSockets
require "event_machine"
require"em-websocket"
server_config_hash = {host: "0", port: 8080}
EventMachine.run do
EM::WebSocket.run(server_config_hash) do |ws|
ws.onopen { |handshake| do_something_on_open(ws, handshake) }
ws.onmessage { |msg| do_something_else_on_message(ws, msg) }
ws.onclose { do_nothing_on_close(ws) }
ws.onerror { |error| definitely_do_nothing_on_error(error) }
end
end
16.
Identyfikacja klienta?
def on_open(ws,handshake)
if user = User.for_token( handshake.query["token"] )
connection_pool.update(ws, user.id)
else
log "Open without a valid user. Foch..."
ws.close
end
rescue => exception
log "OPEN exception: #{exception}"
end
17.
+= Bunny
require "event_machine"
require"bunny"
connection = Bunny.new
channel = connection.create_channel
queue = channel.queue("a_test_queue", durable: false)
EventMachine.run do
queue.subscribe(manual_ack: true, block: false) do
|delivery_info, properties, body|
message = JSON.parse(body) # it's smart and convenient to use JSON
do_magic_on(message) && channel.ack(delivery_info.delivery_tag)
end
end
Razem prezentuje się…
require"event_machine"
require "bunny"
require "em-websocket"
counter = 0
server_config_hash = {host: "0", port: 8080}
connection = Bunny.new
channel = connection.create_channel
queue = channel.queue("a_test_queue", durable: false)
EventMachine.run do
EM::WebSocket.run(server_config_hash) do |ws|
ws.onopen { |handshake| do_something_on_open(ws, handshake) }
ws.onmessage { |msg| do_something_else_on_message(ws, msg) }
ws.onclose { do_nothing_on_close(ws) }
ws.onerror { |error| definitely_do_nothing_on_error(error) }
end
queue.subscribe(manual_ack: true, block: false) do |delivery_info, properties, body|
message = JSON.parse(body) # it's smart and convenient to use JSON
do_magic_on(message)
channel.ack(delivery_info.delivery_tag)
end
EventMachine::PeriodicTimer.new(1) do
send_server_stats_to_manager_email
end
end
Niewiele kodu,
który sporo robi
bo Facebook
sam się nie uzupełni
21.
Tymczasem w aplikacji…
classMessage
def push_to_bunny
$rabbit_session.publish_message(self.id)
end
end
Tyle wystarczy, żeby wysłać
naszą wiadomość
do kolejki. Ruby rulez (-:
Obserwacje
• wyjątki dobrzejest obsługiwać w miejscu,
w którym się ich spodziewamy. EM
domyślnie je przechwytuje i tyle.
• nie należy serializować obiektów i
wrzucać ich do kolejek. Ale przecież nikt
tego nie robi. Robi?
24.
Obserwacje…
• Bundler.require jestpożyteczny
• Obsługę poszczególnych sub-pętli dobrze
wrzucić do dedykowanych klas. Kod staje
się podatny na testowanie.