Category Archives: Java

Rolling Restarts with Capistrano and Haproxy for Java Web Service Apps

Java web apps can be efficient because they are multithreaded and you only need to run one copy of the process to serve multiple conconcurrent requests.
This is in constrast to Ruby apps where you often need multiple processes to serve multiple requests. Using one process instead of ~8 will save you a lot of memory on the system.

The downside of one process is dealing with rolling restarts. In the case of Ruby app servers like Unicorn, multiple processes are ran and thus can be setup to provide rolling restarts.

If you are using a web container such as Tomcat 7, it can support hot reload in place.

But let’s assume your Java JVM web app is ran with a single command(e.g. java -jar backend-1.0.jar &). The idea of this setup is that it can be abstract to any single process web service.

To get rolling restarts out of this setup, we can use capistrano with haproxy.

We want to:

* start two difference servers with one process each(or use two processes on one server, this won’t provide failover though)
* use an haproxy as a load balancer to these servers

In your Java web service apps, add a health check endpoint(/haproxy-bdh3t.txt) and have it serve an empty text file.

[It's important to use a random string as your endpoint if you are running in the public cloud since the load balancer could be referencing an old server address and haproxy could think a server is up but isn't. ]

In your haproxy.cfg, add

option httpchk HEAD /haproxy-bdh3t.txt HTTP/1.0

as your check condition to the backend services.

In your capistrano script, let’s add two servers set as the app role

server "XXX1", :app
server "XXX2", :app

and alter the restart task to:

* remove the check file for one server. This will remove the server from the load balancer
* restart server.
* ping the server.
* add the check file back to the started server which haproxy will add back into the load balancer.
* repeat as a loop for each server.


desc "Restart"
task :restart, :roles => :web do 
  haproxy_health_file = "#{current_path}/path-static-files-dir/haproxy-bdh3t.txt"

  # Restart each host serially
  self.roles[:app].each do |host|
    # take out the app from the load balancer
    run "rm #{haproxy_health_file}", :hosts => host
    # let existing connections finish
    sleep(5)

    # restart the app using upstart
    run "sudo restart backend_server", :hosts => host

    # give it sometime to startup
    sleep(5)

    # add the check file back to readd the server to the load balancer
    run "touch #{haproxy_health_file}", :hosts => host
  end
end

How to parse URL strings in Java

I tend to use apache httpclient as my preferred java http client. I hit an error with invalid symbols such as the space character in this url:

val urlString = "http://maps.google.com/maps?q=Merrick, NY"

val cm = new ThreadSafeClientConnManager()
val client = new DefaultHttpClient(cm)
val httpRequest = new HttpGet(urlString)

java.lang.IllegalArgumentException: Illegal character in query at index 38: http://maps.google.com/maps?q=Merrick, NY
at java.net.URI.create(URI.java:859)
at org.apache.http.client.methods.HttpGet.(HttpGet.java:69)

My first attempt would be to use java.net.URLEncoder.encode

java.net.URLEncoder.encode(urlString, "UTF-8")
res4: java.lang.String = http%3A%2F%2Fmaps.google.com%2Fmaps%3Fq%3DMerrick%2C+NY

But this doesn’t work, it’s only for forms and it tries to encode the entire url string.

Our goal is to convert just the param part of the url from “http://maps.google.com/maps?q=Merrick, NY” to “http://maps.google.com/maps?q=Merrick%2C%20NY”

The trick is to construct a URL object so we can get the separate components and then create a URI object from these components:

val urlString = "http://maps.google.com/maps?q=Merrick, NY"
val url = new java.net.URL(urlString)
val uri = new java.net.URI(url.getProtocol, url.getAuthority, url.getPath, url.getQuery, null)
val httpRequest = new HttpGet(uri)
httpRequest: org.apache.http.client.methods.HttpGet = org.apache.http.client.methods.HttpGet@15ec2337