Skip to content

Instantly share code, notes, and snippets.

@jkutner
Created March 30, 2012 14:57
Show Gist options
  • Save jkutner/2252100 to your computer and use it in GitHub Desktop.
Save jkutner/2252100 to your computer and use it in GitHub Desktop.

Two Applications, One Trinidad Server

One of the advantages of running Ruby on the JVM is that we can deploy multiple applications to the same webserver. Using one JRuby webserver means that there is only one process to manage, monitor, start and stop. Your sysadmins will thank you.

But having mutliple application on one virtual machine also means we can configure them to share resources, thus reducing the overhead required for a production server. In this post, we'll walk through an example of deploying two applications to one Trinidad server.

Trinidad is a light-weight JRuby web server that runs Rails and Rack applications in an embedded Apache Tomcat container. Let's install it by running this command:

$ gem install trinidad -v 1.3.4

Next, let's create a Trinidad home directory. The examples in this post will use /opt/trinidad, but you can use whatever you'd like.

Under the /opt/trinidad directory, we'll create two more directories called thing1 and thing2, which will contain the applications we're going to run on our single Trinidad server. In the thing1 directory, create a config.ru file and put this code in it:

require 'rubygems'
require 'sinatra'

get '/' do
  "I'm thing one!"
end

run Sinatra::Application

In the thing2 directory, create another config.ru file and put this code in it:

require 'rubygems'
require 'sinatra'

get '/' do
  "I'm thing two!"
end

run Sinatra::Application

Next, we'll create a /opt/trinidad/trinidad.yml file, which will be used to configure our Trinidad server.

web_apps:
  default:                                  # use the "/" context
    web_app_dir: '/opt/trinidad/thing1'
  thing2:                                   # use the "/thing2" context
    web_app_dir: '/opt/trinidad/thing2'

Our Trinidad home directory should look like this:

/opt/trinidad/
|-- trinidad.yml/
|-- thing1/
    `-- config.ru/
`-- thing2/
    `-- config.ru/

Before we start the server, let's make sure Sinatra is installed by running this command:

$ gem install sinatra

Now we can run our Trinidad server by executing this command:

$ trinidad --config /opt/trinidad/trinidad.yml
Mar 29, 2012 11:46:52 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-3000"]
Mar 29, 2012 11:46:52 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Tomcat
Mar 29, 2012 11:46:52 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.23
2012-03-30 04:46:52 INFO: No global web.xml found
2012-03-30 04:46:53 INFO: jruby 1.6.7 (ruby-1.8.7-p357) (2012-02-22 3e82bc8) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_29) [darwin-x86_64-java]
2012-03-30 04:46:56 INFO: The start() method was called on component [Realm[Simple]] after start() had already been called. The second call will be ignored.
2012-03-30 04:46:56 INFO: jruby 1.6.7 (ruby-1.8.7-p357) (2012-02-22 3e82bc8) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_29) [darwin-x86_64-java]
2012-03-30 04:46:58 INFO: Starting ProtocolHandler ["http-bio-3000"]

As the server starts up, we'll see that its instatiated two runtimes -- one for each of our applications. We can see each of them by browsing to http://localhost:3000 and http://localhost:3000/thing2.

The two applications are completely isolated. That means if you monkey-patch the String class in one application, it won't affect the other application. If you set a global variable to a constant value in one application, you can set it to a different value in the other application.

Now let's move on and really take advantage of what we've created!

Because these applications are running in the same JVM, they can share a Database Connection Pool. To do this, we'll need to use the trinidad_dbpool_extension. Trinidad provides an extension mechanism that allows us to plug-in many kinds of features.They are particularly useful when we need to hook into the embedded Tomcat container, as our database connection pool will.

To use the trinidad_dbpool_extension, we'll need to add an extensions: entry to our trinidad.yml file. The new entry will contain the configuration for the Database Connection Pool. The entire file should look like this now:

web_apps:
  default:
    web_app_dir: '/opt/trinidad/thing1'
  thing2:
    web_app_dir: '/opt/trinidad/thing2'
extensions:
  postgresql_dbpool:                                   
    jndi: 'jdbc/trinidad'                           
    username: 'postgres'                              
    password: 'Passw0rd'                              
    url: 'jdbc:postgresql://localhost:5432/trinidad'  

The extension creates the database connection pool inside the Tomcat container and gives it a JNDI name. JNDI is a registry service for resources inside of a JVM.

You'll have to use a real database for this to work, but you don't have to use PostgreSQL. The extension also supports MySQL, MSSQL, Oracle, and a generic adapter that covers most other JDBC implementations.

Next, let's use the pool in our applications. Change the thing1/config.ru file to look like this:

require 'rubygems'
require 'sinatra'
require 'active_record'

get '/' do
  ActiveRecord::Base.establish_connection(
    :adapter => "jdbcpostgresql",
    :jndi => "java:/comp/env/jdbc/trinidad"
  )

  r = ActiveRecord::Base.connection.execute(
    "select count(*) from pg_catalog.pg_tablespace")

  "Thing one found: #{r.inspect}"
end

run Sinatra::Application

First, we're loading the active_record Gem, which we'll use to interface with our database. Next, we've added two statements to our get service. The first statement establishes a connection from the pool by referencing the JNDI resource we definined earlier. The second line executes a simple query against PostgreSQL's internal tables. Finally, we're returning the result of the query as the service response.

Next, modify thing2/config.ru so it looks similar to the code above, but with "Thing two" in the response.

Before we can launch these applications, we'll need to install a few more gems by running these commands:

$ gem install trinidad_postgresql_dbpool_extension 
$ gem install activerecord-jdbcpostgresql-adapter

Now kill the Trinidad server if it's still running by pressing Ctrl+C from it's console, and start it up again by running this command once more:

$ trinidad --config /opt/trinidad/trinidad.yml

When we point our browser to http://localhost:3000 and http://localhost:3000/thing2 we'll see something like this (depending on the number of tables in your database):

Thing one found: [{"count" => 0}]

Both applications are connecting to the database!

Sharing a database connection pool simplifies our production architecture by eliminating the need for additional layers like pg_pool. Trinidad makes it very easy to configure, but this same kind of setup can be achieved with any JRuby web server -- including TorqueBox and Warbler+Tomcat/Jetty/etc.

If you found this useful, I encourage you to pick up a copy of my book, Deploying with JRuby, which has tons of other JRuby examples like this one.

The complete source for this example can be found here: https://github.com/jkutner/trinidad-dbpool-example

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment