Share
Embed
Transcript
50 slides
-
What are we talking about? • Current way of building
95% of rails apps • Why is that broken? • Why should you care? • New and improved way of building connected apps -
Writing a review • Set the review language • Set
the review rank (based on some rules) • Update the user score • Was he first to write a full review? (better score) • Update the place score • Update the place score in a travel style • Update the personal place score for user graph -
The Rails Way • How would you say rails is
designed to do this? -
Yes, I am asking a question ! What would you
say is the rails way? -
Problems • Overloading the Lib Directory • Lib directory gets
to be the junk of rails apps -
Problems • Application is bloating beyond your control • Gemfile
• Initializers • Models • Super slow startup times • Super slow testing times -
Again... • Working with workers will make life a bit
better • Still lots of bloat in the lib dir • Lots of workers in the application code to load • Where the hell is my logic? • Some engineers put it in the worker, some do classes -
• Going into the better way I will try… •
Being framework Agnostic • Giving the concept, you make it great for you. • Making sure you know it’s not a one size fits all There’s a better way -
NOT • Not a single application • Not a single
logic location • Not a single test suite • Not a single Gemfile • Not a magic solution for all apps • Not something to start with right away (for a MVP) -
Publishing Events • Working with Events, not workers or classes
• Every model/class can publish any event • There’s no ACK on the other side, it’s just publishing the message to your broker (I said I will be agnostic) • The model/observer responsibility ends here -
Subscribing • On the other side, your “mini-apps” can subscribe
to those events • Events can be unique strings or topics • For example “recommendation_change” is a topic • Any app that cares about this, will register on the event -
Our Mini apps • LanguageDetectionApp • RecommendationRankingApp • PlaceScoringApp •
TravelStyleScoringApp • UserScoringSystem • GraphScoringSystem -
• Apps can be written in • Full rails apps
• Ruby • Sinatra apps • Go • Java more on apps -
more on apps • Different Gems for different apps •
Tests only run on those apps on change • Now, when you change your main app, it runs only those relevant specs • Different API’s, can even be different languages -
send.rb #!/usr/bin/env ruby # encoding: utf-8 require "bunny"
conn = Bunny.new(:automatically_recover => false) conn.start ch = conn.create_channel q = ch.queue("hello") ch.default_exchange.publish("Hello World!", :routing_key => q.name) puts " [x] Sent 'Hello World!'" conn.close -
receive.rb #!/usr/bin/env ruby # encoding: utf-8 require "bunny"
conn = Bunny.new(:automatically_recover => false) conn.start ch = conn.create_channel q = ch.queue("hello") begin puts " [*] Waiting for messages. To exit press CTRL+C" q.subscribe(:block => true) do |delivery_info, properties, body| puts " [x] Received #{body}" end rescue Interrupt => _ conn.close exit(0) end -
new_task.rb #!/usr/bin/env ruby # encoding: utf-8 require "bunny"
conn = Bunny.new(:automatically_recover => false) conn.start ch = conn.create_channel q = ch.queue("task_queue", :durable => true) msg = ARGV.empty? ? "Hello World!" : ARGV.join(" ") q.publish(msg, :persistent => true) puts " [x] Sent #{msg}" conn.close -
worker.rb #!/usr/bin/env ruby # encoding: utf-8 require "bunny"
conn = Bunny.new(:automatically_recover => false) conn.start ch = conn.create_channel q = ch.queue("task_queue", :durable => true) ch.prefetch(1) puts " [*] Waiting for messages. To exit press CTRL+C" begin q.subscribe(:manual_ack => true, :block => true) do |delivery_info, properties, body| puts " [x] Received '#{body}'" # imitate some work sleep body.count(".").to_i puts " [x] Done" ch.ack(delivery_info.delivery_tag) end rescue Interrupt => _ conn.close end -
Topics • Possibly the most powerful feature of RabbitMQ •
You can register to a topic via string/partial/regex • Exchange publishes to queues and consumers consume -
emit_log_topic.rb #!/usr/bin/env ruby # encoding: utf-8 require "bunny"
conn = Bunny.new conn.start ch = conn.create_channel x = ch.topic("topic_logs") severity = ARGV.shift || "anonymous.info" msg = ARGV.empty? ? "Hello World!" : ARGV.join(" ") x.publish(msg, :routing_key => severity) puts " [x] Sent #{severity}:#{msg}" conn.close -
receive_logs_topic.rb #!/usr/bin/env ruby # encoding: utf-8 require "bunny"
if ARGV.empty? abort "Usage: #{$0} [binding key]" end conn = Bunny.new conn.start ch = conn.create_channel x = ch.topic("topic_logs") q = ch.queue("", :exclusive => true) ARGV.each do |severity| q.bind(x, :routing_key => severity) end puts " [*] Waiting for logs. To exit press CTRL+C" begin q.subscribe(:block => true) do |delivery_info, properties, body| puts " [x] #{delivery_info.routing_key}:#{body}" end rescue Interrupt => _ ch.close conn.close end -
Receiving Logs # All Logs ruby -rubygems receive_logs_topic.rb "#"
# Logs that start with kern ruby -rubygems receive_logs_topic.rb "kern.*" # Only Critical ruby -rubygems receive_logs_topic.rb "*.critical" # Multiple ruby -rubygems receive_logs_topic.rb "kern.*" "*.critical" # Emiting kern facility log ruby -rubygems emit_log_topic.rb "kern.critical" "A critical kernel error" -
Gotchas • Configuration of apps • Redis connection • DB
connection • Mongo Connection • Memcached connection • Deployments -
• Application config can be stored in... • Chef Data
Bags (recommended) • ENV Variables (requires a bit more hassle) we work this way right now and making a move to the previous • Thanks to Avner @ Fiverr for the Chef idea Gotchas -
Advantages • Full decoupling • Throw complete chunks of code
to the trash • No more feature XXX, no problem • Adding another feature dependent on an event, No problem, Main app does not change (doesn’t even have to be redeployed) AMAZING RIGHT? -
Advantages #2 • Improved response time (any async will give
you that though) • Reduced complexity by decoupling • Build apps that use the language best suited instead of one gigantic monster app in Ruby/Rails • Deploy separately, scale separately -
Read more • Bunny for RabbitMQ • Sneakers (Awesome!!! Thanks
to @jundot) • RabbitMQ docs • http://www.rubyinside.com/why-rubyists-should-care- about-messaging-a-high-level-intro-5017.html • http://en.wikipedia.org/wiki/ Fallacies_of_Distributed_Computing • Messaging middleware used at Wikipedia