Basic usage guide of JMS message queue interface

overview

introduce

JMS (Java Message Service) is the Java Message Service application programming interface, which is an API for message-oriented middleware (MOM) in the Java platform, which is used to send messages between two applications or in a distributed system for asynchronous communication. Java Message Service is a platform-independent API, and most MOM providers provide support for JMS.

In short, JMS is an API that has nothing to do with the manufacturer. It is a set of API interfaces defined by Sun Corporation in order to unify the manufacturer's interface specifications, and is used to access the messaging system messages. It is similar to JDBC (Java Database Connectivity) and provides the function of asynchronous communication between applications.


JMS architecture

  • JMS provider (implementation of JMS, such as activemq, jbossmq, tonglinkmq, etc.)
  • JMS client (the program or object that uses the provider to send messages, for example, in 12306, is responsible for sending a ticket purchase message to the processing queue to solve the problem of ticket purchase peak, then, the program that sends the message to the queue and gets the message from the queue programs are called clients)
  • JMS producer (producer, sender): the client responsible for creating and sending messages
  • JMS consumer (customer, listener): the client responsible for receiving and processing messages
  • JMS message (message): an object that transfers data between JMS clients
  • JMS queue (queue): an area that holds messages that are sent waiting to be read
  • JMS topic (topic): a mechanism that supports sending messages to multiple subscribers

JMS object model

  • The connection factory (connectionFactory) client uses JNDI to find the connection factory, and then uses the connection factory to create a JMS connection
  • JMS connection: Indicates an active connection between the JMS client and the server, which is established by the client by calling the method of the connection factory
  • JMS session: session identifies the session state of the JMS client and server. A session is established on a JMS connection and identifies a session process between a client and a server.
  • JMS purpose (Destination): also known as message queue, is the actual message source
  • producer and consumer
  • Message type: divided into queue type (first in first out) and subscription type

message listener

MessageListener

MessageListener is the most primitive message listener, which is an interface defined in the JMS specification. It defines an onMessage() method for processing received messages, which only receives a Message parameter.

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

public class ConsumerMessageListener implements MessageListener {
    
    
    public void onMessage(Message message) {
    
    
        // 若生产者发送的是一个纯文本消息,可以直接进行强制转换,或者直接把onMessage方法的参数改成Message的子类TextMessage
        TextMessage textMsg = (TextMessage) message;
        System.out.println("接收到一个纯文本消息。");
        try {
    
    
            System.out.println("消息内容是:" + textMsg.getText());
        } catch (JMSException e) {
    
    
            e.printStackTrace();
        }
    }
}

SessionAwareMessageListener

SessionAwareMessageListener is provided by Spring, it is not a standard JMS MessageListener.

MessageListener is designed purely for receiving messages. If you need to send a message to notify the other party that the message has been received when using MessageListener to process the received message, then you need to reacquire a Connection or Session in the code at this time. The SessionAwareMessageListener is designed to facilitate sending a reply message after receiving a message. It also provides an onMessage() method to process the received message, but this method can receive two parameters at the same time, one is to indicate the currently received The message Message, and the other is the Session object that can be used to send messages.

Using the SessionAwareMessageListener listener, after listening and consuming the message, you don't need to reacquire a Connection or Session, but directly send a message to a certain queue of the original Connection or Session.

Code example:

import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.springframework.jms.listener.SessionAwareMessageListener;

public class ConsumerSessionAwareMessageListener implements SessionAwareMessageListener {
    
    
    private Destination destination;
    
    public void onMessage(TextMessage message, Session session) throws JMSException {
    
    
        System.out.println("收到一条消息");
        System.out.println("消息内容是:" + message.getText());
        
        MessageProducer producer = session.createProducer(destination);
        Message textMessage = session.createTextMessage("ConsumerSessionAwareMessageListener。。。");
        producer.send(textMessage);
    }
    
    public Destination getDestination() {
    
    
        returndestination;
    }
    
    public void setDestination(Destination destination) {
    
    
        this.destination = destination;
    }
}

Description: A SessionAwareMessageListener is defined. After receiving a message in this Listener, use the corresponding Session to create a producer to the destination and the corresponding message, and then use the created producer to send the corresponding message.


MessageListenerAdapter

The MessageListenerAdapter class implements the MessageListener interface and the SessionAwareMessageListener interface. Its main function is to perform type conversion on the received message, and then hand it over to an ordinary Java class for processing through reflection.

  • MessageListenerAdapter will convert the received message as follows:

    • Convert TextMessage to String object
    • Convert BytesMessage to byte array
    • Convert MapMessage to Map object
    • ObjectMessage is converted to the corresponding Serializable object

    Code example:

    // 目标处理器类
    public class ConsumerListener {
          
            
        public void handleMessage(String message) {
          
            
            System.out.println("ConsumerListener通过handleMessage接收到一个纯文本消息,消息内容是:" + message);  
        }  
        public void receiveMessage(String message) {
          
            
            System.out.println("ConsumerListener通过receiveMessage接收到一个纯文本消息,消息内容是:" + message);  
        }  
    }  
    
    <!-- 消息监听适配器 -->  
    <bean id="messageListenerAdapter" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">  
        <property name="delegate">  
            <bean class="com.tiantian.springintejms.listener.ConsumerListener"/>  
        </property>  
        <property name="defaultListenerMethod" value="receiveMessage"/>  
    </bean>  
    
    <!-- 消息监听适配器对应的监听容器 -->  
    <bean id="messageListenerAdapterContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">  
        <property name="connectionFactory" ref="connectionFactory"/>  
        <property name="destination" ref="adapterQueue"/>  
        <!-- 使用MessageListenerAdapter来作为消息监听器 -->  
        <property name="messageListener" ref="messageListenerAdapter"/>
    </bean>  
    

    Notice:

    • MessageListenerAdapter will perform a type conversion on the received message, and then use reflection to hand it over to the real target processor: an ordinary Java class (ConsumerListener) for processing.

      If the real target processor is a MessageListener or a SessionAwareMessageListener, then Spring will directly use the received Message object as a parameter to call their onMessage method instead of using reflection to call.

      Therefore, when defining a MessageListenerAdapter, it is necessary to specify such a target class for it. This target class can be specified by the constructor parameter of MessageListenerAdapter, or by its delegate attribute.

  • Another main function of MessageListenerAdapter is to automatically send return messages through the handleMessage method injected by MessageListenerAdapter.

    When the return value of the method used to process the received message (handleMessage by default) is not empty (null or void), Spring will automatically encapsulate it as a JMS Message, and then automatically reply. There are two main ways to specify the address to which this reply message will be sent:

    • You can specify the destination of the reply message corresponding to the message through the setJMSReplyTo method of the sent Message
    • Specify by defaultResponseDestination property of MessageListenerAdapter

basic use

rely

<!-- jms -->
<dependency>
    <groupId>javax.jms</groupId>
    <artifactId>javax.jms-api</artifactId>
</dependency>
<!-- spring jms -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jms</artifactId>
</dependency>

<!-- tonglinkMq jms api -->
<dependency>
    <groupId>com.tongtech.tlq</groupId>
    <artifactId>TongJMS-without-atomikos</artifactId>
    <version>8.1.0-SNAPSHOT</version>
</dependency>

SpringBoot integrates jms

jms configuration class

import org.springframework.jms.connection.CachingConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter;
import org.springframework.jms.core.JmsOperations;
import org.springframework.jms.core.JmsTemplate;
import org.tongtech.tmqi.ConnectionFactory;

@EnableJms	// 声明对 JMS 注解的支持
@Configuration
public class TestCreator {
    
    
    private String host;
    private Integer port;
    private String queueManager;
    private String channel;
    private String username;
    private String password;
    private int ccsid;
    private String queueName;
    private long receiveTimeout;
    
	// 配置连接工厂(tonglinkMq)
    @Bean
    public ConnectionFactory connectionFactory() throws JMSException {
    
    
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setProperty("tmqiAddressList", "tlq://127.0.0.1:10024");
        connectionFactory.setProperty("tmqiDefaultUsername", "admin");
        connectionFactory.setProperty("tmqiDefaultPassword", "123456");
        return connectionFactory;
    }
    
	// 配置缓存连接工厂 不配置该类则每次与MQ交互都需要重新创建连接,大幅降低速度。
    @Bean
    @Primary
    public CachingConnectionFactory cachingConnectionFactory(ConnectionFactory connectionFactory) {
    
    
        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
        cachingConnectionFactory.setTargetConnectionFactory(connectionFactory);
        cachingConnectionFactory.setSessionCacheSize(500);
        cachingConnectionFactory.setReconnectOnException(true);
        return cachingConnectionFactory;
    }
    
	// 配置DefaultJmsListenerContainerFactory, 用@JmsListener注解来监听队列消息时,尤其存在多个监听的时候,通过实例化配置DefaultJmsListenerContainerFactory来控制消息分发
    @Bean(name = "jmsQueueListenerCF")
    public DefaultJmsListenerContainerFactory jmsQueueListenerContainerFactory(CachingConnectionFactory cachingConnectionFactory) {
    
    
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(cachingConnectionFactory);
        // 设置连接数。如果对消息消费有顺序要求,这里建议设置为"1-1"
        // 注:使用同一个监听工厂类监听多个队列时,连接数需大于等于监听队列数
        factory.setConcurrency("3-10");	// 下限-上限
        // 重连间隔时间
        factory.setRecoveryInterval(1000L);
        
        // factory.setPubSubDomain(true);	// 支持发布订阅功能(topic)
        // factory.setConcurrency("1"); 	// topic 模式,并发必须设置为1,不然一条消息可能会被消费多次
        
        return factory;
    }
    
	// 配置JMS模板,实例化jmsTemplate后,可以在方法中通过@autowired的方式注入模板,用方法调用发送/接收消息
    // 注:如果只是接收消息,可以不配置此步
    @Bean
    public JmsTemplate jmsQueueTemplate(CachingConnectionFactory cachingConnectionFactory) {
    
    
        JmsTemplate jmsTemplate = new JmsTemplate(cachingConnectionFactory);
        jmsTemplate.setReceiveTimeout(receiveTimeout); // 设置超时时间
        
        // jmsTemplate.setPubSubDomain(true);	// 开启发布订阅功能(topic)
        
        return jmsTemplate;
    }
}

Send a message

public class jmsUtil {
    
    
    @Autowired
    private JmsTemplate jmsQueueTemplate;

    /**
     * 发送原始消息 Message
     */
    public void send(){
    
    
        jmsQueueTemplate.send("queue1", new MessageCreator() {
    
    
            @Override
            public Message createMessage(Session session) throws JMSException {
    
    
                return session.createTextMessage("我是原始消息");
            }
        });
    }
    
    /**
     * 发送消息自动转换成原始消息
     * 注:关于消息转换,还可以通过实现MessageConverter接口来自定义转换内容
     */
    public void convertAndSend(){
    
    
        jmsQueueTemplate.convertAndSend("queue1", "我是自动转换的消息");
    }
}

Listen to receive messages

Use annotations @JmsListenerto set the monitoring method

@Slf4j
@Component
// 此处继承MessageListenerAdapter非必需。但若只使用@JmsListener注解监听,可能会出现监听消息获取不及时或者获取不到消息的情况,加上继承MessageListenerAdapter后便不会出现
public class MdxpMessageListener extends MessageListenerAdapter {
    
    
    
    /**
     * 消息队列监听器
     * destination 队列地址,此处使用静态变量,支持配置化详见下文
     * containerFactory 监听器容器工厂(包含配置源), 若存在2个以上的监听容器工厂,需进行指定
     */
    @Override
    @JmsListener(destination = "TEST_QUEUE",containerFactory = "jmsQueueListenerCF")
    public void onMessage(Message message) {
    
    
        // JmsListener收到消息后,会自动封装成自己特有的数据格式,需要自行根据消息类型解析原始消息
        String msgText = ""; 
        double d = 0; 
        try {
    
     
            if (msg instanceof TextMessage) {
    
        
                msgText = ((TextMessage) msg).getText(); 
            } else if (msg instanceof StreamMessage) {
    
        
                msgText = ((StreamMessage) msg).readString();    
                d = ((StreamMessage) msg).readDouble();    
            } else if (msg instanceof BytesMessage) {
    
        
                byte[] block = new byte[1024];    
                ((BytesMessage) msg).readBytes(block);    
                msgText = String.valueOf(block);    
            } else if (msg instanceof MapMessage) {
    
        
                msgText = ((MapMessage) msg).getString("name");    
            }
            log.info("接收消息={}", msgText);
        } catch (JMSException e) {
    
     
            log.error("消息接收异常!", e);
        }
    }
    
    @JmsListener(destination = "TEST_QUEUE2",containerFactory = "jmsQueueListenerCF")
    // @Payload是消费者接受生产者发送的队列消息,将队列中的json字符串变成对象的注解,注意填充类需要实现序列化接口
    public void messageListener2(@payload User user){
    
    
        log.info("message={}", user)
    }
}

@JmsListener annotation destination supports configuration

Inject configuration read class

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
/**
 * 队列名称配置
 * 这里切记要@Data,或手动set和get
 */
@Component
@Data
public class QueueNameConfig {
    
    
 
    @Value("${ibmmq.queue-test}")
    private String testQueue;
 
}

Queue listener class

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.listener.adapter.MessageListenerAdapter;
import org.springframework.stereotype.Component;
import javax.jms.Message;
import javax.jms.TextMessage;
 
/**
 * MQ消费者
 */
@Component
@Slf4j
public class ReceiveMessage extends MessageListenerAdapter {
    
    
 
    /**
     * destination:监听的队列名称,使用SpEL表达式写入
     * containerFactory:监听的工厂类,为配置类中所配置的名字
     */
    @Override
    @JmsListener(destination = "#{@queueNameConfig.testQueue}", containerFactory = "jmsListenerContainerFactory")
    public void onMessage(Message message) {
    
    
        TextMessage textMessage = (TextMessage) message;  //转换成文本消息
        try {
    
    
            String text = textMessage.getText();
            log.info("接收信息:{}", text);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
}

javax native jms

public class jmstest {
    
    
    public static void main(String[] args) throws Exception {
    
     
        // 配置工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setProperty("tmqiAddressList", "tlq://127.0.0.1:10024");
        connectionFactory.setProperty("tmqiDefaultUsername", "admin");
        connectionFactory.setProperty("tmqiDefaultPassword", "123456");
        // 获取连接和会话
        Connection mqConn = connectionFactory.createConnection(); 
        // 创建会话。CLIENT_ACKNOWLEDGE:手动应答,AUTO_ACKNOWLEDGE:自动应答
        Session mqSession = mqConn.createQueueSession(false, Session.CLIENT_ACKNOWLEDGE); 
        // 创建队列
        Queue queuemq = Session.createQueue(queueName);
        // 获取消费者
        MessageConsumer consumer = mqSession.createConsumer(mqSession.createQueue(queueName)); 
        // 设置监听器
        consumer.setMessageListener(new MessageListener() {
    
     
            public void onMessage(Message msg) {
    
     
                // JmsListener收到消息后,会自动封装成自己特有的数据格式,需要自行根据消息类型解析原始消息
                String msgText = ""; 
                double d = 0; 
                try {
    
     
                    if (msg instanceof TextMessage) {
    
    
                        msgText = ((TextMessage) msg).getText(); 
                    } else if (msg instanceof StreamMessage) {
    
        
                        msgText = ((StreamMessage) msg).readString();    
                        d = ((StreamMessage) msg).readDouble();    
                    } else if (msg instanceof BytesMessage) {
    
        
                        byte[] block = new byte[1024];    
                        ((BytesMessage) msg).readBytes(block);    
                        msgText = String.valueOf(block);    
                    } else if (msg instanceof MapMessage) {
    
        
                        msgText = ((MapMessage) msg).getString("name");    
                    }
                    log.info("接收消息={}", msgText);
                    // 手动应答
                	textMessage.acknowledge();
                } catch (JMSException e) {
    
     
                    log.error("消息接收异常!", e);
                }
            }
        }); 
        // 启动连接
        mqConn.start(); 
    }
    
    // 获取生产者
    MessageProducer producer = mqSession.createProducer(mqSession.createQueue(queueName)); 
    
    // topic(广播)模式
    // Topic topic = Session.createTopic(queueName);
    // MessageProducer producer = mqSession.createProducer(topic); 
    
    producer.setDeliveryMode(DeliveryMOde.NON_PERSISTENT);
    producer.send(mqSession.createTexttMessage("这是一条消息"));
    
    // 关闭资源
    producer.close();
    // 断开连接
    connection.close();
} 

Guess you like

Origin blog.csdn.net/footless_bird/article/details/132272018