jruby-hornetq

Feedback is welcome and appreciated :)

Introduction

jruby-hornetq create a Ruby friendly API into the HornetQ Java libraries without compromising performance. It does this by sprinkling "Ruby-goodness" into the existing HornetQ Java classes and interfaces, I.e. By adding Ruby methods to the existing classes and interfaces. Since jruby-hornetq exposes the HornetQ Java classes directly there is no performance impact that would have been introduced had the entire API been wrapped in a Ruby layer.

In this way, using regular Ruby constructs a Ruby program can easily interact with HornetQ in a highly performant way

Install

gem install jruby-hornetq

Important

jruby-hornetq exposes the HornetQ Core API, not its JMS API. There are several reasons for choosing the HornetQ Core API over its JMS API:

  • The Core API supports the use of Addresses, not just Queues
  • The Core API exposes more capabilities than the JMS API (E.g. Management APIs)
  • The HornetQ team recommend the Core API for performance
  • The HornetQ JMS API is just another wrapper on top of its Core API

To use the JMS API from JRuby see the jruby-jms project

HornetQ

For information on the HornetQ messaging and queuing system, see: http://www.jboss.org/hornetq

For more documentation on any of the classes, see: http://docs.jboss.org/hornetq/2.2.2.Final/api/index.html

Concepts & Terminology

Queue

In order to read messages a consumer needs to the read messages from a queue. The queue is defined prior to the message being sent and is used to hold the messages. The consumer does not have to be running in order to receive messages.

Address

In traditional messaging and queuing systems there is only a queue when both read and writing messages. With the advent of AMQP and in HornetQ we now have the concept of an Address which is different from a Queue.

An Address can be thought of the address we would put on an envelope before mailing it. We do not have to have any knowlegde of the USPS infrastructure to mail the letter. In HornetQ we Address a message and in HornetQ the Address is routed to one or more Queues.

Durable

Messages in HornetQ can be marked as durable which means they will be persisted to disk to prevent message loss in the event of a power failure or other system failure. This does however mean that every durable message does incur the overhead of a disk write every time it is read or written (produced or consumed).

Broker

HornetQ is a broker based architecture which requires the use of one or more centralized brokers. A broker is much like the "server" through which all messages pass through.

An in-vm broker can be used for passing messages around within a Java Virtual Machine (JVM) instance without making network calls. Highly recommended for passing messages between threads in the same JVM.

Consumer

Producer

Asynchronous Messaging

It is recommended to keep the state of the message flow in the message itself.

Synchronous Messaging

Messaging Patterns

Message Priority

Overview

jruby-hornetq is primarily intended to make it easy to use the HornetQ client core API. It also supports running the HornetQ broker for scenarios such as in-vm messaging.

The examples below address some of the messaging patterns that are used in messaging and queuing.

Producer-Consumer

Producer: Write messages to a queue:

require 'rubygems'
require 'hornetq'

HornetQ::Client::Factory.create_session(:connection => {:uri => 'hornetq://localhost'}) do |session|
  # Create Producer so that we can send messages to the Address 'jms.queue.ExampleQueue'
  producer = session.create_producer('jms.queue.ExampleQueue')

  # Create a non-durable message to send
  message = session.create_message(HornetQ::Client::Message::TEXT_TYPE,false)
  message << "#{Time.now}: ### Hello, World ###"

  producer.send(message)
end

Consumer: Read message from a queue:

require 'rubygems'
require 'hornetq'

HornetQ::Client::Factory.start(:connection => {:uri => 'hornetq://localhost'}) do |session|
  consumer = session.create_consumer('jms.queue.ExampleQueue')

  # Receive a single message, return immediately if no message available
  if message = consumer.receive_immediate
    puts "Received:[#{message.body}]"
    message.acknowledge
  else
    puts "No message found"
  end
end

Client-Server

Server: Receive requests and send back a reply

require 'rubygems'
require 'hornetq'

# Shutdown Server after 5 minutes of inactivity, set to 0 to wait forever
timeout = 300000

HornetQ::Client::Factory.start(:connection => {:uri => 'hornetq://localhost'}) do |session|
  server = session.create_server('jms.queue.ExampleQueue', timeout)

  puts "Waiting for Requests..."  
  server.run do |request_message|
    puts "Received:[#{request_message.body}]"

    # Create Reply Message
    reply_message = session.create_message(HornetQ::Client::Message::TEXT_TYPE, false)
    reply_message.body = "Echo [#{request_message.body}]"

    # The result of the block is the message to be sent back to the client
    reply_message
  end

  # Server will stop after timeout period after no messages received
  server.close
end

Client: Send a request and wait for a reply

require 'rubygems'
require 'hornetq'

# Wait 5 seconds for a reply
timeout = 5000

HornetQ::Client::Factory.start(:connection => {:uri => 'hornetq://localhost'}) do |session|
  requestor = session.create_requestor('jms.queue.ExampleQueue')

  # Create non-durable message
  message = session.create_message(HornetQ::Client::Message::TEXT_TYPE,false)
  message.body = "Request Current Time"

  # Send message to the queue
  puts "Send request message and wait for Reply"
  if reply = requestor.request(message, timeout)
    puts "Received Response: #{reply.inspect}"
    puts "  Message: #{reply.body.inspect}"
  else
    puts "Time out, No reply received after #{timeout/1000} seconds"
  end

  requestor.close
end

Threading

A factory instance can be shared between threads, whereas a session and any artifacts created by the session should only be used by one thread at a time.

For consumers, it is recommended to create a session for each thread and leave that thread blocked on ClientConsumer::receive A timeout can be used if the thread needs to do any other work. At this time it is Not recommended to use ClientConsumer::receive_immediate across multiple threads due to known issues in HornetQ with this API.

Example

Logging

Dependencies

JRuby

jruby-hornetq has been tested against JRuby 1.5.1, but should work with any current JRuby version.

HornetQ

The libraries required for the HornetQ Client and to start a simple Core API only Broker are included with the Gem.

GenePool

GenePool is used to implement session pooling

Running the Broker

Not only does jruby-hornetq make it easy to work with HornetQ from JRuby as a client, it also supports using JRuby to launch a Broker instance

Example Usage

Starting up a standalone hornetq server:

bin/hornetq_server examples/server/standalone_server.yml

Starting up a backup/live combination

bin/hornetq_server examples/server/backup_server.yml bin/hornetq_server examples/server/live_server.yml

Development

Want to contribute to jruby-hornetq?

First clone the repo and run the tests:

git clone git://github.com/reidmorrison/jruby-hornetq.git
cd jruby-hornetq
jruby -S rake test

Feel free to ping the mailing list with any issues and we'll try to resolve it.

Contributing

Once you've made your great commits:

  1. Fork jruby-hornetq
  2. Create a topic branch - git checkout -b my_branch
  3. Push to your branch - git push origin my_branch
  4. Create an Issue with a link to your branch
  5. That's it!

You might want to checkout our [Contributing][cb] wiki page for information on coding standards, new features, etc.

Meta

This project uses Semantic Versioning.

Authors

Reid Morrison :: [email protected] :: @reidmorrison

Brad Pardee :: [email protected]

License

Copyright 2011 Clarity Services, Inc.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

jruby-hornetq includes files from HornetQ, which is also licensed under the Apache License, Version 2.0: http://www.jboss.org/hornetq