JMS Failover With Transaction using Replication Example

This example demonstrates two servers coupled as a live-backup pair for high availability (HA) using replication, and a client connection failing over from live to backup when the live server is crashed.

Failover behavior differs whether the JMS session is transacter or not.

When a transacted JMS session is used, once-and-only once delivery is guaranteed.

ActiveMQ Artemis also provides an example for non-transaction failover.

For more information on ActiveMQ Artemis failover and HA, and clustering in general, please see the clustering section of the user manual.

Example step-by-step

To run the example, simply type mvn verify -Pexample from this directory

In this example, the live server is server 1, and the backup server is server 0

The connection will initially be created to server1, server 1 will crash, and the client will carry on seamlessly on server 0, the backup server.

  1. Get an initial context for looking up JNDI from server #1.
  2.            initialContext = getContext(1);
            
  3. Look up the JMS resources from JNDI on server #1.
  4.            Queue queue = (Queue)initialContext.lookup("/queue/exampleQueue");
               ConnectionFactory connectionFactory = (ConnectionFactory)initialContext.lookup("/ConnectionFactory");
            
  5. Create a JMS Connection
  6.            connection = connectionFactory.createConnection();
            
  7. Create a JMS transacted Session
  8.            Session session = connection.createSession(true, 0);
            
  9. Start the connection to ensure delivery occurs
  10.            connection.start();
            
  11. Create a JMS MessageProducer
  12.            MessageProducer producer = session.createProducer(queue);
            
  13. Create a JMS MessageConsumer
  14.            MessageConsumer consumer = session.createConsumer(queue);
            
  15. Send half of the messages, kill the live server and send the remaining messages
  16.            sendMessages(session, producer, numMessages, true);
            

    When server #1 crashes, the client automatically detects the failure and automatically fails over from server #1 to server #0 (in your real program you wouldn't need to sleep).

  17. As failover occurred during transaction, the session has been marked for rollback only and commit will fail
  18.            try
               {
                  session.commit();
               } catch (TransactionRolledBackException e)
               {
                  System.err.println("transaction has been rolled back: " + e.getMessage());
               }
            
  19. We resend all the messages
  20.            sendMessages(session, producer, numMessages, false);
            
  21. We commit the session successfully: the messages will be all delivered to the activated backup server
  22.            session.commit();
            
  23. We are now transparently reconnected to server #0, the backup server. We consume the messages sent before the crash of the live server, commit the session, and check there are no other message on the queue
  24.         for (int i = 0; i < numMessages; i++)
            {
               TextMessage message0 = (TextMessage)consumer.receive(5000);
               System.out.println("Got message: " + message0.getText());
            }
            session.commit();
            System.out.println("Other message on the server? " + consumer.receive(5000));
            
  25. And finally, always remember to close your resources after use, in a finally block. Closing a JMS connection will automatically close all of its sessions, consumers, producer and browser objects
  26.            finally
               {
                  if (connection != null)
                  {
                     connection.close();
                  }
    
                  if (initialContext != null)
                  {
                     initialContext.close();
                  }
               }