Operations 26 min read

Understanding Message Queues (MQ) and Using ActiveMQ with Java and Spring Boot

This article explains the fundamentals of message queues, why they are needed for decoupling, asynchronous processing and throttling, introduces JMS concepts, details ActiveMQ storage options and protocols, and provides practical Java and Spring Boot code examples for producers, consumers, and advanced features such as transactions, persistence, and delayed delivery.

Full-Stack Internet Architecture
Full-Stack Internet Architecture
Full-Stack Internet Architecture
Understanding Message Queues (MQ) and Using ActiveMQ with Java and Spring Boot

Understanding Message Queues (MQ)

A message queue (MQ) is a container that stores messages (data to be transferred) in a first‑in‑first‑out (FIFO) structure. The entity that puts data into the queue is called a producer , and the entity that retrieves data is called a consumer .

Why Use a Message Queue

Decoupling – producers and consumers do not need to know each other’s interfaces.

Asynchronous processing – time‑consuming tasks can be handled later without blocking the main flow.

Throttling / rate limiting – queues absorb bursts of traffic and let downstream services process at a sustainable rate.

Decoupling Example

Consider a user system (System A) that generates a userId . Systems B, C and D all need this identifier. Without a queue, System A must call each system directly, leading to tight coupling and frequent code changes when a downstream system changes its API.

public class SystemA {
    SystemB systemB = new SystemB();
    SystemC systemC = new SystemC();
    private String userId = "activeMq-1234567890";
    public void doSomething() {
        systemB.SystemBNeed2do(userId);
        systemC.SystemCNeed2do(userId);
    }
}

After introducing a message queue, System A only writes the userId to the queue; B, C and D read it independently, eliminating the need to modify System A when downstream requirements change.

Asynchronous Processing

When the main business (e.g., order creation) is performed by System A, auxiliary tasks such as sending SMS or updating analytics can be processed asynchronously by other systems, improving response time and overall throughput.

Throttling / Rate Limiting

During a high‑traffic event (e.g., a flash sale with 3000 requests per second), two processing nodes each capable of handling 1000 requests can pull work from the queue, preventing overload and keeping the system stable.

JMS (Java Message Service) Overview

JMS is a Java API standard that abstracts underlying Message‑Oriented Middleware (MOM) implementations such as ActiveMQ, RocketMQ, IBM MQSeries, etc. It defines concepts such as Broker, Provider, Producer, Consumer, Queue, Topic, ConnectionFactory, Connection, Session, and Destination.

Broker : the server that provides core messaging services.

Producer : creates messages and sends them to a destination.

Consumer : receives messages from a destination, either synchronously (receive()) or asynchronously (MessageListener).

Queue (P2P) : point‑to‑point model where each message is consumed by a single consumer.

Topic (Pub/Sub) : publish‑subscribe model where a message is delivered to all subscribed consumers.

Session : a single‑threaded context for producing and consuming messages; can be transacted.

Message Reliability Mechanisms

JMS supports three acknowledgment modes: Session.AUTO_ACKNOWLEDGE , Session.CLIENT_ACKNOWLEDGE , and Session.DUPS_OK_ACKNOWLEDGE . Messages can be sent with PERSISTENT or NON_PERSISTENT delivery modes, and priority levels (0‑9) can be set to influence ordering.

ActiveMQ Specifics

Storage Options

ActiveMQ offers several persistence adapters; the default is KahaDB, which stores messages in append‑only log files with B‑Tree indexes. JDBC persistence can be used to store messages in relational databases (MySQL, PostgreSQL, Oracle, etc.).

<persistenceAdapter>
    <kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>

Supported Protocols

TCP (default, e.g., tcp://hostname:61616 )

NIO – reduces thread count for high‑concurrency scenarios.

UDP – useful when firewalls block TCP.

SSL, HTTP(S), VM

Security

Web console authentication is configured via jetty-realm.properties . Broker‑level authentication can be defined in activemq.xml using simpleAuthenticationPlugin with users and groups.

<plugins>
  <simpleAuthenticationPlugin>
    <users>
      <authenticationUser username="admin" password="admin" groups="admins,publishers,consumers"/>
      ...
    </users>
  </simpleAuthenticationPlugin>
</plugins>

Typical Java API Usage

Creating a connection, session, destination, producer, and sending a text message:

ActiveMQConnectionFactory acf = new ActiveMQConnectionFactory(
    ActiveMQConnectionFactory.DEFAULT_USER,
    ActiveMQConnectionFactory.DEFAULT_PASSWORD,
    "tcp://localhost:61618");
Connection connection = acf.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue("mq.test");
MessageProducer producer = session.createProducer(queue);
TextMessage message = session.createTextMessage("msg: hello world");
producer.send(message);
connection.close();

Consumer example (synchronous receive loop):

MessageConsumer consumer = session.createConsumer(queue);
while (true) {
    Message msg = consumer.receive();
    TextMessage tm = (TextMessage) msg;
    System.out.println("text:" + tm.getText());
}

Advanced Features

Transactional messages – session.commit() and session.rollback() .

Non‑persistent delivery – producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT) .

Message priority – producer.setPriority(int) .

Time‑to‑live – producer.setTimeToLive(long) .

Dead‑letter queue handling – messages that cannot be delivered end up in ActiveMQ.DLQ .

Exclusive consumer – session.createQueue("myQueue?consumer.exclusive=true") .

Asynchronous send – enable with connectionFactory.setUseAsyncSend(true) .

Message throttling – configure jms.producerWindowSize on the broker URL.

Delayed delivery – enable scheduler in activemq.xml and set ScheduledMessage.AMQ_SCHEDULED_DELAY on the message.

Spring Boot Integration

Add the starter dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>

Configure the broker URL, credentials and connection pool in application.yml (or application.properties ).

spring:
  activemq:
    broker-url: tcp://localhost:61618
    user: admin
    password: admin
    pool:
      enabled: true
      max-connections: 5
    packages:
      trust-all: true
  jms:
    pub-sub-domain: true

Define a JmsListenerContainerFactory for queue and topic modes, then write @JmsListener methods for consuming messages and a service that uses JmsMessagingTemplate to send messages.

@Configuration
@EnableJms
public class ActiveMqConfig {
    @Bean
    public JmsListenerContainerFactory
jmsListenerContainerTopic(ConnectionFactory activeMQConnectionFactory) {
        DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
        bean.setPubSubDomain(true);
        bean.setConnectionFactory(activeMQConnectionFactory);
        return bean;
    }
    @Bean
    public JmsListenerContainerFactory
jmsListenerContainerQueue(ConnectionFactory activeMQConnectionFactory) {
        DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
        bean.setConnectionFactory(activeMQConnectionFactory);
        return bean;
    }
}

Producer service example (simplified):

@Service
public class MqProducerService {
    @Autowired
    private JmsMessagingTemplate jmsMessagingTemplate;
    public void sendStringQueue(String destination, String msg) {
        ActiveMQQueue queue = new ActiveMQQueue(destination);
        jmsMessagingTemplate.convertAndSend(queue, msg);
    }
}

Consumer example using @JmsListener:

@JmsListener(destination = "user", containerFactory = "jmsListenerContainerQueue")
public void receiveStringQueue(String msg) {
    System.out.println("Received: " + msg);
}

Conclusion

The article covered the motivation for using message queues, core JMS concepts, ActiveMQ architecture, storage and protocol choices, security configuration, common Java APIs, and seamless Spring Boot integration, providing a solid foundation for building decoupled, asynchronous, and scalable systems.

JavaasynchronousSpring BootMessage QueueDecouplingActiveMQJMS
Full-Stack Internet Architecture
Written by

Full-Stack Internet Architecture

Introducing full-stack Internet architecture technologies centered on Java

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.