Successfully reported this slideshow.

Scaling Rails With Torquebox Presented at JUDCon:2011 Boston

5

Share

Loading in …3
×
1 of 67
1 of 67

More Related Content

Related Books

Free with a 14 day trial from Scribd

See all

Related Audiobooks

Free with a 14 day trial from Scribd

See all

Scaling Rails With Torquebox Presented at JUDCon:2011 Boston

  1. 1. Scaling Rails Applications with TorqueBox Ben Browning
  2. 2. Ben Browning •TorqueBox Contributor •Red Hat Senior Engineer since July 2010 •Ruby yay! JVM yay! Java boo! JRuby yay!
  3. 3. The Team
  4. 4. TorqueBox •The power of JBoss with the expressiveness of Ruby •Rails, Sinatra, Rack and non-web applications running on top of JBoss AS and JRuby •Messaging, Asynchronous Tasks, Scheduled Jobs, and Services built-in
  5. 5. Rails “Ruby on Rails® is an open-source web framework that’s optimized for programmer happiness and sustainable productivity. It lets you write beautiful code by favoring convention over configuration.” - http:// rubyonrails.org
  6. 6. Scaling Ability to increase throughput by adding hardware
  7. 7. Learn By (Beer) Example Let’s create an application to find the most popular beer according to Twitter.
  8. 8. Beer Tweets •Web front-end •Twitter Streaming API client •Scheduled job to clean old beer tweets •Caching so our database doesn’t fall over
  9. 9. Web Front-end class BeersController < ApplicationController def most_popular @popular_beers = Beer.most_popular(:limit => 25) end end
  10. 10. Twitter Streaming API Client •Connect to Twitter’s streaming API and find all tweets containing “beer” •Needs to run as a daemon, outside of the web request / response cycle
  11. 11. TorqueBox Services •Long-running, non-web daemons that share the runtime environment and deployment lifecycle of your app •A class with initialize( Hash ), start(), and stop() methods
  12. 12. Twitter Streaming API Client class BeerService def initialize(options) @client = StreamingTwitter.new(options["username"], options["password"]) end def start Thread.new { find_beer } end def stop @client.disconnect end end
  13. 13. Twitter Streaming API Client class BeerService def find_beer @client.search('beer') do |tweet| Beer.create_from_json(tweet) end end end
  14. 14. Twitter Streaming API Client config/torquebox.yml: services: beer_service: username: ilikebeer password: sodoyou
  15. 15. Clean Stale Beer •Delete all beer-related tweets older than 7 days •Run once an hour to prune in manageable chunks
  16. 16. Scheduled Jobs •Tasks that need to be executed on a recurring schedule •A class with a run() method
  17. 17. Clean Stale Beer class BeerCleaner def run() Beer.clean_older_than(7.days) end end
  18. 18. Clean Stale Beer config/torquebox.yml: jobs: beer_cleaner: description: Clean stale beer-related tweets job: BeerCleaner cron: '0 44 * * * ?'
  19. 19. Caching We’re showing the same list of most popular beers to all users so why hit the database on every request?
  20. 20. Caching Web Front-end class BeersController < ApplicationController caches_action :most_popular, :expires_in => 30.seconds def most_popular @popular_beers = Beer.most_popular(:limit => 25) end end
  21. 21. Beer Tweets We’re collecting tweets about beer, serving the results up via a cached web interface, and keeping our database cleaned of stale data.
  22. 22. We’re Done, Right? We’ve added all our features and deployed on TorqueBox so we can handle limitless load, right?
  23. 23. Benchmarks
  24. 24. Benchmarks
  25. 25. We’re Done, Right? No way - it’s beer and Twitter! This will be wildly popular with social alcoholics and we need to plan accordingly - we need to scale!
  26. 26. Performance != Scalability Just because an application performs well on a single server doesn’t mean it can scale.
  27. 27. Add More Servers Well, before we can do that we need a load balancer to route requests to multiple servers.
  28. 28. mod_cluster The same mod_cluster you know and love to use with JBoss AS works with TorqueBox.
  29. 29. mod_cluster Based on Apache’s mod_proxy but with two-way communication to JBoss and TorqueBox providing intelligent request routing based on load.
  30. 30. mod_cluster
  31. 31. Add More Servers As each TorqueBox instance boots it will find the other TorqueBox and mod_cluster instances and add itself to the cluster.
  32. 32. Sessions •By default Rails stores all session data in cookies •Production Rails applications usually use a database or memcached for session storage •TorqueBox provides its own session storage
  33. 33. TorqueBox Sessions config/initializers/session_store.rb: BeerTweets::Application.config. session_store TorqueBox::Session::ServletStore
  34. 34. TorqueBox Sessions •Server-side sessions without hitting the database or requiring an external process (memcached) •Uses JBoss AS session replication under the hood
  35. 35. Are We Scaled Yet? We’ve taken care of the load balancer and session replication, but what about our Twitter Streaming API client? How will it scale?
  36. 36. Twitter Streaming API Client •By default our service will start on every node in the cluster •Sometimes that’s what you want, but not in this case; we only want one copy of each beer tweet to be saved.
  37. 37. HASingleton Services •TorqueBox supports highly-available singleton services •The service will only run on one node in the cluster •If that node goes down, another node will start the service
  38. 38. HASingleton Services config/torquebox.yml: services: beer_service: username: ilikebeer password: sodoyou singleton: true
  39. 39. Twitter Streaming API Client •Now our service will only run on one node, but what happens if tweets come in from Twitter faster than that one node can process them?
  40. 40. Backgroundable •Convert any method call into an asynchronous call •Uses HornetQ under the hood and calls are load- balanced across the cluster
  41. 41. Backgroundable class BeerService def find_beer @client.search('beer') do |tweet| Beer.background.create_from_json(tweet) end end end
  42. 42. Asynchronous Tasks class BeerTask < TorqueBox::Messaging::Task def create_from_json(payload) Beer.create_from_json(payload[:tweet]) end end
  43. 43. Asynchronous Tasks class BeerService def find_beer @client.search('beer') do |tweet| BeerTask.async(:create_from_json, :tweet => tweet) end end end
  44. 44. Twitter Streaming API Client All nodes in our cluster will now process incoming tweets even though only a single node maintains a http connection to Twitter.
  45. 45. Now We’re Scaled, Right? Not quite - we still have that pesky job that cleans stale beer to deal with.
  46. 46. Clean Stale Beer •By default, scheduled jobs will run on all nodes in a cluster. •We only want our beer cleaning job to run on a single node.
  47. 47. HASingleton Jobs config/torquebox.yml: jobs: beer_cleaner: description: Clean stale beer-related tweets job: BeerCleaner cron: '0 44 * * * ?' singleton: true
  48. 48. All Scaled? Almost! We still need to take care of our caching.
  49. 49. Caching •Rails ships with support for multiple cache stores (file- based, in-memory, memcached) •None allow multiple nodes to share a common cache without setting up an external process
  50. 50. TorqueBoxStore •Rails 3 ActiveSupport::Cache::Store implementation backed by Infinispan •Supports invalidated, replicated, and distributed clustering modes
  51. 51. TorqueBoxStore module BeerTweets class Application < Rails::Application config.cache_store :torque_box_store(:mode => :distributed, :sync => false) end end
  52. 52. Are We Scaled? As we add additional nodes to the cluster they increase our web request throughput, background job throughput, and cache capacity. We’re scaled!
  53. 53. But Wait, There’s More!
  54. 54. Injection Inject CDI resources, messaging destinations, JNDI entries, MBeans into your Ruby application @questions = inject(‘/queues/questions’) @datasource = inject(‘java:comp/env/jdbc/myDS’) @thing = inject(com.mycorp.Something) @bean = inject(‘SomeMCBean’)
  55. 55. Message Processors class SeniorHandler < TorqueBox::Messaging::MessageProcessor def on_message(body) puts “Processing #{body} of #{message}” end end
  56. 56. Message Processors config/torquebox.yml: messaging: /queues/people: SeniorHandler: filter: “age >= 55” concurrency: 2
  57. 57. Queues and Topics questions = inject(‘/queues/questions’) answers = inject(‘/queues/answers’) Thread.new do questions.publish ‘What time is it?’ puts answers.receive(:timeout => 1000) end puts questions.receive answers.publish Time.now
  58. 58. Queues and Topics config/torquebox.yml: queues: /queues/questions: /queues/answers:
  59. 59. BackStage Dashboard and RESTful API to inspect and control Ruby components
  60. 60. BackStage
  61. 61. StompBox Easy Heroku-esque git-based deployments
  62. 62. StompBox
  63. 63. Roadmap 1.0.0 Just Released! Upcoming: AS7 Transactions SSO Drools / jBPM Mobicents WebSockets
  64. 64. Resources •http://torquebox.org •https://github.com/torquebox •#torquebox on FreeNode •@torquebox
  65. 65. Thanks!
  66. 66. Questions?

Editor's Notes

  • \n
  • \n
  • \n
  • \n
  • \n
  • myths &amp;#x201C;Ruby doesn&amp;#x2019;t scale, Rails doesn&amp;#x2019;t scale&amp;#x201D;\n
  • scaling out vs scaling up\n
  • \n
  • \n
  • simple Rails controller w/ an accompanying view (not shown) to render the HTML\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • normal load balancers just forward requests to the backend server\nwith mod_cluster, TorqueBox and JBoss AS can send load and other information to the load balancer\n
  • \n
  • When using multicast discovery - there are some other tricks for networks that don&amp;#x2019;t support multicast\n
  • \n
  • \n
  • Java and Rails applications can share session data\nAS session replication is battle-proven, very configurable\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • If backgroundable is too magic for you, we also support explicit asynchronous tasks\n
  • \n
  • \n
  • \n
  • \n
  • Just like HASingleton services\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • ×