JBoss Messaging Symmetric Cluster Example


This examples demonstrates a symmetric cluster set-up with JBoss Messaging.

JBoss Messaging has extremely flexible clustering which allows you to set-up servers in many different topologies.

The most common topology that you'll perhaps be familiar with if you are used to application server clustering is a symmetric cluster.

With a symmetric cluster, the cluster is homogeneous, i.e. each node is configured the same as every other node, and every node is connected to every other node in the cluster.

By connecting node in such a way, we can, from a JMS point of view, give the impression of distributed JMS queues and topics.

The configuration used in this example is very similar to the configuration used by JBoss Messaging when installed as a clustered profile in JBoss Application Server.

To set up JBoss Messaging to form a symmetric cluster we simply need to mark each server as clustered and we need to define a cluster-connection in jbm-configuration.xml.

The cluster-connection tells the nodes what other nodes to make connections to. With a cluster-connection each node that we connect to can either be specified indivually, or we can use UDP discovery to find out what other nodes are in the cluster.

Using UDP discovery makes configuration simpler since we don't have to know what nodes are available at any one time.

Here's the relevant snippet from the server configuration, which tells the server to form a cluster with the other nodes:

     
   <cluster-connection name="my-cluster">
      <address>jms</address>
	   <retry-interval>500</retry-interval>
	   <use-duplicate-detection>true</use-duplicate-detection>
	   <forward-when-no-consumers>true</forward-when-no-consumers>
	   <max-hops>1</max-hops>
	   <discovery-group-ref discovery-group-name="my-discovery-group"/>
   </cluster-connection>
   
     

In this example we create a symmetric cluster of three live nodes, and we also pair each live node with it's own backup node. (A backup node is not strictly necessary for a symmetric cluster).

By providing each node with a backup, we ensure that, in case of failure, the cluster will continue without failure, with all connections on live failing over transparently onto the backup.

In this example will we will demonstrate this by deploying a JMS topic and Queue on all nodes of the cluster , sending messages to the queue and topic from different nodes, and verifying messages are received correctly by consumers on different nodes.

During the example will will also kill each live server in turn, at different times, and verify that the sending consuming of messages carries on uninterrupted, as connections transparently fail over from live to backup.

For more information on configuring JBoss Messaging clustering in general, please see the clustering section of the user manual.

Example step-by-step

To run the example, simply type ant from this directory


  1. We instantiate a connection factory directly, specifying the UDP address and port for discovering the list of servers in the cluster. We could use JNDI to look-up a connection factory, but we'd need to know the JNDI server host and port for the specific server to do that, and that server might not be available at the time. By creating the connection factory directly we avoid having to worry about a JNDI look-up. In an app server environment you could use HA-JNDI to lookup from the clustered JNDI servers without having to know about a specific one.
  2.            
       ConnectionFactory cf = new JBossConnectionFactory("231.7.7.7", 9876); 
       
            
  3. Directly instantiate JMS Queue and Topic objects
  4.            
       Queue queue = new JBossQueue("exampleQueue");
             
       Topic topic = new JBossTopic("exampleTopic");           
               
            
  5. We create three connections, they should be to different nodes of the cluster in a round-robin fashion and start them.
  6.            
       connection0 = cf.createConnection();
             
       connection1 = cf.createConnection();
             
       connection2 = cf.createConnection();
             
       connection0.start();
             
       connection1.start();
             
       connection2.start();           
               
            
  7. We create a session on each connection.
  8.            
       Session session0 = connection0.createSession(false, Session.AUTO_ACKNOWLEDGE);
             
       Session session1 = connection1.createSession(false, Session.AUTO_ACKNOWLEDGE);
             
       Session session2 = connection2.createSession(false, Session.AUTO_ACKNOWLEDGE);           
               
            
  9. We create a topic subscriber on each server.
  10.            
       MessageConsumer subscriber0 = session0.createConsumer(topic);
             
       MessageConsumer subscriber1 = session1.createConsumer(topic);
             
       MessageConsumer subscriber2 = session2.createConsumer(topic);                     
               
            
  11. We create a queue consumer on server 0.
  12.           
       MessageConsumer consumer0 = session0.createConsumer(queue);
              
            
  13. We create an anonymous message producer on server 2.
  14.           
       MessageProducer producer2 = session2.createProducer(null);
              
            
  15. We send 500 messages each to the queue and topic.
  16.            
       final int numMessages = 500;
                      
       for (int i = 0; i < numMessages; i++)
       {
          TextMessage message1 = session2.createTextMessage("Topic message 1");
       
          producer2.send(topic, message1);
          
          TextMessage message2 = session2.createTextMessage("Queue message 1");
          
          producer2.send(queue, message2);
       }
               
            
  17. We kill live server 1, this will cause connection1 to transparently fail over onto server 4.
  18.            
       killServer(1);
                
            
  19. Verify all subscribers receive the messages.
  20.            
       for (int i = 0; i < numMessages; i++)
       {         
          TextMessage received0 = (TextMessage)subscriber0.receive(5000);
          
          if (received0 == null)
          {
             return false;
          }
          
          TextMessage received1 = (TextMessage)subscriber1.receive(5000);
          
          if (received1 == null)
          {
             return false;
          }
          
          TextMessage received2 = (TextMessage)subscriber2.receive(5000);
          
          if (received2 == null)
          {
             return false;
          }
                      
          TextMessage received3 = (TextMessage)consumer0.receive(5000);
          
          if (received3 == null)
          {
             return false;
          }         
       }
               
            
  21. Send 500 more messages to the queue and topic.
  22.            
       for (int i = 0; i < numMessages; i++)
       {
               
            
  23. Half way through sending we kill server 2
  24.            
          if (i == numMessages / 2)
          {
             killServer(2);
          }
                      
          TextMessage message3 = session2.createTextMessage("Topic message 2");
          
          producer2.send(topic, message3);
                                  
          TextMessage message4 = session2.createTextMessage("Queue message 2");
          
          producer2.send(queue, message4);
       }
            
  25. Verify all the messages are received by the subscribers.
  26.            
       for (int i = 0; i < numMessages; i++)
       {
               
            
  27. Half way through receiving, we kill server 0.
  28.            
          if (i == numMessages / 2)
          {
             killServer(0);
          }
       
          TextMessage received0 = (TextMessage)subscriber0.receive(5000);
          
          if (received0 == null)
          {
             return false;
          }
          
          TextMessage received1 = (TextMessage)subscriber1.receive(5000);
          
          if (received1 == null)
          {
             return false;
          }
          
          TextMessage received2 = (TextMessage)subscriber2.receive(5000);
          
          if (received2 == null)
          {
             return false;
          }
          
          TextMessage received3 = (TextMessage)consumer0.receive(5000);
          
          if (received3 == null)
          {
             return false;
          }         
       }
               
            
  29. Be sure to close our resources!
  30.            
       finally
       {
          if (connection0 != null)
          {
             connection0.close();
          }
    
          if (connection1 != null)
          {
             connection1.close();
          }
          
          if (connection2 != null)
          {
             connection2.close();
          }
       }