好记性,不如烂笔头

万物寻其根,通其堵,便能解其困。
  博客园  :: 新随笔  :: 管理

ActiveMQ 笔记

Posted on 2024-03-31 21:41    阅读(3)  评论(0)    收藏  举报

如要学习请跳转原文,原文地址:ActiveMQ学习总结 - 吊儿郎当小少年 - 博客园 (cnblogs.com)

使用场景比对:常见的消息中间件对比(详细版)_常用消息中间件有什么区别-CSDN博客

 

点对点消息传递

  • 每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在消息队列中)
  • 发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列
  • 接收者在成功接收消息之后需向队列应答成功,如果你希望发送的每个消息都应该被成功处理的话,那么你需要P2P模式。
package com.namejr.Web_JRBackstage.serviceImpl;

import com.namejr.Web_JRBackstage.base.PublicUtilHelper;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.stereotype.Component;

import javax.jms.*;

@Component
public class ActivemqServiceImpl {
    private String tcpPathUrl="tcp://localhost:61616";
    private String queueName="testQueue1";

    public void testRunning(){
        Runnable tempTask1= () -> {
            try{
                startServiceRunByPointToPoint();
            }catch (Exception err){
                err.printStackTrace();
            }
        };
        PublicUtilHelper.tempExecutor.execute(tempTask1);
        Runnable tempTask2= () -> {
            try{
                startClientRunByPointToPoint();
            }catch (Exception err){
                err.printStackTrace();
            }
        };
        PublicUtilHelper.tempExecutor.execute(tempTask2);
    }

    // 模拟消息发送者--点对点
    private void startServiceRunByPointToPoint() throws Exception{
        // 第一步:创建ConnectionFactory对象,需要指定服务端ip及端口号。
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(tcpPathUrl);
        // 第二步:使用ConnectionFactory对象创建一个Connection对象。
        Connection connection = factory.createConnection();
        // 第三步:开启连接,调用Connection对象的start方法。
        connection.start();
        // 第四步:使用Connection对象创建一个Session对象。
        //第一个参数:是否开启事务。true:开启事务,第二个参数忽略。
        //第二个参数:当第一个参数为false时,才有意义。消息的应答模式。1、自动应答2、手动应答。
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 第五步:使用Session对象创建一个Destination对象(topic、queue),此处创建一个Queue对象。
        //参数:队列的名称。
        Destination destination = session.createQueue(queueName);
        // 第六步:使用Session对象创建一个Producer对象。
        MessageProducer producer = session.createProducer(destination);
     /** 设置持久化
     * PERSISTENT:指示JMS provider持久保存消息,以保证消息不会因为JMS provider的失败而丢失
     * NON_PERSISTENT:不要求JMS provider持久保存消息 */
     //producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
     int runIndex=20;  // 模拟不断发送消息
        do{
            // 第七步:创建一个Message对象,创建一个TextMessage对象。
            TextMessage textMessage = session.createTextMessage("模拟消息发送:发送到第" + runIndex+"条消息。");
            // 第八步:使用Producer对象发送消息。
            producer.send(textMessage);
            runIndex--;
            Thread.sleep(2000);
        }while (runIndex>0);
        // 第九步:关闭资源。
        producer.close();
        session.close();
        connection.close();
    }

    // 模拟消息消费者--点对点
    private void startClientRunByPointToPoint() throws Exception{
        // 第一步:创建一个ConnectionFactory对象。
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(tcpPathUrl);
        // 第二步:从ConnectionFactory对象中获得一个Connection对象。
        Connection connection = factory.createConnection();
        // 第三步:开启连接。调用Connection对象的start方法。
        connection.start();
        // 第四步:使用Connection对象创建一个Session对象。
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 第五步:使用Session对象创建一个Destination对象。和发送端保持一致queue,并且队列的名称一致。
        Queue queue = session.createQueue(queueName);
        // 第六步:使用Session对象创建一个Consumer对象。
        MessageConsumer consumer = session.createConsumer(queue);
        // 第七步:接收消息。
        consumer.setMessageListener(message -> {
            TextMessage textMessage = (TextMessage) message;
            boolean closeFlag=false;
            try {
                //取消息的内容
                String text = textMessage.getText();
                System.out.println("接收到消息:"+text);
                if(text.contains("发送到第5条消息。")){
                    closeFlag=true;
                }
            } catch (JMSException terr) {
                terr.printStackTrace();
            }
            finally {
                if(closeFlag){
                    // 第九步:关闭资源
                    try{
                        consumer.close();
                        session.close();
                        connection.close();
                    }catch (Exception terr){
                        terr.printStackTrace();
                    }
                }
            }
        });
    }
}

 

 发布/订阅消息传递

每个消息可以有多个消费者
  发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息,而且为了消费消息,订阅者必须保持运行的状态。为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。 如果你希望发送的消息可以不被做任何处理、或者被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用Pub/Sub模型
消息的消费
  在JMS中,消息的产生和消息是异步的。对于消费来说,JMS的消息者可以通过两种方式来消费消息。

  同步:订阅者或接收者调用receive方法来接收消息,receive方法在能够接收到消息之前(或超时之前)将一直阻塞。

  异步:订阅者或接收者可以注册为一个消息监听器。当消息到达之后,系统自动调用监听器的onMessage方法。

import com.namejr.Web_JRBackstage.base.PublicUtilHelper;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.stereotype.Component;

import javax.jms.*;
import java.util.Random;

@Component
public class ActivemqServiceImpl {
    private String tcpPathUrl="tcp://localhost:61616";
    private String queueName="testQueue1";
    private String topicName="testTopic1";

    public void testRunning(){
        Runnable tempTask1= () -> {
            try{
                startServiceRunByPublishAndSubscribe();
            }catch (Exception err){
                err.printStackTrace();
            }
        };
        PublicUtilHelper.tempExecutor.execute(tempTask1);
        Runnable tempTask2= () -> {
            try{
                startClientRunByPublishAndSubscribe1();
            }catch (Exception err){
                err.printStackTrace();
            }
        };
        PublicUtilHelper.tempExecutor.execute(tempTask2);
        Runnable tempTask3= () -> {
            try{
                startClientRunByPublishAndSubscribe2();
            }catch (Exception err){
                err.printStackTrace();
            }
        };
        PublicUtilHelper.tempExecutor.execute(tempTask3);
    }

    
    // 模拟消息发送者--发布订阅
    private void startServiceRunByPublishAndSubscribe() throws Exception{
        //第一步:创建ConnectionFactory对象,需要指定服务端ip及端口号。
        // brokerURL服务器的ip及端口号
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(tcpPathUrl);
        // 第二步:使用ConnectionFactory对象创建一个Connection对象。
        Connection connection = factory.createConnection();
        // 第三步:开启连接,调用Connection对象的start方法。
        connection.start();
        // 第四步:使用Connection对象创建一个Session对象。
        // 第一个参数:是否开启事务。true:开启事务,第二个参数忽略。
        // 第二个参数:当第一个参数为false时,才有意义。消息的应答模式。1、自动应答2、手动应答。
     /** 消息的签收情形分两种:
      带事务的session:如果session带有事务,并且事务成功提交,则消息被自动签收。如果事务回滚,则消息会被再次传送。
      不带事务的session:不带事务的session的签收方式,取决于session的配置。Activemq支持一下三种模式:
        Session.AUTO_ACKNOWLEDGE 消息自动签收
        Session.CLIENT_ACKNOWLEDGE 客戶端调用acknowledge方法手动签收。textMessage.acknowledge();//手动签收
        Session.DUPS_OK_ACKNOWLEDGE 不是必须签收,消息可能会重复发送。在第二次重新传送消息的时候,消息只有在被确认之后,才认为已经被成功地消费了。
      消息的成功消费通常包含三个阶段:客户接收消息、客户处理消息和消息被确认。 在事务性会话中,当一个事务被提交的时候,确认自动发生。在非事务性会话中,消息何时被确认取决于创建会话时的应答模式(acknowledgement mode)。
    */
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 第五步:使用Session对象创建一个Destination对象(topic、queue),此处创建一个topic对象。
        // 参数:话题的名称。
        Destination destination = session.createTopic(topicName);
        // 第六步:使用Session对象创建一个Producer对象。
        MessageProducer producer = session.createProducer(destination);
        int runIndex=20;  // 模拟不断发送消息
        do{
            // 第七步:创建一个Message对象,创建一个TextMessage对象。
            TextMessage textMessage = session.createTextMessage("模拟消息发送:发送到第" + runIndex+"条消息。");
            // 第八步:使用Producer对象发送消息。
            producer.send(textMessage);
            runIndex--;
            Thread.sleep(500);
        }while (runIndex>0);

        // 第九步:关闭资源。
        producer.close();
        session.close();
        connection.close();
    }

    // 模拟消息消费者--发布订阅
    private void startClientRunByPublishAndSubscribe1() throws Exception{
        //第一步:创建ConnectionFactory对象,需要指定服务端ip及端口号。
        // brokerURL服务器的ip及端口号
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(tcpPathUrl);
        // 第二步:使用ConnectionFactory对象创建一个Connection对象。
        Connection connection = factory.createConnection();
        // 第三步:开启连接,调用Connection对象的start方法。
        connection.start();
        //connection.setClientID(UUID.randomUUID().toString());  // 指定唯一id
        // 第四步:使用Connection对象创建一个Session对象。
        // 第一个参数:是否开启事务。true:开启事务,第二个参数忽略。
        // 第二个参数:当第一个参数为false时,才有意义。消息的应答模式。1、自动应答2、手动应答。
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 第五步:使用Session对象创建一个Destination对象。和发送端保持一致topic,并且话题的名称一致。
        Topic topic = session.createTopic(topicName);
        // 第六步:使用Session对象创建一个Consumer对象。
        MessageConsumer consumer = session.createConsumer(topic);
        // 第七步:接收消息。
        consumer.setMessageListener(message -> {
            TextMessage textMessage = (TextMessage) message;
            boolean closeFlag=false;
            try {
                //取消息的内容
                String text = textMessage.getText();
                System.out.println("第一个消费者接收到消息:"+text);
                if(text.contains("发送到第1条消息。")){
                    closeFlag=true;
                }
                Random tempRandom=new Random();
                int tempSleepVal=tempRandom.nextInt(2000);
                Thread.sleep(tempSleepVal);
            } catch (Exception terr) {
                terr.printStackTrace();
            }
            finally {
                if(closeFlag){
                    // 第九步:关闭资源
                    try{
                        consumer.close();
                        session.close();
                        connection.close();
                    }catch (Exception terr){
                        terr.printStackTrace();
                    }
                }
            }
        });
    }

    // 模拟消息消费者--发布订阅
    private void startClientRunByPublishAndSubscribe2() throws Exception{
        //第一步:创建ConnectionFactory对象,需要指定服务端ip及端口号。
        // brokerURL服务器的ip及端口号
        ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(tcpPathUrl);
        // 第二步:使用ConnectionFactory对象创建一个Connection对象。
        Connection connection = factory.createConnection();
        // 第三步:开启连接,调用Connection对象的start方法。
        connection.start();
        //connection.setClientID(UUID.randomUUID().toString());  // 指定唯一id
        // 第四步:使用Connection对象创建一个Session对象。
        // 第一个参数:是否开启事务。true:开启事务,第二个参数忽略。
        // 第二个参数:当第一个参数为false时,才有意义。消息的应答模式。1、自动应答2、手动应答。
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 第五步:使用Session对象创建一个Destination对象。和发送端保持一致topic,并且话题的名称一致。
        Topic topic = session.createTopic(topicName);
        // 第六步:使用Session对象创建一个Consumer对象。
        MessageConsumer consumer = session.createConsumer(topic);
        // 第七步:接收消息。
        consumer.setMessageListener(message -> {
            TextMessage textMessage = (TextMessage) message;
            boolean closeFlag=false;
            try {
                //取消息的内容
                String text = textMessage.getText();
                System.out.println("第二个消费者接收到消息:"+text);
                if(text.contains("发送到第1条消息。")){
                    closeFlag=true;
                }
                Random tempRandom=new Random();
                int tempSleepVal=tempRandom.nextInt(1500);
                Thread.sleep(tempSleepVal);
            } catch (Exception terr) {
                terr.printStackTrace();
            }
            finally {
                if(closeFlag){
                    // 第九步:关闭资源
                    try{
                        consumer.close();
                        session.close();
                        connection.close();
                    }catch (Exception terr){
                        terr.printStackTrace();
                    }
                }
            }
        });
    }
}

 

 

待续。。。