Crank Up Your Apps With TorqueBox

Post on 19-Jan-2015

4,536 views 2 download

Tags:

description

Presentation by Jim Crossley at Magic Ruby 2011

transcript

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: you@yourhost.com

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: you@yourhost.com

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: you@yourhost.com

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: you@yourhost.com

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: you@yourhost.com

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: you@yourhost.com

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: you@yourhost.com

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: you@yourhost.com

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;; endend...@something.foo@something.background.bar

64

lib/something.rb

Backgroundable

include TorqueBox::Messaging

class Something include Backgroundable always_background :foo def foo;; end def bar;; endend...@something.foo@something.background.bar

65

lib/something.rb

Backgroundable

include TorqueBox::Messaging

class Something include Backgroundable always_background :foo def foo;; end def bar;; endend...@something.foo@something.background.bar

66

lib/something.rb

Backgroundable

include TorqueBox::Messaging

class Something include Backgroundable always_background :foo def foo;; end def bar;; endend...@something.foo@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

@slowpoke.takes_forever@slowpoke.background.might_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