Complete Download Deploying with JRuby 9k Deliver Scalable Web Apps Using the JVM 1st Edition Joe Kutner PDF All Chapters
Complete Download Deploying with JRuby 9k Deliver Scalable Web Apps Using the JVM 1st Edition Joe Kutner PDF All Chapters
https://ebookultra.com
https://ebookultra.com/download/deploying-with-
jruby-9k-deliver-scalable-web-apps-using-the-
jvm-1st-edition-joe-kutner/
https://ebookultra.com/download/using-docker-developing-and-deploying-
software-with-containers-1st-edition-adrian-mouat/
ebookultra.com
https://ebookultra.com/download/developing-web-apps-with-haskell-and-
yesod-2nd-edition-michael-snoyman/
ebookultra.com
Pro Android Web Apps Develop for Android Using HTML5 CSS3
JavaScript 1st Edition Damon Oehlman
https://ebookultra.com/download/pro-android-web-apps-develop-for-
android-using-html5-css3-javascript-1st-edition-damon-oehlman/
ebookultra.com
Web Application Development with R Using Shiny Beeley
https://ebookultra.com/download/web-application-development-with-r-
using-shiny-beeley/
ebookultra.com
https://ebookultra.com/download/progressive-web-apps-1st-edition-
jason-grigsby/
ebookultra.com
https://ebookultra.com/download/angularjs-up-and-running-enhanced-
productivity-with-structured-web-apps-1st-edition-shyam-seshadri/
ebookultra.com
Server Driven Web Apps with htmx Any Language Less Code
Simpler Code 1st Edition R. Mark Volkmann
https://ebookultra.com/download/server-driven-web-apps-with-htmx-any-
language-less-code-simpler-code-1st-edition-r-mark-volkmann/
ebookultra.com
https://ebookultra.com/download/beginning-android-web-apps-
development-1st-ed-edition-jon-westfall/
ebookultra.com
Deploying with JRuby 9k Deliver Scalable Web Apps
Using the JVM 1st Edition Joe Kutner Digital Instant
Download
Author(s): Joe Kutner
ISBN(s): 9781680501698, 1680501690
Edition: 1
File Details: PDF, 6.62 MB
Year: 2016
Language: english
Early praise for Deploying with JRuby 9K
Joe has pulled together a great collection of deployment knowledge from his years
of experience building and supporting JRuby applications. He’s an expert on this
subject and Deploying with JRuby 9k is the definitive text for getting JRuby appli-
cations up and running.
➤ Charles Oliver Nutter
JRuby co-lead
Deploying with JRuby 9k answers the most frequently asked questions about real-
world use of JRuby. Whether you’re coming to JRuby from Ruby or Java, Joe fills
in all the gaps you’ll need to deploy JRuby with confidence.
➤ Tom Enebo
JRuby co-lead
I’ve been working with JRuby for years and I still learned several immediately
actionable steps to improve the performance and maintenance of real-world
JRuby apps.
➤ Matt Margolis
director, application development at Getty Images
Deploying with JRuby 9k is full of practical and actionable advice about how to
get the most benefit out of the JVM when running your Ruby app on JRuby.
➤ Chris Seaton
Oracle Labs and JRuby contributor
Deploying with JRuby 9k is the essential guide for anyone building Ruby applica-
tions on the JVM. It’s loaded with tips, tricks, and best practices that newcomers
and experts can learn from.
➤ Terence Lee
Ruby task force member at Heroku
As a developer of MRI, I get super jealous reading about the JVM ecosystem and
tooling. With this book, Joe has finally made that ecosystem approachable for
JRuby applications.
➤ Zachary Scott
Ruby-core member and maintainer of Sinatra
Deploying with JRuby 9k
Deliver Scalable Web Apps Using the JVM
Joe Kutner
Acknowledgments . . . . . . . . . . . ix
Preface . . . . . . . . . . . . . . xi
Index . . . . . . . . . . . . . . 143
Acknowledgments
Writing a book is a lot like writing code. You need to know the rules, recognize
patterns, and occasionally know when to break the rules. Both writing and
coding are crafts. And like with any craft, you improve by getting advice from
more experienced professionals and being critiqued by your peers. I’m so
fortunate to have had this kind of help.
I’m inexpressibly thankful to those who reviewed this book prior to its publi-
cation. I was humbled by the attention to detail and wise feedback they pro-
vided in making it a finished product. Thank you, Jeff Holland, Margaret Le,
Matt Margolis, Jay McGaffigan, Chris Seaton, and Tim Uckun. I consider you
all to be my friends!
I’d also like to thank the staff at the Pragmatic Bookshelf: Susannah Pfalzer,
Dave Thomas, Andy Hunt, and probably a whole bunch of other people I don’t
know about.
Above all, thank you, Brian P. Hogan, my editor. This is our fourth endeavor
together, and as usual I’ve become a better writer because of it. I look forward
to working on future projects together.
I must also thank the creators of the technologies I’ve written about. This
book would not have been possible without your hard work. Thank you,
Charles Nutter, Thomas Enebo, Karol Buček, Christian Meier, Chris Seaton,
and the rest of the JRuby team. You’re the most amazing group in all of the
open source world. I owe you all my deepest gratitude and a free beverage.
Finally, I’d like to thank my wife and son. I could not have completed this
project without your love and support.
The background jobs are run with Resque,1 the scheduled jobs are run with
2
cron, and the long-running jobs use Ruby daemons, which are monitored by
monit because they tend to crash.3 It’s going to take some time to figure out
which component is the culprit because you have no centralized management
interface. Standing up a new server will take almost as long because the
infrastructure is so complex. But the website has to get back online if you’re
going to stay in business.
The problem I’ve just described is all too common. It has happened to everyone
from small startups to large companies that use Rails to serve millions of
requests. Their infrastructure is complex, and the myriad components are
difficult to manage because they’re heterogeneous and decentralized in nature.
Even worse, Rubyists have become comfortable with this way of doing things,
and some may think it’s the only way of doing things. But that’s not the case.
The recent growth and increased adoption of the Java Virtual Machine (JVM)
as a platform for Ruby applications has opened many new doors. Deployment
strategies that weren’t possible with MRI Ruby are now an option because of
the JVM’s built-in management tools and support for native operating system
threads. Ruby programmers can leverage these features by deploying their
applications on JRuby.
It’s common for Ruby programmers to think that JRuby deployment will look
identical to deployment with MRI Ruby (that is, running lots of JVM processes
1. https://github.com/resque/resque
2. http://daemons.rubyforge.org/
3. http://mmonit.com/monit/
In this book, you’ll explore the most popular and well-supported methods for
deploying JRuby. You have a surprising amount of flexibility in the processes
and platforms to choose from, which allows Ruby and Java programmers to
tailor their deployments to suit many different environments.
You may want to include some Java code in your application. Or you may
want to make calls to some Java libraries. That’s entirely your choice. If you
want to write your programs exclusively in Ruby and deploy them on the Java
Virtual Machine—as so many of us do—then go ahead.
There are many reasons to deploy Ruby applications on the JVM, and using
Java libraries and APIs is just one of them. In this book, you’ll learn how to
get the most out of the JVM without writing any Java code.
The application’s name is Twitalytics, and it’s a rich Twitter client. (As you
probably know, Twitter is a social networking website that’s used to post
short status updates, called tweets.) Twitalytics tracks an organization’s
tweets, annotates them, and performs analytic computations against data
captured in those tweets to discover trends and make predictions. But it can’t
handle its current load.
4. http://vimeo.com/27494052
Twitalytics has several background jobs that are used to stream tweets into
the application, perform analytics, and clean up the database as it grows. In
addition, it receives a large volume of HTTP requests for traditional web traffic.
But doing this on MRI means running everything in separate processes, which
consumes more resources than its infrastructure can handle.
You’ll begin working on the app in Chapter 1, Getting Started with JRuby, on
page 1. You’ll learn what makes JRuby a better deployment platform and
why it’s a good fit for this application. Then you’ll extract a microservice from
the Twitalytics monolith, port it to JRuby, and package it into an archive file
with the Warbler gem. But before you can deploy it, you’ll need to create an
environment where it can run.
Once you’ve completed the production server setup, you’ll be ready to deploy.
You’ll learn how JRuby deployment differs from the more common practice
of traditional Ruby application deployment and how containerization technolo-
gies like Docker can simplify the process. In additional to using Docker, you’ll
deploy to the cloud on the Heroku platform as a service.
The Warbler gem gives you a quick way to get started with JRuby. But it’s
just a stepping-stone on your path to better performance. As the book pro-
gresses, you’ll improve your deployment strategy by running Twitalytics on
some other JRuby web servers.
But you still won’t be making the most of what the JVM has to offer. To do
that, you’ll need a new kind of platform.
In Chapter 5, Deploying JRuby in the Enterprise, on page 73, you’ll learn about
a Ruby application server. You’ll use TorqueBox, a server based on the popular
JBoss application server, to run Twitalytics. This kind of deployment is unique
when compared to traditional Ruby deployments because it provides a com-
plete environment to run any kind of program, not just a web application.
You’ll learn how this eliminates the need for external processes. In the end,
you’ll have the most advanced deployment environment available to any Ruby
application.
Twitalytics is a Rails application, and you’ll use this to your advantage as you
deploy it. But all of the server technologies you’ll use work equally well with
When you encounter Rails-specific features in this book, be aware that this
is only for demonstration purposes and not because the frameworks being
used work exclusively with Rails. Rails works with these servers because it’s
Rack based.
You’re not required to have any experience with JRuby. This book is written
from the perspective of someone who is familiar with MRI-based Ruby
deployments but wants a modern deployment strategy for their applications.
Some of the concepts we’ll discuss may be more familiar to programmers with
Java backgrounds, but it’s not required that you have any experience with
Java or its associated technologies.
5. http://www.sinatrarb.com/
6. http://en.wikipedia.org/wiki/DevOps
Conventions
The examples in this book can be run on Linux, Mac, Windows, and many
other operating systems. But some small changes to the command-line
statements may be required for certain platforms.
We’ll use notation from bash, which is the default shell on Mac OS X and many
Linux distributions. The $ prompt will be used for all command-line examples.
Windows command prompts typically use something like C:\> instead, so when
you see a command like this
$ bundle install
you’ll know not to type the dollar sign and to read it like this:
C:\> bundle install
Most commands will be compatible between Windows and bash systems (such
as cd and mkdir). In the cases where they’re not compatible, the appropriate
commands for both systems will be spelled out. One case in particular is the
rm command, which will look like this:
$ rm temp.txt
$ rm -rf tmp/
Another Unix notation that’s used in this book is the ~ (tilde) to represent a
user’s home directory. When you see a command like this
$ cd ~/code/twitalytics
Other than these minor notation changes, the examples in this book are
compatible with Windows by virtue of the Java Virtual Machine.
file and put it in your home directory. This will create a code directory and
inside that will be a twitalytics directory, which contains the baseline code for
the application (in other words, the MRI-based code).
But you’re not quite ready to run this code with JRuby. It needs to be ported
first. You’ll learn how to do that in the coming chapters.
Online Resources
Several online resources can help if you’re having trouble setting up your
environment or running any of the examples in this book.
For Java-related problems, the Java Community has forums7 and numerous
Java-related articles.
For JRuby-related problems, the official JRuby website8 has links to several
community outlets. The most useful of these are the mailing list9 and the
#jruby IRC channel on FreeNode.10
7. https://community.oracle.com/community/java
8. http://jruby.org/community
9. https://github.com/jruby/jruby/wiki/MailingLists
10. http://freenode.net/
11. http://torquebox.org/community/mailing_lists/
12. http://torquebox.org/documentation/
Warbler is a gem used to package source code into an archive file you can
deploy without the need for complicated configuration management scripts.
This makes the process more flexible, portable, and faster.
Your time constraints make Warbler a great solution. It won’t maximize your
use of the JVM, but it will allow you to take advantage of the most important
parts. You’ll be able to service all of your site’s web requests from a single
process without changing much code. The drawback is that you’ll have to
1. https://github.com/jruby/warbler
HTTP
Request
Apache/Nginx
MRI MRI
App App
2. http://httpd.apache.org/
There are many problems with this kind of architecture, and those problems
have been realized by Twitter, GitHub, and countless others. They include
the following:
Stuck processes Sometimes the processes will get into a stuck state and
need to be killed by an external tool like god or Monit.
Memory growth Each of the processes keeps its own copy of an application,
along with Rails and any supporting gems, in memory. Each new instance
means you’ll also need more memory for the server.
JRuby enables a similar model to the one used by MRI but with only one JVM
process. Inside this JVM process is a single application instance capable of
handling all of a website’s traffic. This works by allowing the platform to create
many threads that run against the same application instance in parallel. You
can create far more JVM threads than MRI processes because they’re much
lighter in weight. This model is illustrated in the figure on page 4.
Apache is included in the architecture diagram, but its role for a single
instance is greatly reduced. It may be used to serve up static content and
load balance a distributed cluster, but it won’t need to distribute requests
across multiple processes on a single machine.
In the coming chapters, you’ll build an architecture like the one just described
with each of the technologies you use. You’ll start by using Warbler to package
a simple Rack application, which will get you up and running quickly. But
first, you’ll need to install JRuby and a few other pieces of software.
3. https://www.elastic.co/products/logstash
4. http://dev.venntro.com/2013/07/euruko-2013-summary/
HTTP
Request
Apache/Nginx
JVM
Thread
App Thread
Thread
Thread
Installing Java
On Debian-based Linux platforms such as Ubuntu, the JVM can be installed
with APT, like this:
$ sudo apt-get install openjdk-8-jdk
On Fedora, Oracle Linux, and Red Hat, the JVM is installed with the yum
command, like this:
$ su -c "yum install java-1.8.0-openjdk"
For Mac OS X and Windows systems, you can download the latest version of
Java 8 directly from Oracle’s website.5
For Windows systems, you’ll need to set the JAVA_HOME variable. (The exact
path may vary.)
C:\> set JAVA_HOME="C:\Program Files\Java\jdk1.8.0_72"
In all cases, you can check that the JVM was installed correctly by running
this command:
$ java -version
java version "1.8.0_72"
Java(TM) SE Runtime Environment (build 1.8.0_72-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.72-b15, mixed mode)
Now that the JVM is ready, you can put JRuby on your machine.
The leading backslash in the command disables any aliases you may have
set in your shell and runs the curl binary directly.
You’ll also need to reload your shell. The most dependable way to do this is
by closing your terminal and opening a new one. In the new terminal, use
RVM to install JRuby with this command:
5. http://www.oracle.com/technetwork/java/javase/downloads/index.html
Set JRuby as the default Ruby on your platform by running this command:
$ rvm --default use jruby-9.0.5.0
Using /Users/jkutner/.rvm/gems/jruby-9.0.5.0
On Windows, you can install JRuby by downloading and running the Windows
installer from the official JRuby website.6
If you’re using any other kind of system or if you prefer not to use RVM, then
JRuby can be installed manually with these three steps:
You can check that JRuby was installed correctly with this command:
$ ruby -v
jruby 9.0.5.0 (2.2.3) 2016-01-26 7bee00d Java HotSpot(TM) 64-Bit
Server VM 25.72-b15 on 1.8.0_72-b15 [darwin-x86_64]
Without RVM, you’ll have to modify the commands used in this book. RVM
lets you invoke JRuby without the jruby or jgem commands, so you’ll need to
change all ruby commands in this book to jruby commands. You’ll also need to
6. http://jruby.org/files/downloads/9.0.5.0/index.html
7. http://jruby.org/files/downloads/9.0.5.0/index.html
prefix other commands (such as bundle, gem, and rails) with the jruby -S prefix,
like this:
$ jruby -S bundle install
Of course, before you can run bundle install, you’ll need to install Bundler. If
you’re using RVM, run this command:
$ gem install bundler -v 1.11.2
You will never be asked to run any of the examples in this book with MRI
Ruby. Remember, when you see the ruby, gem, rake, or similar commands,
you’re expected to run them with JRuby.
Installing Git
Git is a source control management tool that allows you to track versions of
your code. You’ll use Git to switch between different versions of Twitalytics
as you deploy it to new platforms. Follow the instructions for downloading
and installing Git from the official website.8
It’s OK to use some other form of version control if you’d prefer, but the
examples in this book will be specific to Git. Most of the examples will even
work without version control software, but that’s not recommended. The
source code for each branch you’ll create is available from http://pragprog.com/
titles/jkdepj2/source_code, so instead of switching branches, you can change to the
directory that corresponds to the chapter you’re reading. If you don’t use Git,
some of the Heroku examples later in the book won’t work.
Now that your software dependencies are installed, let’s move on and run
some actual code.
Introducing Warbler
Warbler is a gem that creates a web application archive (WAR) file from a
Rails- or Rack-based application.
A WAR file is a zip file that follows a few conventions. Warbler takes care of
packaging an application according to these conventions, so all you need to
do is run the Warbler commands.
8. http://git-scm.com/download
Joe asks:
What’s in a WAR File?
A WAR file is a special case of Java archive (JAR) file; both are really just zip files.
But a WAR file is structured according to a standard that’s recognized by all Java
web servers. You can take a closer look at this by extracting the WAR file you created
in this chapter with any unzipping tool. Inside it, you’ll find these essential components
(among many other things):
twitalytics.war
|-- index.html
|-- META-INF/
`-- MANIFEST.MF
`-- WEB-INF/
|-- lib/
`-- web.xml
Inside the WEB-INF directory is the web.xml file, which is the most important part of the
archive. It contains a description of how the components in the web application are
put together at runtime. It’s similar to the config/application.rb, config/environment.rb, and
config/routes.rb files of a Rails application all combined into a single descriptor. Fortu-
nately, Warbler handles the creation of this file for you based on the settings in the
config/warbler.rb file.
You can digitally sign a WAR file, which creates a checksum for each file contained
in the archive. This is used by a web server to ensure that no one has tampered with
it or that it has not been corrupted in some way. If the checksums don’t match, then
the server won’t load the files.
The WAR file that Warbler creates will be completely self-contained and ready
to be deployed to a Java web server. Warbler bundles JRuby, your web
framework, a web server, and all of the dependencies needed to adapt a Ruby
web application to the JVM.
To demonstrate Warbler, you’ll create the simplest web application you can.
Create a directory called myapp, and in that directory create a config.ru file. Then
put the following code into it:
Warbler/myapp/config.ru
Install the Warbler gem to your JRuby gem path by running this command:
$ gem install warbler -v 2.0.1
Fetching: rubyzip-1.2.0.gem (100%)
Successfully installed rubyzip-1.2.0
Fetching: jruby-rack-1.1.20.gem (100%)
Successfully installed jruby-rack-1.1.20
Fetching: jruby-jars-9.0.5.0.gem (100%)
Successfully installed jruby-jars-9.0.5.0
Fetching: warbler-2.0.1.gem (100%)
Successfully installed warbler-2.0.1
4 gems installed
Warbler has two JRuby-specific dependencies. The jruby-jars gem includes the
core JRuby code and standard library files. This allows other gems to depend
on JRuby without freezing to a specific version. The other dependency, the
jruby-rack gem, is responsible for adapting the Java web server specification to
the Rack specification.
Next, use the warble command to create the archive file. Run it with the war
option from the same directory as the config.ru file you created earlier.
$ warble war
This creates a WAR file capable of running without the need for a freestanding
Java web server like Tomcat. You can run the WAR file with this command:
$ java -jar myapp.war
That’s all you need to know to get started with Warbler. Now let’s make some
adjustments to the Twitalytics application. It wasn’t built to run on JRuby,
so it has some code that’s specific to MRI. You’re going to fix these parts so
they work on the new platform.
This directory contains the code for a small pure-Ruby HTTP service. The
service accepts a POST request with some text. It searches the text for the
names of publicly traded companies and then annotates the text with current
stock price quotes for those companies. Open the config.ru file and you’ll see
the handler:
stock-service/config.ru
post '/stockify' do
text = request.body.read.to_s
stocks = Stocks.parse_for_stocks(text)
quotes = Stocks.get_quotes(stocks)
new_text = Stocks.sub_quotes(text, quotes)
end
The first line in the handler for the /stockify route captures the body of the
request. The second line passes the text to the parse_for_stocks function, which
returns a list of symbols matching any company names mentioned in the
text. The third line uses the get_quotes function to retrieve current prices for
the stocks from a Yahoo! API. The last line combines it all by adding the
markup to the text.
Before making any changes, initialize a Git repository and create a branch
by running these commands:
$ git init
$ git add -A
$ git commit -m "initial commit"
$ git checkout -b warbler
Switched to a new branch 'warbler'
Now you can safely configure Warbler while preserving your master branch.
The first step in porting this service to JRuby is adding Warbler to the appli-
cation’s dependencies. Open the Gemfile and put this code at the end of it:
Warbler/stock-service/Gemfile
group :development do
gem 'warbler', '2.0.1'
end
You’re ready to package the app into an executable WAR file with Warbler.
Since you don’t want to type the executable directive every time you package
the app, you’ll begin by adding a Warbler configuration file. Create a config/war-
ble.rb file by running this command:
$ bin/warble config
The new file contains a wealth of instructions and examples for the different
configuration options, which are helpful to read because you never know
what you’ll want to change. Don’t worry about preserving its contents. You
can always re-create it by running warble config again. Given that safety net,
replace the entire contents of the config/warble.rb file with this code:
Warbler/stock-service/config/warble.rb
Warbler::Config.new do |config|
config.features = %w(executable)
config.jar_name = "stock-service"
end
Now when you run the warble command, it will detect this configuration and
generate an executable WAR file even when you omit the executable directive
from the command line. Give it a try:
$ bin/warble war
This generates a stock-service.war file, which you can execute by running this
command:
$ java -jar stock-service.war
With the Java process running, test out the service by opening another termi-
nal window and executing this command:
The server responds with an marked-up version of the original text containing
current stock price information. Because it depends on an external API, the
service does a lot of waiting. This causes the threads that are handling
incoming HTTP requests to be blocked. It looks like the following figure.
Block wait
Now imagine a request thread being freed up to handle other requests instead
of blocking for a single request to finish. It looks like the following figure.
That’s called asynchronous request processing, and it can dramatically
improve throughput in an I/O-constrained application (such as an app that
relies heavily on a database or external service).
Async wait
The JVM supports asynchronous I/O in several forms. For this microservice,
you’ll use an asynchronous context, which is a standard feature of Java, with
a background thread to free up your request thread. First, enable the asyn-
chronous capabilities of the web server by adding this line to the config block
in your config/warble.rb file:
Warbler/stock-service/config/warble.rb
config.webxml.servlet_filter_async = true
Then, add these two lines of code to the beginning of the POST handler:
Warbler/stock-service/config.ru
response.headers["Transfer-Encoding"] = "chunked"
async = env['java.servlet_request'].start_async
The first line sets a standard HTTP header that will ensure the client’s request
is kept open while the app does its asynchronous processing. The second line
creates a new asynchronous context. Now wrap the original four lines of the
POST handler in a Thread like this:
Warbler/stock-service/config.ru
text = request.body.read.to_s
Thread.new do
begin
puts "Thread(async): #{Thread.current.object_id}"
stocks = Stocks.parse_for_stocks(text)
quotes = Stocks.get_quotes(stocks)
new_text = Stocks.sub_quotes(text, quotes)
async.response.output_stream.println(new_text)
ensure
async.complete
end
end
The new Thread will allow the processing to happen in the background so the
POST handler can return. And instead of the handler simply returning some
string, it will write the output to the asynchronous context. You’ll also add a
puts statement that logs the ID of the request thread. Add this line to the end
of the POST handler (outside the Thread body).
Warbler/stock-service/config.ru
And invoke the service with the same curl command as before:
$ curl -d "Text about Apple, a computer company" http://localhost:8080/stockify
The output’s the same, but in the logs you’ll see the different thread identifiers:
Thread(main) : 2332
Thread(async): 2330
Keep in mind that puts is not atomic, so you might get a bit of interweaving
in the output.
This is great, but there’s still a problem with the code. The number of threads
this service can create is unbounded, which could overrun your system. To
make things worse, creating a new thread is an expensive operation. You can
fix both of these issues by using a thread pool executor. This is a great
example of a kind of concurrency issue you must consider when using JRuby.
You can add a thread pool to the application with only a few lines. First, add
a dependency on the concurrent-ruby gem to the Gemfile by adding this code to it:
Warbler/stock-service-thread-pool/Gemfile
Now modify the config.ru file to use the new gem by creating a thread pool.
Immediately after the end of the App class, add this line of code:
Warbler/stock-service-thread-pool/config.ru
App.set :thread_pool,
Concurrent::ThreadPoolExecutor.new(max_threads: 100)
This uses the ThreadPoolExecutor class to create a cached thread pool and adds
it as a setting on the App class. A cached thread pool will grow organically and
reuse threads as needed. It also prevents thread starvation by setting an
upper bound on the number of threads with the max_thread option.
You can use the thread pool by replacing the Thread.new invocation in the POST
handler with a call to settings.thread_pool.post, as shown here:
Warbler/stock-service-thread-pool/config.ru
settings.thread_pool.post do
begin
puts "Thread(async): #{Thread.current.object_id}"
stocks = Stocks.parse_for_stocks(text)
quotes = Stocks.get_quotes(stocks)
Now run Bundler again, repackage with Warbler, run the app, and make the
curl request a few more times. In the logs, you’ll see that the same thread is
being used for the asynchronous part of the service each time it’s invoked.
Thread(main) : 2332
Thread(async): 2330
Thread(main) : 2334
Thread(async): 2330
Thread(main) : 2336
Thread(async): 2330
In practice, you could make this service even more reactive by using an
asynchronous HTTP client to invoke the Yahoo! service. And if the parse_for_stocks
is going to be expensive or invoke an external service, you could put it in its
own thread. Steps like these further eliminate bottlenecks in the system,
increasing the potential throughput. But they’re possible only with a truly
concurrent platform such as JRuby. You’ll learn to implement some of these
ideas later in the book.
Before moving on, commit your changes to the warbler branch with the git add
and git commit commands:
$ git add Gemfile Gemfile.lock config config.ru
$ git commit -m "Updated for JRuby"
Wrapping Up
You packaged a microservice into an archive file. That’s a huge step for this
application because it means you can deploy it to any environment that has
a JVM available. There are many possibilities, including containers that run
in the cloud, containers that run on embedded devices, and containers that
run on a dedicated server.
You also learned how the JVM can simplify a Ruby architecture no matter
what JRuby web framework you use. This will be important as you work your
way through the book and as you continue to develop new applications on
your own.
Joe asks:
What Is Truffle?
If you follow the JRuby project on Twitter or read the JRuby mailing list, you may
have heard about a project called Truffle.
In early 2014, Truffle was open sourced and integrated into the larger JRuby project.
The Truffle developers and JRuby developers have been working alongside each other,
sharing code, and even sharing a mailing list for a while now. They’re not so much
competitors as they are contemporaries.
Truffle has the potential to achieve peak performance well beyond what’s possible
with standard JRuby, but it’s not production ready. Major components such as
OpenSSL and networking are yet to be completed. It also requires an experimental
JVM (Graal) and doesn’t work with a standard JVM.
You can learn more about Truffle from the project’s official website,c which is hosted
by its lead developer.
a. http://labs.oracle.com/
b. http://openjdk.java.net/projects/graal/
c. http://chrisseaton.com/rubytruffle/
Having a JRuby application packaged into a WAR file is a good first step, but
you still need to deploy it and consume it. In the coming chapters, you’ll learn
how to get this WAR file into production and how to use JRuby to invoke the
services it exposes. But first, you need to create a production environment
in which it can run.
Installing Docker
Deployment is the process of taking code or binaries from one environment
and moving them to a another environment where you execute them. In the
case of a web app case, you’ll move code from your development machine to
a production server. You’ve already configured a development environment,
but you still need to create a production environment you can use as the
target of your deployments. For this, you’ll use Docker,1 which reduces the
process of provisioning a production environment to just a few steps.
1. http://docker.com/
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL
default * virtualbox Running tcp://192.168.99.100:2376
On Mac only, you must set a few environment variables so the Docker client
can communicate with the Docker Machine. Run this command:
$ eval "$(docker-machine env default)"
2. https://www.virtualbox.org
3. https://www.docker.com/toolbox
4. http://docs.docker.com/mac/step_one/
On Windows, those environment variables are set for you when you run the
Desktop app. On Mac, if you don’t want to run that command every time you
open a new terminal, then run this command to add the environment variables
to your profile:
$ docker-machine env default >> ~/.profile
Now you’re ready to use the Docker client. You can move on to Getting Started
with Docker, on page 20.
And you can check your kernel version with this command:
$ uname -r
3.13.0-57-generic
If the output of either uname command doesn’t match the requirements, then
you’ll need to run Docker on a virtualization layer by installing Docker
Machine.5 Otherwise, you can install Docker natively. To begin the native
installation, update your package manager by running this command:
$ sudo apt-get update
Then install the generic Linux kernel image. This kernel has the advanced
multi-layered unification filesystem (AUFS) built in, which is required to run
Docker.
$ sudo apt-get install linux-image-generic-lts-trusty
After your computer has restarted, you can install Docker with this command:
$ curl -sSL https://get.docker.com/ | sh
5. http://docs.docker.com/machine/install-machine/
Now check that Docker can communicate with Docker Machine by running
this command:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED ...
This is an empty list, but you’d see a list of running Docker processes (known
as containers) if there were any. A container represents some isolated process
or processes that are running within the Docker context. They’re isolated
from the rest of the processes on your machine and even from the other
Docker containers. This has many of the same benefits as traditional visual-
ization but with a very different underlying implementation.
Hypervisor
Host OS
With Docker, each container runs natively on the host operating system via
the Docker Engine. They’re isolated from each other, but they still rely on the
host operating system to schedule processes, allocate memory, and do other
things that are common across containers. This model is shown in the figure
on page 21.
Docker Engine
Host OS
Docker
Container
Docker Docker
Image Container
Docker
Container
You can list the Docker images available on your machine by running this
command:
$ docker images
REPOSITORY TAG IMAGE ID CREATED ...
The command doesn’t list any images because you haven’t created any since
installing Docker a moment ago. Go ahead and download your first image.
You’ll begin with the heroku/jvm image, which mirrors the cloud environment
You now have a local representation of the Heroku stack. This stack includes
a JVM, so it’s perfect for running an executable WAR file. You can inspect
the JVM version by running the following command:
$ docker run -t heroku/jvm java -version
openjdk version "1.8.0_51-cedar14"
OpenJDK Runtime Environment (build 1.8.0_51-cedar14-b16)
OpenJDK 64-Bit Server VM (build 25.51-b03, mixed mode)
Notice that the version of the Java runtime is different from your local Java
installation. The docker run command runs another command inside the context
of a Docker container. The -t option defines the image to load, which in this
case is the heroku/jvm image you downloaded a moment ago. The last part, java
-version, is the command that Docker will run. When Docker ran the java com-
mand, it didn’t use the Java runtime on your host system. It used the Java
runtime contained in the heroku/jvm image.
To use this Java runtime with a Warbler WAR file, you must add the WAR
artifact to the container. For this, you’ll create your own Docker image.
6. https://heroku.com
To begin, move into the root directory of the stock-service application you
implemented in Chapter 1, Getting Started with JRuby, on page 1.
$ cd ~/code/stock-service
Then create a Dockerfile in that directory and put the following code in it:
Warbler/stock-service-docker/Dockerfile
FROM heroku/jvm
This defines an image that inherits from the heroku/jvm image. It then tells
Docker to add the WAR file from the local machine to the /app/user directory
in the image. This directory is what Heroku considers the root of any Docker-
based application.
This creates a new image named stock-service, which is ready to run your
application. Execute the images command again to see it in the list:
$ docker images
REPOSITORY TAG IMAGE ID CREATED
heroku/jvm latest 35ecdbd5516b 6 days ago
stock-service latest 103d45860c83 26 minutes ago
This command tells Docker to run a container with the java -jar stock-service.war
command, based on the stock-service image, and with port 8080 published to
the host system (so you can access it outside the container). To view the
application, you’ll need to know the hostname of the container. On Linux this
is localhost, but on Mac and Windows it’s the address of the Docker Machine.
Open another terminal and run this command to see it:
$ docker-machine ip default
192.168.99.100
Now you can use curl to make a request to the service just as you did when it
was running locally if you’re on Linux, or with the following command on Mac
and Windows:
$ curl -d "Hi Apple" http://$(docker-machine ip default):8080/stockify
Hi <div class='stock' data-symbol='AAPL' data-day-high='116.14'>Apple Inc.</div>
Your microservice is ready for production! Deploying your Docker image could
involve a docker push command, which uploads the image to a Docker host and
runs it. However, managing your own Docker infrastructure in production
defeats much of the purpose of Docker, which abstracts away the underlying
platform. For that reason, you’ll deploy the stock-service Docker image to a
mature and well-curated platform, Heroku.
But even without deploying to production, the work you’ve done to set up
Docker is still invaluable. It gives you the ability to run a production environ-
ment locally. And in the coming chapters you’ll enhance this environment to
include a database and other services. Having a complete production environ-
ment you can run with a single command is great for debugging, on-boarding
new employees, and scaling. Even better, because the Docker environment
you created is based on the Heroku stack, you can deploy the WAR to Heroku
with a great deal of confidence.
First, create a free Heroku account by visiting the Heroku website7 and filling
out a few bits of information. You won’t even need a credit card for now.
Once you’ve created a Heroku account, download and install the Heroku
toolbelt.8 This is a command-line interface (CLI) used to create, manage, and
deploy your Heroku apps. You can do most of these things from the web-
based dashboard,9 but you’ll need to use the CLI to deploy your app.
With the toolbelt installed, open a terminal and log in with the credentials
you created earlier:
$ heroku login
Enter your Heroku credentials.
Email: [email protected]
Password:
Authenticating is required to allow both the heroku and git commands to work
with the deployment examples in this book.
Note that if you’re behind a firewall that requires the use of a proxy to connect
with external HTTP/HTTPS services, you should set the HTTP_PROXY or
HTTPS_PROXY environment variable in your local development environment
before running the heroku command.
Once you’re logged in, install the heroku-deploy toolbelt plugin by running
this command:
$ heroku plugins:install https://github.com/heroku/heroku-deploy
This plugin helps you deploy WAR and JAR files from the Heroku CLI. But
first you’ll need an app to deploy to.
Make sure you’re still in the root directory of the stock-service app, and run
the following command to provision a new Heroku app and associate it with
your local app:
$ heroku create
Creating app... done, stack is cedar-14
https://obscure-fjord-4138.herokuapp.com/ | https://git.heroku.com/...
Heroku will randomly assign your app a unique name based on a clever mash-
up of terms. The example used here is obscure-fjord-4138, yours will be different.
Now you need to create one new file that tells Heroku how to run your app.
Create a Procfile in the root directory of the project and put this code in it:
7. http://signup.heroku.com
8. http://toolbelt.heroku.com
9. http://dashboard.heroku.com
Warbler/stock-service-docker/Procfile
This tells Heroku that your application has a single process, called web, and
gives it the command to run for that process. The command uses two options,
Xmx and Xss, to optimize memory usage characteristics of the process for the
Heroku platform. You’ll learn what these mean and how to customize them
in Chapter 7, Tuning a JRuby Application, on page 109. Otherwise, it’s the same
command you ran locally.
Now deploy your app by running this command from the same directory as
the Procfile:
$ heroku deploy:jar --jar stock-service.war
Uploading stock-service.war....
-----> Packaging application...
...
-----> Done
The deploy process will take a minute to package your application and upload
the WAR file to Heroku. When it’s done, you can ensure the process is running
with this command:
$ heroku ps:scale web=1
Scaling dynos... done, now running web at 1:Free.
You’ll see the landing page where you can test out the /stockify service. Notice
that the URL begins with the name of the app and is followed by herokuapp.com.
This is the standard convention for new Heroku apps, but you can always
configure DNS for custom domains.
Return to the terminal, and exercise the /stockify service with curl but replace
obscure-fjord-4138 with the name of your app:
Deploying only the executable WAR file to Heroku is the quickest way to get
your app running in the cloud. But you can also deploy the entire Docker
image you created earlier. You may prefer this approach if you’re using
Docker to set up some extra dependencies.
Like the toolbelt plugin for WAR files, there’s also a toolbelt plugin for Docker.
Install the heroku-container-tools toolbelt plugin by running this command:
{
"name": "stock-service"
}
Now you’re ready to initialize the Heroku Docker config. Run this command:
$ heroku container:init --dockerfile Dockerfile
This reads the Dockerfile and Procfile and generates a docker-compose.yml, which
describes the complete environment.
Finally, deploy to the same Heroku app you created earlier by running this
command:
$ heroku container:release
Remote addons: (0)
Local addons: (0)
Missing addons: (0)
Creating local slug...
Building web...
...
The release process will take some time to package your application and
upload the image to Heroku. When it’s done, you can run the open command
again to view the app in a browser. Or you can use curl as before.
Wrapping Up
You’ve created an environment you can use to run your microservice or any
other JRuby application in production. You’ve also set up Docker, which
allows you to add new components to your infrastructure without running a
bunch of commands or managing a complicated configuration management
codebase. The tools you’ve used are state of the art for the industry, and it’s
likely that you’ll use them to set up new environments each time you embark
with a new customer or employer.
When you need to scale this environment horizontally, you won’t have to do
much work. You’ll start with the base image and launch Docker containers
from it. Or you can simply scale up your Heroku app.
In the coming chapters, you’ll build on this base Docker image. You’ll add
more components and deploy more complicated web applications. In the next
chapter, you’ll convert a full-blown Rails application to JRuby.
Habington, 485.
Hamelius, Herr, 425 note, 432 note, 448 note.
Hamilton, Anthony, 502.
Hannay, Mr David, 332 note, 341, 350.
Hardy, Alexandre (1560-1631), 255 note.
Harington, Sir John (1561-1612), 183 note, 186, 199.
Harris, James (1709-80), 473-476.
Harvey, Gabriel (1545-1630), 148, 165 sq.
Hawes, Stephen (?-1523?), 146, 147.
Hazlitt, 145, 219, 372 note, 496.
Heads of an Answer to Rymer, 373 note, 397 note.
Hédelin, see Aubignac.
Hegel, 274, 527 note.
Heinsius, Daniel (1580-1655), 356, 357.
Heldenbuch, 569.
Heliodorus, 77, 131.
Henriade, the, 514 note, 522, 526.
Henry IV., Dennis on, 434.
Herbert, G., 87.
Héricault, M. Ch. d’, 112 note.
Hermaphroditus, 66 note.
Hermes, 473.
Hermogenes, 319, 329.
Hero and Leander, 74.
“Heroic Play,” the, 367 sq.
“Heroic Poem,” the, 367 sq.
Hessus, Eobanus, 14, 28.
Heywood, Thomas, 398 note.
Histoire du Théâtre Français, Fontenelle’s, 505.
—— the Frères Parfait’s, 527.
Historia de las Ideas Estéticas en España, 331-351 passim.
History of the Rise and Progress of Poetry, 476.
—— of the Royal Society, 398.
Hobbes, Thomas (1588-1679), 40, 367-371.
Hoffmanswaldau, 552.
Home, Henry, see Kames.
Homer, 25, 31-35, 47, 52, 74, 86, 99, 122-126, 130-132, 141, 310
sq.
Horace, 6, 9, 129 sq. and passim.
Howard, Edward, 367 note.
—— Henry, see Surrey, Earl of.
—— J., 367 note.
—— Sir Robert (d. 1698: his birth-date and those of his brothers E.
and J. are very uncertain), 376 sq.
Howell’s Letters, 371 note.
Hudibras, 513 note.
Huerta, Vicente Garcia de la (1730?-87), 550 note.
Huet, Pierre Daniel (1630-1721), bishop of Avranches, 274, 275.
Huetiana, 275.
Hugo, Victor, 87.
Hume, Alexander, 209 note.
—— David, 462.
Hunt, Leigh, 372 note.
Hurd, 469.
Hutten, Ulrich von (1488-1523), 352 note.