Date post: | 19-Jan-2015 |
Category: |
Documents |
Upload: | jim-crossley |
View: | 4,536 times |
Download: | 2 times |
MagicRubyFeb 2011
Crank Up Your Apps With
Creative Commons BY-SA 3.0
Jim Crossley
1
Jim who?
• “recovering java architect”
• java since ’96, ruby since ’06
• emacs and guitars since forever
• Red Hat Senior Engineer
• proud member of
2
3
Bob McWhirter
4
Agenda• High-level BS and Development
Process
• Web, Messaging, Scheduling, Services, Clustering
5
TorqueBoxthe power of JBoss with the expressiveness of Ruby
6
TorqueBox: what?
• A “real” app server for Ruby
• Founded in 2008
• Bob’s “labor of love”
• 100% open-source, LGPL license
• Reflects a strong commitment to Ruby from Red Hat
7
TorqueBox: why?
• “Native” support for Rack apps
• Built-in background processing
• Built-in scheduling
• Built-in clustering
• Easily scalable
• Optionally enterprisey
8
JRubya good idea done well
9
JRuby: why?
• Very fast runtime
• Real threads
• Java libraries
• Java tools
• Healthy community
10
JRuby: why not?
• Slower start up
• Native gems
• FFI
• C extension support
• Some Ruby libs not thread-safe
11
JBoss ASbut before we get to that...
12
Enterprise Javamisconceptions
13
Scary?Is it
14
Acronymious?
AWT
EAR
EJBJAR
JAX-RPC
JAX-RS
JAX-WS
JAXB
JAXPJCK
JCP
JDBC JDK
JDO
JDOMJEE
JLSJME
JMM
JMS
JMXJNDI
JNIJNLP JPA
JPQL
JREJSE
JSF
JSP JSR
JSTLJTA
JVM
JTS JWS
NPE
NIO
SAF POJO
WAR SLSB
15
Bloated?
16
Bewildering?
17
Enterprise Java is all thatand more! but...
18
I promise...
• No XML
• No Java *
• No war files *
• Only Ruby, YAML and...
* Unless you really want to
19
JBoss ASthe enterprisey good parts
20
JBoss AS6
• Tomcat for web
• Infinispan for caching
• HornetQ for messaging
• Quartz for scheduling
• mod_cluster for clustering
21
AS = Application Server
• Not just “web server + interpreter”
• More like initd than httpd
• Can host multiple, disparate apps simultaneously
• Provides basic services to all the apps it hosts
22
Hot Deployment
• anything added to deploy/ will get deployed
• anything removed from deploy/ will get undeployed
• anything updated in deploy/ will get redeployed
• TorqueBox deployers make JBoss grok YAML and ruby archives
$JBOSS_HOME/server/default/deploy/
23
Hot Deployment
• anything added to deploy/ will get deployed
• anything removed from deploy/ will get undeployed
• anything updated in deploy/ will get redeployed
• TorqueBox deployers make JBoss grok YAML and ruby archives
$JBOSS_HOME/server/default/deploy/
deployment descriptors
24
Hot Deployment
• anything added to deploy/ will get deployed
• anything removed from deploy/ will get undeployed
• anything updated in deploy/ will get redeployed
• TorqueBox deployers make JBoss grok YAML and ruby archives
$JBOSS_HOME/server/default/deploy/
zip files (archives)
25
Development Processhow do they work?
26
easy install
$ wget http://torquebox.org/torquebox-dev.zip$ unzip torquebox-dev.zip
$ export TORQUEBOX_HOME=$PWD/torquebox-1*$ export JBOSS_HOME=$TORQUEBOX_HOME/jboss$ export JRUBY_HOME=$TORQUEBOX_HOME/jruby
$ export PATH=$JRUBY_HOME/bin:$PATH
27
easy install
$ wget http://torquebox.org/torquebox-dev.zip$ unzip torquebox-dev.zip
$ export TORQUEBOX_HOME=$PWD/torquebox-1*$ export JBOSS_HOME=$TORQUEBOX_HOME/jboss$ export JRUBY_HOME=$TORQUEBOX_HOME/jruby
$ export PATH=$JRUBY_HOME/bin:$PATH
28
easy install
$ wget http://torquebox.org/torquebox-dev.zip$ unzip torquebox-dev.zip
$ export TORQUEBOX_HOME=$PWD/torquebox-1*$ export JBOSS_HOME=$TORQUEBOX_HOME/jboss$ export JRUBY_HOME=$TORQUEBOX_HOME/jruby
$ export PATH=$JRUBY_HOME/bin:$PATH
29
easy install
$ wget http://torquebox.org/torquebox-dev.zip$ unzip torquebox-dev.zip
$ export TORQUEBOX_HOME=$PWD/torquebox-1*$ export JBOSS_HOME=$TORQUEBOX_HOME/jboss$ export JRUBY_HOME=$TORQUEBOX_HOME/jruby
$ export PATH=$JRUBY_HOME/bin:$PATH
Make sure the jruby found in your path is in $JRUBY_HOME/bin.
30
easy install
$ jruby -S gem install bundler$ jruby -S gem install rails$ jruby -S gem install sinatra
31
Rakefile
rake tasks
require "org.torquebox.rake-support"
32
rake tasks
rake torquebox:run Run TorqueBox server
rake torquebox:deploy[context_path] Deploy the app in the current directory
rake torquebox:undeploy Undeploy the app in the current directory
33
rake tasks
Start torquebox:run in its own shell and leave it running. Instead of script/server or shotgun or thin or whatever else, use torquebox:deploy.
34
deployment descriptors
torquebox:deploy creates a deployment descriptor in the JBoss deploy/ directory
35
deploy/myapp-rails,rack.yml
deployment descriptors
application: root: /path/to/myapp env: developmentweb: context: myapp host: www.yourhost.com static: public rackup: config.ruenvironment: MAIL_HOST: mail.yourhost.com REPLY_TO: [email protected]
36
deploy/myapp-rails,rack.yml
deployment descriptors
application: root: /path/to/myapp env: developmentweb: context: myapp host: www.yourhost.com static: public rackup: config.ruenvironment: MAIL_HOST: mail.yourhost.com REPLY_TO: [email protected]
The fully-qualified path to the app.
This will be the value of either
RAILS_ROOT or RACK_ROOT
37
deploy/myapp-rails,rack.yml
deployment descriptors
application: root: /path/to/myapp env: developmentweb: context: myapp host: www.yourhost.com static: public rackup: config.ruenvironment: MAIL_HOST: mail.yourhost.com REPLY_TO: [email protected]
The runtime mode of the app. This will be either RAILS_ENV
or RACK_ENV
38
deploy/myapp-rails,rack.yml
deployment descriptors
application: root: /path/to/myapp env: developmentweb: context: myapp host: www.yourhost.com static: public rackup: config.ruenvironment: MAIL_HOST: mail.yourhost.com REPLY_TO: [email protected]
The app’s context path (or “sub URI”):
http://localhost:8080/myappCan be set via rake:
rake torquebox:deploy[myapp]The default is root:
http://localhost:8080/
39
deploy/myapp-rails,rack.yml
deployment descriptors
application: root: /path/to/myapp env: developmentweb: context: myapp host: www.yourhost.com static: public rackup: config.ruenvironment: MAIL_HOST: mail.yourhost.com REPLY_TO: [email protected]
A list of virtual hostnames to which
to bind the app.
40
deploy/myapp-rails,rack.yml
deployment descriptors
application: root: /path/to/myapp env: developmentweb: context: myapp host: www.yourhost.com static: public rackup: config.ruenvironment: MAIL_HOST: mail.yourhost.com REPLY_TO: [email protected]
The location of the app’s static
content, either absolute or relative to the app’s root.
41
deploy/myapp-rails,rack.yml
deployment descriptors
application: root: /path/to/myapp env: developmentweb: context: myapp host: www.yourhost.com static: public rackup: config.ruenvironment: MAIL_HOST: mail.yourhost.com REPLY_TO: [email protected]
The name of the rackup script used to
boot your app
42
deploy/myapp-rails,rack.yml
deployment descriptors
application: root: /path/to/myapp env: developmentweb: context: myapp host: www.yourhost.com static: public rackup: config.ruenvironment: MAIL_HOST: mail.yourhost.com REPLY_TO: [email protected]
Any environment variables required
by the app.
43
deployment descriptors
• config/torquebox.yml
• internal descriptors have the same structure as the external ones in deploy/
•may be used to provide your own reasonable defaults
44
Webmake rack, not war
45
jruby-rack
• All rack-based frameworks supported: rails, sinatra, etc
• No packaging required: apps deploy from where they sit on disk
• No redeploy necessary to see changes when using rack reloading or rails development mode
46
Gemfile
database connectivity
gem "activerecord-jdbc-adapter"
gem "jdbc-postgres"# gem "jdbc-sqlite3"# gem "jdbc-mysql"
47
rails template
• Adds TorqueBox rake tasks
• Adds the JDBC sqlite3 gems
• Adds TorqueBox session_store
• Adds Backgroundable module
48
my first rails app
$ jruby -S rails new myapp -m \ $TORQUEBOX_HOME/share/rails/template.rb
$ cd myapp$ jruby -S bundle install
$ jruby -S rake torquebox:deploy
49
Messagingasynchronicity
50
Resque
ActiveMessaging
cron
Taskr
rufus-scheduler
ConveyorWorkerQueue
Ap4r
CronEdit
lots of options
RailsCronOpen4
Spawn
Sparrow
Kestral
RabbitMQ
ActiveMQ
Amazon SQS
beanstalkd
starling
workling
async_observer
daemons
BackgroundDRb
BackgroundJob
Background-Fu
JobFu
DelayedJob
rake
51
TorqueBox::Messaging
• JMS (Java Message Service) is an API for messaging
•HornetQ is the JBoss JMS implementation
52
TorqueBox::Messaging
•Task•Backgroundable•MessageProcessor
•Queue•Topic
53
TorqueBox::Messaging
• No extra tables in your database
• No external system to manage
• Little to no config required at all
• No redeploy necessary in dev mode
• Efficient loading of rails environment
• Automatic load balancing and retries
• Works on Windows, if you care
54
app/tasks/email_task.rb
Tasks
class EmailTask < TorqueBox::Messaging::Task def welcome(payload) person = payload[:person] person ||= Person.find_by_id(payload[:id]) if person # send the email person.welcomed = true person.save! end endend
55
app/tasks/email_task.rb
Tasks
class EmailTask < TorqueBox::Messaging::Task def welcome(payload) person = payload[:person] person ||= Person.find_by_id(payload[:id]) if person # send the email person.welcomed = true person.save! end endend
56
app/tasks/email_task.rb
Tasks
class EmailTask < TorqueBox::Messaging::Task def welcome(payload) person = payload[:person] person ||= Person.find_by_id(payload[:id]) if person # send the email person.welcomed = true person.save! end endend
57
app/tasks/email_task.rb
Tasks
class EmailTask < TorqueBox::Messaging::Task def welcome(payload) person = payload[:person] person ||= Person.find_by_id(payload[:id]) if person # send the email person.welcomed = true person.save! end endend
58
app/tasks/email_task.rb
Tasks
class EmailTask < TorqueBox::Messaging::Task def welcome(payload) person = payload[:person] person ||= Person.find_by_id(payload[:id]) if person # send the email person.welcomed = true person.save! end endend
59
app/controllers/people_controller.rb
Tasks
class PeopleController < ApplicationController def create @person = Person.new(params[:person]) respond_to do |format| if @person.save EmailTask.async(:welcome, :id => person.id) # respond appropriately end end endend
60
app/controllers/people_controller.rb
Tasks
class PeopleController < ApplicationController def create @person = Person.new(params[:person]) respond_to do |format| if @person.save EmailTask.async(:welcome, :id => person.id) # respond appropriately end end endend
61
Tasks
Call them from your controllers, models, and observers, or even other tasks. Even in non-Rails apps!
62
Backgroundable
Inspired by DelayedJob’s handle_asynchronously, it’s trivial to create implicit background Tasks.
63
lib/something.rb
Backgroundable
include TorqueBox::Messaging
class Something include Backgroundable always_background :foo def foo;; end def bar;; [email protected]@something.background.bar
64
lib/something.rb
Backgroundable
include TorqueBox::Messaging
class Something include Backgroundable always_background :foo def foo;; end def bar;; [email protected]@something.background.bar
65
lib/something.rb
Backgroundable
include TorqueBox::Messaging
class Something include Backgroundable always_background :foo def foo;; end def bar;; [email protected]@something.background.bar
66
lib/something.rb
Backgroundable
include TorqueBox::Messaging
class Something include Backgroundable always_background :foo def foo;; end def bar;; [email protected]@something.background.bar
67
app/models/slow_poke.rb
Backgroundable
class SlowPoke < ActiveRecord::Base def takes_forever;; end def might_take_awhile;; endend
SlowPoke.always_background :takes_forever
@[email protected]_take_awhile
68
Queues
Tasks are built on top of Queues. Of course, you may build your own “MOM” apps by defining your own Queues, Topics, and their message Processors yourself.
69
config/queues.yml
Queues
/queues/questions:
/queues/answers: durable: false
70
Processors
You can create a processor class to receive messages from a Topic or Queue
71
config/messaging.yml
Processors
/topics/print: PrintHandler/queues/popular: - PopularHandler - AdultObserver: filter: "age >= 18" concurrency: 5/queues/students: PrintHandler: config: color: true
72
config/messaging.yml
Processors
/topics/print: PrintHandler/queues/popular: - PopularHandler - AdultObserver: filter: "age >= 18" concurrency: 5/queues/students: PrintHandler: config: color: true
73
config/messaging.yml
Processors
/topics/print: PrintHandler/queues/popular: - PopularHandler - AdultObserver: filter: "age >= 18" concurrency: 5/queues/students: PrintHandler: config: color: true
74
config/messaging.yml
Processors
/topics/print: PrintHandler/queues/popular: - PopularHandler - AdultObserver: filter: "age >= 18" concurrency: 5/queues/students: PrintHandler: config: color: true
75
app/models/print_handler.rb
Processors
include TorqueBox::Messaging
class PrintHandler < MessageProcessor def on_message(body) puts "Processing #body of #message" end def configure(opts) @color = opts['color'] endend
76
Queues (again)
But how do you send a message?
77
example
Queues
include TorqueBoxreq = Messaging::Queue.new '/queues/questions'res = Messaging::Queue.new '/queues/answers' Thread.new do req.publish "What time is it?" puts res.receive( :timeout => 1000 )end puts req.receiveres.publish Time.now
78
example
Queues
include TorqueBoxreq = Messaging::Queue.new '/queues/questions'res = Messaging::Queue.new '/queues/answers' Thread.new do req.publish "What time is it?" puts res.receive( :timeout => 1000 )end puts req.receiveres.publish Time.now
79
example
Queues
include TorqueBoxreq = Messaging::Queue.new '/queues/questions'res = Messaging::Queue.new '/queues/answers' Thread.new do req.publish "What time is it?" puts res.receive( :timeout => 1000 )end puts req.receiveres.publish Time.now
80
example
Queues
include TorqueBoxreq = Messaging::Queue.new '/queues/questions'res = Messaging::Queue.new '/queues/answers' Thread.new do req.publish "What time is it?" puts res.receive( :timeout => 1000 )end puts req.receiveres.publish Time.now
81
“on the fly”
Queues
include TorqueBox
queue = Messaging::Queue.new '/queues/foo'queue.create ... queue.destroy
82
Topics
• behavior is different, but interface is the same.
• all subscribers of a topic see each message, but only one subscriber will see any message from a queue
• use topics.yml to define topics
• use TorqueBox::Messaging::Topic
83
Schedulingget regular later
84
app/jobs/newsletter_sender.rb
Jobs
class NewsletterSender def run() subscriptions = Subscription.find(:all) subscriptions.each do |e| send_newsletter( e ) end end end
85
config/jobs.yml
Jobs
monthly_newsletter: description: first of month job: NewsletterSender cron: ‘0 0 0 1 * ?’
sandbox: job: Sandbox cron: ‘*/5 * * * * ?’
86
Seconds Minutes Hours DOM Month DOW Year
0-59 0-59 0-23 1-31? L W
1-12JAN-DEC
1-7SUN-SAT? L #
1970-2099empty
0 */30 10-13 ? * FRI#3
“Fire every half hour from 10am until 1pm on the third Friday of each month”
Jobs
87
Jobs
• More portable. What is the first day of the week on BSD again? What’s cron on Windows?
• Self contained within the app. No external systems to manage and keep in sync.
• Full rails environment loaded and available.
88
Servicesrun along, lil’ daemon
89
Services
Long-running, non-web “daemons” that share the runtime environment and deployment lifecycle of your app.
90
Services
• Represented as a class with optional initialize(Hash), start() and stop() methods, which should each return quickly.
• Typically will start a long-running loop in a thread and respond to external events.
• Configured via services.yml
91
config/services.yml
Services
IrcBot: server: freenode.net channel: #torquebox publish: /topics/irc
MyMudServer:
SomeOtherService:
92
app/models,etc/my_service.rb
Services
class MyService def initialize opts= name = opts[:publish] @queue = Messaging::Queue.new(name) end def start Thread.new run end def stop @done = true endend
93
app/models,etc/my_service.rb
Services
class MyService def initialize opts= name = opts[:publish] @queue = Messaging::Queue.new(name) end def start Thread.new run end def stop @done = true endend
94
app/models,etc/my_service.rb
Services
class MyService def initialize opts= name = opts[:publish] @queue = Messaging::Queue.new(name) end def start Thread.new run end def stop @done = true endend
95
app/models,etc/my_service.rb
Services
class MyService def initialize opts= name = opts[:publish] @queue = Messaging::Queue.new(name) end def start Thread.new run end def stop @done = true endend
96
app/models,etc/my_service.rb
Services
class MyService def run until @done @queue.publish(Time.now) sleep(1) end endend
97
app/models,etc/my_service.rb
Services
class MyService def run until @done @queue.publish(Time.now) sleep(1) end endend
98
app/services/evented_service.rb
Services
class EventedService def initialize opts= @opts = opts end def start Thread.new EventMachine.run ... end def stop EventMachine.stop endend
99
Clusteringless failure faster
100
out-of-the-box• JBoss provides
• session replication
• load-balanced messaging
• mod_cluster provides
• session affinity
• intelligent load-balanced web
• failover
101
JBOSS_CONFTorqueBox ships with two JBoss server configurations: default (not clustered) and all (clustered). To enable clustering...
$ export JBOSS_CONF=all
102
deploy and run
$ export JBOSS_CONF=all$ jruby -S rake torquebox:deploy$ jruby -S rake torquebox:run
103
mod_cluster
A reverse proxy implemented as an Apache module with JBoss awareness. Constantly gathers load statistics and deployment availability for intelligent request distribution.
104
mod_cluster
105
mod_cluster
• Dynamic configuration
• Server-side load factor calculation
• Fine-grained web app lifecycle
• AJP (Apache JServ Protocol) is optional. HTTP[S] is also supported.
106
Futurecoming soon
107
Future• Performance benchmarking and
optimization
• Infinispan for ActiveSupport::Cache and NoSQL persistence
• JBoss SSO, HASingleton, logging
• CDI injection of Java components into Ruby
• More JavaEE bridged to Ruby where it makes sense, e.g. BPM, Drools, Transactions, etc.
108
Resources
• http://torquebox.org
• #torquebox on freenode
• https://github.com/torquebox
• http://twitter.com/torquebox
• http://projectodd.org
109
Thanks!questions?
110