Abstract: This article belongs to the original, welcome to reprint, reprint please reserve source: github.com/jasonGeng88… All services in this paper are deployed in docker container mode

The current environment

  1. Mac OS 10.11.x
  2. Docker 1.12.1
  3. JDK 1.8
  4. SpringBoot 1.5

The basic concept

Before introducing the specific use of JMS, a brief introduction to some basic knowledge of JMS. I’m going to break it down into three parts, namely, connection to message queues (MQ), message sending, and message receiving.

Here, our technology selection is SpringBoot, JMS and ActiveMQ

To better understand JMS, there is no SpringBoot zero configuration for scaffolding projects

Message is sent

The sending of messages is done through the JmsTemplate class in the JMS core package, which simplifies JMS because it handles the creation and release of resources when sending or receiving messages synchronously. As you can guess from what it does, it needs to reference the connection factory we created above:

@Bean
public JmsTemplate jmsQueueTemplate(){

    return new JmsTemplate(connectionFactory());
    
}Copy the code

Once the JmsTemplate is created, we can call its methods to send messages. There are two concepts to note here:

  1. Where does the message go? -> Specifies the Destination of the sending queue, which is a JMS management object that can be stored and extracted in JNDI.
  2. What is the body of the message sent? – > has been realizedjavax.jms.MessageObject similar to the JAVA RMI Remote object.

Code examples:

@Autowired
private JmsTemplate jmsQueueTemplate;

/ **
* Send the original Message Message
 * /
public void send(){

    jmsQueueTemplate.send("queue1".new MessageCreator() {
        @Override
        public Message createMessage(Session session) throws JMSException {
            return session.createTextMessage("I'm the source"); }}); }Copy the code

Optimization: Of course, instead of creating a Message object every time through the MessageCreator anonymous class, the JmsTemplate class provides methods for converting object entities into Message objects automatically. ConvertAndSend (String destinationName, final Object Message).

Examples of optimized code:

/ **
* Sent messages are automatically converted to original messages
 * /
public void convertAndSend(){

    jmsQueueTemplate.convertAndSend("queue1"."I am automatically converting messages");
    
}Copy the code

Note: Message transformation can also be implementedMessageConverterInterfaces customize transformation content

scenario

Code address: github.com/jasonGeng88…

Now that you have a basic understanding of JMS, let’s use it in a specific scenario.

First of all, we need to start ActiveMQ first. Here, we start it in the way of Docker container.

Start command:

docker run -d -p 8161:8161 -p 61616:61616 --name activemq webcenter/activemq
Copy the code

After successful startup, check the effect on the ActiveMQ visual interface (http://localhost:8161) :


Point-to-point model (single consumer)

The following describes one of the most common scenarios in message queuing, the point-to-point pattern. The basic concepts are as follows:

  1. Each message can only be consumed by one Consumer. Once a message has been consumed, it no longer exists in the message queue.
  2. There is no time dependency between sender and receiver, which means that when a sender sends a message, it does not affect whether the receiver is running or not.
  3. After receiving a message successfully, the receiver must reply to the queue successfully.

Code implementation (in order to simplify the code, part of the code as described above) :

  • Startup file (application.java)
@SpringBootApplication
@EnableJms
public class Application {

    .

    / **
* Template classes for JMS queues
* connectionFactory() is the ActiveMQ connectionFactory
     * /
    @Bean
    public JmsTemplate jmsQueueTemplate() {return new JmsTemplate(connectionFactory());
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args); }}Copy the code

The @enableJMS annotation is set on the @Configuration class to declare support for JMS annotations.

  • Message Producer (pTPProducer.java)
@Component
public class PtpProducer {

    @Autowired
    private JmsTemplate jmsQueueTemplate;

    / **
* Sent messages are automatically converted to original messages
     * /
    public void convertAndSend(){
        jmsQueueTemplate.convertAndSend("ptp"."I am automatically converting messages"); }}Copy the code

  • Producer invocation class (ptpController.java)
@RestController
@RequestMapping(value = "/ptp")
public class PtpController {

    @Autowired
    private PtpProducer ptpProducer;

    @RequestMapping(value = "/convertAndSend")
    public Object convertAndSend(){
        ptpProducer.convertAndSend();
        return "success"; }}Copy the code

  • Message listening container factory
@SpringBootApplication
@EnableJms
public class Application {

	.

    / **
* Listener container factory for JMS queues
     * /
    @Bean(name = "jmsQueueListenerCF")
    public DefaultJmsListenerContainerFactory jmsQueueListenerContainerFactory() {
        DefaultJmsListenerContainerFactory factory =
                new DefaultJmsListenerContainerFactory(a); factory.setConnectionFactory(connectionFactory());
        //Setting the number of connections
        factory.setConcurrency("3-10");
        //Reconnection interval time
        factory.setRecoveryInterval(1000L);
        return factory;
    }

   .
   
}Copy the code

  • Message listener
@Component
public class PtpListener1 {

    / **
* Message queue listeners
* destination Specifies the queue address
* containerFactory specifies the listener containerFactory if more than two listener container factories exist
     * /
    @JmsListener(destination = "ptp".containerFactory = "jmsQueueListenerCF")
    public void receive(String msg) {System.out.println("Point-to-point mode 1:" +msg); }}Copy the code

Peer-to-peer model (multi-consumer)

Based on the above consumer consumption pattern, since there may be many producers sending messages to a queue at the same time, one consumer may become the bottleneck. Therefore, multiple consumers are required to share the consumption pressure (the consumption thread pool can solve some of the pressure, but after all, on a single machine, it cannot be distributed, so it is necessary to have multiple consumers), resulting in the following scenario.

Code implementation

  • Add a new listener
@Component
public class PtpListener2 {

    @JmsListener(destination = Constant.QUEUE_NAME.containerFactory = "jmsQueueListenerCF")
    public void receive(String msg) {System.out.println("Point-to-point mode 2:" +msg); }}Copy the code

Publish and subscribe model

In addition to the point-to-point pattern, the publish-subscribe pattern is a common use in message queues. Imagine an instant chat group where you send a message. Everyone who is in the group (that is, subscribed to the group) will receive your message.

Basic Concepts:

  1. Each message can have multiple consumers.
  2. There is a temporal dependency between publisher and subscriber. For subscribers to a Topic, it must create a subscriber before it can consume the publisher’s messages.
  3. In order to consume messages, the subscriber must remain running.

Code implementation

  • Modify the JmsTemplate template class to support publish-subscribe functionality
@SpringBootApplication
@EnableJms
public class Application {

    .

    @Bean
    public JmsTemplate jmsTopicTemplate() {JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory());
        jmsTemplate.setPubSubDomain(true);
        return jmsTemplate;
    }
    
    .

}Copy the code

  • Message Producers (PubSubproducer.java)
@Component
public class PtpProducer {

    @Autowired
    private JmsTemplate jmsTopicTemplate;

    public void convertAndSend(){
		jmsTopicTemplate.convertAndSend("topic"."I am automatically converting messages"); }}Copy the code

  • Producer invocation class (PubSubController.java)
@RestController
@RequestMapping(value = "/pubsub")
public class PtpController {

    @Autowired
    private PubSubProducer pubSubProducer;

    @RequestMapping(value = "/convertAndSend")
    public String convertAndSend(){
        pubSubProducer.convertAndSend();
        return "success"; }}Copy the code

  • Modify DefaultJmsListenerContainerFactory class, make its support to release subscription function
@SpringBootApplication
@EnableJms
public class Application {

	.

    / **
* Listener container factory for JMS queues
     * /
    @Bean(name = "jmsTopicListenerCF")
    public DefaultJmsListenerContainerFactory jmsTopicListenerContainerFactory() {
        DefaultJmsListenerContainerFactory factory =
                new DefaultJmsListenerContainerFactory(a); factory.setConnectionFactory(connectionFactory());
        factory.setConcurrency("1");
        factory.setPubSubDomain(true);
        return factory;
    }

   .
   
}Copy the code

  • Message listener (set 2 subscribers here)
@Component
public class PubSubListener1 {

    @JmsListener(destination = "topic".containerFactory = "jmsTopicListenerCF")
    public void receive(String msg) {System.out.println("Subscriber 1 -" +msg); }}@Component
public class PubSubListener2 {

    @JmsListener(destination = "topic".containerFactory = "jmsTopicListenerCF")
    public void receive(String msg) {System.out.println("Subscriber 2 -" +msg); }}Copy the code

conclusion

Here is just a brief description and use of SpringBoot and JMS integration. For detailed introduction, you can check the official documentation of Spring. I was also fortunate to participate in the translation work of Spring 5 initiated by Concurrent Programming network, mainly translating the JMS chapter of Spring 5. Its content for the above basic concepts of JMS, have a detailed description of the expansion, interested can take a look, of course, the translation level is limited, good English advice to see the original.