1 overview

JMS, or Java Message Service, is a set of specifications that provide a convenient and common way for Java programs to create, send, receive, and read messages.

Message Oriented Middleware (MMIDDLEWARE for short) is used in many medium and large projects. Popular mmiddleware products include ActiveMQ, RabbitMQ, Kafka, RocketMQ, etc. Many large commercial applications use these products to build messaging systems for applications, where messages are passed between modules (or services) in the application to decouple, reduce peak load, and improve application throughput.

Because there are so many products, without a mature specification, the specific implementation of message-oriented middleware can be very different from one manufacturer to another, making it very difficult for developers to switch products and have to rewrite a lot of code to adapt to the product. JMS is such a specification, which defines a message middleware products should have which components, which interface, etc., if the message middleware can according to the specification defines interfaces, developers in building a news system only need to pay a few price to switch different products, because the interface is the same, almost don’t need to modify the code.

The JMS specification defines several components as follows:

  • The JMS Provider. A component that provides a messaging service to a JMS client, which can have producer clients, consumer clients, and so on.
  • The JMS Message. That is, the messaging entity.
  • JMS Domains. The destination of the message delivery.

The JMS Message contains the following sections:

  • The Header. All messages have the same Header fields, similar to HTTP headers. These fields are used for routing or identity authentication between the client and the provider, including JMSDestination, JMSMessageID, and JMSTimestamp.
  • The Properties. Properties are optional. A property consists of a key-value pair. The key is the name of the property, and the value is the value of the property. An application can customize attributes and send them along with the Header and Body attached to a Message.
  • Body is the Body of the message, for example, for a normal text message, the message subject is the content of this article, such as the “Hello” string.

This is a brief introduction to JMS. For more information about the JMS specification, please refer to the JSR 914 documentation (you can find this keyword directly in a search engine). I will introduce the interface defined by the JMS specification and use ActiveMQ to build a small Demo as a practice. Point-to-point transport model and publish/subscribe transport model.

Note that the message here is not a narrow message, that is, it does not refer to a sentence or an email, etc. Messages can be not only strings, but also Java objects, etc.

2 the JMS interface

The interfaces defined by the JMS specification are shown in the figure above. On the left is the JMS general interface, and on the right is the interfaces of the two different messaging models.

  • A Connection needs to be created with a ConnectionFactory.
  • Connection indicates the Connection to the JMS Provider.
  • Destination refers to the Destination at which a message is sent, such as the recipient’s email address when an email is sent.
  • A Session, or Session, is a single-threaded context for sending and receiving messages.
  • MessageProducer is the sender of messages and is responsible for creating and sending JMS Providers for messages.
  • MessageConsumer, or MessageConsumer, is the receiver of the message, responsible for receiving and reading (consuming) the message from the JMS Provider.

The following is a diagram of the interfaces:

As you will see below, we almost write according to such a diagram when using ActiveMQ to write code.

3 use ActiveMQ

JMS specification is not implemented in standard Java SE, so there are no interfaces and corresponding implementations mentioned above in standard JDK, but Java EE has implementation, but it is relatively troublesome to build Java EE (just a Demo), so ActiveMQ is directly used here. ActiveMQ implements JMS specifications and provides interfaces defined by JMS specifications. The reason why ActiveMQ is used instead of other products is that the implementation of ActiveMQ is relatively “standard” and fits JMS specifications very well, and the learning threshold is lower than other products.

3.1 Install and run ActiveMQ

The installation is very simple, you can download it directly from ActiveMQ’s official website. The version I use here is 5.15.6. Under Windows, download the compressed package apache-ActivemQ-5.15.6-bin. zip (under Unix, Download apache-Activemq-5.15.6-bin.tar. gz), decompress, enter the bin directory in the command line, and enter activemq start.

Go to the Get Start section of the official website for a look, the installation of the boot is very detailed, I will not go into here.

After startup, you can enter http://localhost:8161 in the browser to view various data of ActiveMQ, as shown below:

Click on Manage ActiveMQ Broker to enter the console. The default account and password are admin, and the following is displayed:

Here, you can select each TAB bar to view different information, such as Topic, Queue, etc., the details are not detailed here, readers can try their own.

3.2 Import ActiveMQ dependencies

If you are using Maven to build your project, you can add the following dependencies:

<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-all</artifactId>
    <version>5.15.6</version>
</dependency>
Copy the code

If you are not using Maven, you will need to import the jar package into your project. The package contains the activemq-all-5.15.6.jar package and you can import it into your project’s classpath.

3.3 write Demo

With all the preparation done, it’s time to write the code. The demo code looks like this:

JMSProducer

package top.yeonon.jms;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/ * * *@Author yeonon
 * @date2018/10/20 0020 14:09 * * /
public class JMSProducer {

    private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
    private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
    private static final String BROKER_URL = ActiveMQConnection.DEFAULT_BROKER_URL;

    public static void main(String[] args) throws JMSException {
        // Connect factory
        ConnectionFactory connectionFactory;
        / / the connection
        Connection connection;
        / / the Session Session
        Session session;
        / / destination
        Destination destination;

        // Create a connection factory instance
        connectionFactory = new ActiveMQConnectionFactory(USERNAME,PASSWORD, BROKER_URL);
        // Create a connection from the connection factory instance
        connection = connectionFactory.createConnection();
        Create Session from the connection, the first parameter is whether to enable transaction, select not to enable, pass false
        // The second parameter is message confirmation mode, where automatic confirmation is selected
        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Queue (the Queue in the javax. JMS package) inherits the Destination interface
        destination = session.createQueue("Hello");
        // Create a producer that passes ina destination. The producer is bound to the destination
        MessageProducer producer = session.createProducer(destination);

        // When the configuration is complete, start the connection
        connection.start();

        // Send a message
        for (int i = 0; i < 10; i++) {
            TextMessage message = session.createTextMessage("message " + i);
            System.out.println("producer send message : " + message.getText());
            producer.send(message);
        }

        // After sending, close the connectionconnection.close(); }}Copy the code

JMSConsumer

package top.yeonon.jms;

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

/ * * *@Author yeonon
 * @date2018/10/20 0020 [* * /
public class JMSConsumer {

    private static final String USERNAME = ActiveMQConnection.DEFAULT_USER;
    private static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
    private static final String BROKER_URL = ActiveMQConnection.DEFAULT_BROKER_URL;

    public static void main(String[] args) throws JMSException {
        // Connect factory
        ConnectionFactory connectionFactory;
        / / the connection
        Connection connection;
        / / the Session Session
        Session session;
        / / destination
        Destination destination;

        // Create a connection factory instance
        connectionFactory = new ActiveMQConnectionFactory(USERNAME,PASSWORD, BROKER_URL);
        // Create a connection from the connection factory instance
        connection = connectionFactory.createConnection();
        Create Session from the connection, the first parameter is whether to enable transaction, select not to enable, pass false
        // The second parameter is message confirmation mode, where automatic confirmation is selected
        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Queue (the Queue in the javax. JMS package) inherits the Destination interface
        destination = session.createQueue("Hello");
        // Create a producer that passes ina destination. The producer is bound to the destination
        MessageConsumer consumer = session.createConsumer(destination);

        // When the configuration is complete, start the connection
        connection.start();

        // Accept the message
        while (true) {
            TextMessage message = (TextMessage) consumer.receive(10000);
            if(message ! =null)
                System.out.println("receive message : "+ message.getText()); }}}Copy the code

The comments in the code are very clear, just to mention a confusing Destination. In the demo code, we use Queue to represent a Destination. If you are first exposed to message queues, you might think it is a bit strange that a Queue is a Destination. It is entirely possible, because the destination is not necessarily the IP address or email or something like that, as long as it is an acceptable carrier of Message, now most of the Message middleware can only Queue to more or less as a carrier of Message, so sometimes the Message directly to the abbreviation for Message Queue middleware (Message Queue).

Run consumer, wait a little while, and then run producer. After running producer, you can see output similar to the following on the producer console:

producer send message : message 0
producer send message : message 1
producer send message : message 2
producer send message : message 3
producer send message : message 4
producer send message : message 5
producer send message : message 6
producer send message : message 7
producer send message : message 8
producer send message : message 9
Copy the code

Then go to the Consumer console and see something like this:

receive message : message 0
receive message : message 1
receive message : message 2
receive message : message 3
receive message : message 4
receive message : message 5
receive message : message 6
receive message : message 7
receive message : message 8
receive message : message 9
Copy the code

If you can see the above output, it indicates that the producer has successfully sent the message to the JMS Provider (ActiveMQ service), and the consumer has successfully fetched the message from the JMS Provider, read it, and processed it. If you enable multiple consumers, a message sent by producer is consumed by multiple consumers. For example, there are two consumers.

consumer1:

receive message : message 0
receive message : message 2
receive message : message 4
receive message : message 6
receive message : message 8
Copy the code

consumer2:

receive message : message 1
receive message : message 3
receive message : message 5
receive message : message 7
receive message : message 9
Copy the code

This is a bit of load balancing because there are now two consumers sharing a queue Hello and taking turns to take messages out of queue Hello and consume them (which is the default for ActiveMQ).

4 Message transfer model

The JMS specification defines two messaging transport models: the point-to-point transport model and the publish/subscribe transport model.

4.1 Point-to-point transfer model

In the point-to-point model, messages are sent by producers to queues, which then send messages to consumers. In this process, the message destination is the queue, the consumer listens to the queue, and when there is a message in the queue, the queue sends the message to the consumer listening to the queue. A queue can simply be thought of as a relay station that receives messages from producers and then sends them back to consumers.

A queue can have multiple producers and consumers, but a message can only be accepted and consumed by one consumer. The JMS provider determines which consumer the message should be sent to on a “first come, first served” basis. If there is no a consumer is listening to the queue, the message farewell piled up in the queue, message can be piled up how much the size of the queue and JMS provider specific implementation of the decision, if more than the size of the rules, redundant messages may be abandoned, can also be stored in persistent storage (depending on the specific implementation).

4.2 Publish/subscribe transport model

Under the publish/subscribe model, the destination of a message is no longer a simple queue, but a Topic (Topic), to which the message is sent by the producer, and then to the consumers who subscribe to the Topic. A Topic can also bind to multiple producers and consumers. Unlike the point-to-point model, a message can be consumed by multiple consumers. That is, if two consumers subscribe to a Topic, when a message is sent to that Topic, the Topic sends two copies of the message to both consumers.

It is worth noting that this Topic is an abstract concept, its specific form can also be a queue or group composed of multiple queue queue (depending on the specific implementation), just call on the queue on a Topic labels, producers and consumers of the target is no longer simply a queue, but a Topic. For example, we now define a Topic called Hello in which the producer wants to send a message and the consumer is interested (subscription Topic). At the bottom of this Topic is a queue. When the producer sends a message, The underlying message is actually sent to this queue labeled Hello Topic, and then a copy of the message is delivered to consumers who are interested in Hello Topic.

The explanation here may be a little unreasonable, hope the reader can understand. If you don’t understand it, I’ll write a code demo of the publish/subscribe model to make it easier for readers to understand.

The Demo I wrote in Section 3 was actually a point-to-point model. Switching from point-to-point model to publish/subscribe model was very simple. For the Demo above, only two lines of code needed to be changed:

// Producer in the point-to-point model
destination = session.createQueue("Hello");
// Producer in publish/subscribe model
destination = session.createTopic("Hello");


// Consumer in the point-to-point model
destination = session.createQueue("Hello");
// Consumer in the publish/subscribe model
destination = session.createTopic("Hello");
Copy the code

After making the changes, run two consumers and a producer and look at the console output.

The output of the producer is roughly as follows:

producer send message : message 0
producer send message : message 1
producer send message : message 2
producer send message : message 3
producer send message : message 4
producer send message : message 5
producer send message : message 6
producer send message : message 7
producer send message : message 8
producer send message : message 9
Copy the code

consumer1:

receive message : message 0
receive message : message 1
receive message : message 2
receive message : message 3
receive message : message 4
receive message : message 5
receive message : message 6
receive message : message 7
receive message : message 8
receive message : message 9
Copy the code

consumer2:

receive message : message 0
receive message : message 1
receive message : message 2
receive message : message 3
receive message : message 4
receive message : message 5
receive message : message 6
receive message : message 7
receive message : message 8
receive message : message 9
Copy the code

Consumer1 and Consumer2 both received 10 messages from Producer for a total of 20 messages. This is different from the point-to-point model, where no matter how many consumers there are, a total of 10 messages can be received.

5 subtotal

This article briefly introduced some of the components, interfaces, of the JMS specification, which can deepen your understanding of various message-oriented middleware products. Then I introduced the two messaging models defined by JMS: the point-to-point model and the publish/subscribe model. The biggest difference between the two is that in the point-to-point model, a message can only be accepted and consumed by one consumer, no matter how many consumers there are, whereas in the publish/subscribe model, a message can be consumed by multiple consumers. The article also provides a Demo, so that we can intuitively feel the difference between the two models, deepen understanding.

6 Reference Materials

JSR 914