MQ系列02:ActiveMQ

ActiveMQ

1.关于ActiveMQ

ActiveMQ是Apache 出品的一款开源消息中间件,它实现了JMS标准。

2.关于JMS

JMS API是一个消息服务规范,用于应用程序之间的通信。

3.关于消息模型

  • 3.1 点对点(Point-To-Point)

    使用队列(Queue)作为消息通信载体,即生产者与消费者模式。一条消息只能被一个消费者使用,未被消费的消息在队列中保留直到被消费或超时

  • 3.2 发布/订阅(Publisher-Subscriber)

    使用主题(Topic)作为消息通信载体,类似于广播模式。发布者发布一条消息,该消息通过主题传递给所有在线的订阅者。普通订阅者在发布消息之后上线或者是发布消息之后订阅,将会错过这条消息;持久订阅者不会出现这个问题。

4.消息确认方式

  • 4.1 自动确认模式,不需客户端进行确认

    Session.AUTO_ACKNOWLEDGE 1

  • 4.2 客户端进行确认

    Session.CLIENT_ACKNOWLEDGE 2
    如果创建Session时,acknowledgeMode = Session.CLIENT_ACKNOWLEDGE;那么后续的代码需要message.acknowledge()才能确认消息。

  • 4.3 允许副本的确认模式

    Session.DUPS_OK_ACKNOWLEDGE 3

  • 4.4 支持实务,需要提交实务

    Session.SESSION_TRANSACTED 0
    创建Session,需要设置transacted = true,acknowledgeMode=Session.SESSION_TRANSACTED;那么后续代码需要session.commit()才能提交。

5.控制台的ActiveMQ

  • 下载ActiveMQ(activemq),选择Windows
  • 解压
  • 运行:32位在[ActiveMQ_install_dir]/bin/Win32/,双击activemq.bat;64位在[ActiveMQ_install_dir]/bin/Win64/,双击activemq.bat
  • 5.1 引入依赖
      <dependency>
          <groupId>javax.jms</groupId>
          <artifactId>javax.jms-api</artifactId>
          <version>2.0.1</version>
      </dependency>
      <dependency>
          <groupId>org.apache.activemq</groupId>
          <artifactId>activemq-all</artifactId>
          <version>5.15.9</version>
      </dependency>
    
  • 5.2 编写常量类 Constant.java
    import org.apache.activemq.ActiveMQConnection;
    public interface Constant {
        //用户名
        String USERNAME = ActiveMQConnection.DEFAULT_USER;
        //密码
        String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;
        //ActiveMQ地址
        String BROKER_URL = ActiveMQConnection.DEFAULT_BROKER_URL;
        Integer MESSAGE_COUNT = 10;
    }
    
  • 5.3 点对点模型
    • 5.3.1 生产者 PointToPointProducer.java
    import cn.techpan.happiness.mq.active_mq.console.Constant;
    import org.apache.activemq.ActiveMQConnectionFactory;
    import javax.jms.Connection;
    import javax.jms.ConnectionFactory;
    import javax.jms.JMSException;
    import javax.jms.MessageProducer;
    import javax.jms.Queue;
    import javax.jms.Session;
    import javax.jms.TextMessage;
    import java.util.Objects;
    /**
     * ActiveMQ P2P 生产端
     */
    public class PointToPointProducer {
        public static void main(String[] args) {
            Connection connection = null;
            try {
                //1获取ActiveMQ的连接工厂
                ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(Constant.USERNAME, Constant.PASSWORD, Constant.BROKER_URL);
                //2使用连接工厂创建连接
                connection = connectionFactory.createConnection();
                //3开启连接
                connection.start();
                //4创建session对象
                //  参数一:是否支持事务
                //  参数二:消息确认方式
                Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
                //5创建队列
                Queue queue = session.createQueue("queue");
                //6创建消息生产者(MessageProducer)
                MessageProducer messageProducer = session.createProducer(queue);
                //7发送消息
                sendMessage(session, messageProducer);
            } catch (JMSException e) {
                e.printStackTrace();
            } finally {
                try {
                    Objects.requireNonNull(connection).close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }
        private static void sendMessage(Session session, MessageProducer messageProducer) {
            for (int i = 0; i < Constant.MESSAGE_COUNT; i++) {
                try {
                    TextMessage textMessage = session.createTextMessage();
                    textMessage.setText("PointToPointProducer 发送消息: " + i);
                    System.out.println("PointToPointProducer 发送消息: " + i);
                    messageProducer.send(textMessage);
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    • 5.3.2 消费者 PointToPointCustomer.java
    import cn.techpan.happiness.mq.active_mq.console.Constant;
    import org.apache.activemq.ActiveMQConnectionFactory;
    import javax.jms.Connection;
    import javax.jms.ConnectionFactory;
    import javax.jms.JMSException;
    import javax.jms.MessageConsumer;
    import javax.jms.Queue;
    import javax.jms.Session;
    import javax.jms.TextMessage;
    import java.util.Objects;
    /**
     * ActiveMQ P2P 消费端
     */
    public class PointToPointCustomer {
        public static void main(String[] args) {
            Connection connection = null;
            try {
                ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(Constant.USERNAME, Constant.PASSWORD, Constant.BROKER_URL);
                connection = connectionFactory.createConnection();
                connection.start();
                Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
                Queue queue = session.createQueue("queue");
                //创建消息消费者(MessageConsumer)
                MessageConsumer messageConsumer = session.createConsumer(queue);
                while (true) {//同步接收消息(异步可使用MessageListener)
                    //同步接收:主线程阻塞式等待下一个消息的到来,可以设置timeout,超时则返回null。
                    //同步接收又称为阻塞式接收
                    //同步接收,是在获取MessageConsumer实例之后,调用以下的API:
                    //receive():Message
                    //      获取下一个消息。这个调用将导致无限期的阻塞,直到有新的消息产生。
                    //receive(long timeout):Message
                    //      获取下一个消息。这个调用可能导致一段时间的阻塞,直到超时或者有新的消息产生。超时则返回null。
                    //receiveNoWait():Message
                    //      获取下一个消息。这个调用不会导致阻塞,如果没有下一个消息,直接返回null。
                    TextMessage textMessage = (TextMessage) messageConsumer.receive(10000);
                    if (textMessage != null) {
                        System.out.println("PointToPointCustomer 接收消息: " + textMessage.getText());
                    } else {
                        break;
                    }
                }
            } catch (JMSException e) {
                e.printStackTrace();
            } finally {
                try {
                    Objects.requireNonNull(connection).close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
    
            }
        }
    }
    
  • 5.4 发布/订阅模型
    • 5.4.1 生产者 PublishSubscribeProducer.java
    import cn.techpan.happiness.mq.active_mq.console.Constant;
    import org.apache.activemq.ActiveMQConnectionFactory;
    import javax.jms.Connection;
    import javax.jms.ConnectionFactory;
    import javax.jms.DeliveryMode;
    import javax.jms.JMSException;
    import javax.jms.MessageProducer;
    import javax.jms.Session;
    import javax.jms.TextMessage;
    import javax.jms.Topic;
    import java.util.Objects;
    /**
     * ActiveMQ Pub-Sub 生产者
     */
    public class PublishSubscribeProducer {
    
        public static void main(String[] args) {
            Connection connection = null;
            try {
                ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(Constant.USERNAME, Constant.PASSWORD, Constant.BROKER_URL);
                connection = connectionFactory.createConnection();
                connection.start();
                Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
                //创建主题
                Topic topic = session.createTopic("topic");
                //创建生产者
                MessageProducer messageProducer = session.createProducer(topic);  
                //发送消息
                sendMessage(session, messageProducer);
            } catch (JMSException e) {
                e.printStackTrace();
            } finally {
                try {
                    Objects.requireNonNull(connection).close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }
        private static void sendMessage(Session session, MessageProducer messageProducer) throws JMSException {
            for (int i = 0; i < Constant.MESSAGE_COUNT; i++) {
                TextMessage textMessage = session.createTextMessage();
                String message = "PublishSubscribeProducer Send Message:" + i;
                System.out.println(message);
                textMessage.setText(message);
                messageProducer.send(textMessage);
            }
        }
    }
    
    • 5.4.2 消费者 PublishSubscribeCustomer.java
    import cn.techpan.happiness.mq.active_mq.console.Constant;
    import org.apache.activemq.ActiveMQConnectionFactory;
    import javax.jms.Connection;
    import javax.jms.ConnectionFactory;
    import javax.jms.JMSException;
    import javax.jms.MessageConsumer;
    import javax.jms.Session;
    import javax.jms.TextMessage;
    import javax.jms.Topic;
    import java.util.Objects;
    /**
     * ActiveMQ Pub-Sub 消费者
     */
    public class PublishSubscribeCustomer {
        public static void main(String[] args) {
            Connection connection = null;
            try {
                ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(Constant.USERNAME, Constant.PASSWORD, Constant.BROKER_URL);
                connection = connectionFactory.createConnection();
                connection.start();
                //创建session,不支持事务,自动确认
                Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
                //创建topic
                Topic topic = session.createTopic("topic");
                //创建普通订阅者
                MessageConsumer messageConsumer = session.createConsumer(topic);
                while (true) {
                    //同步获取消息,等待超时时间为10000ms。
                    TextMessage textMessage = (TextMessage) messageConsumer.receive(10000);
                    if (textMessage != null) {
                        System.out.println("Message Arrived: " + textMessage.getText());
                    } else {
                        break;
                    }
                }
            } catch (JMSException e) {
                e.printStackTrace();
            } finally {
                try {
                    Objects.requireNonNull(connection).close();
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

6.与Spring集成

  • 6.1 引入依赖
    <dependency>
        <groupId>javax.jms</groupId>
        <artifactId>javax.jms-api</artifactId>
        <version>2.0.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-all</artifactId>
        <version>5.15.9</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-messaging</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jms</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.8</version>
        <scope>provided</scope>
    </dependency>
    
  • 6.2 消息监听器
    • 6.2.1 QueueMessageListener.java
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.stereotype.Component;
      import javax.jms.JMSException;
      import javax.jms.Message;
      import javax.jms.MessageListener;
      import javax.jms.TextMessage;
      /**
       * 配置消息监听器
       */
      @Slf4j
      @Component
      public class QueueMessageListener implements MessageListener {
          @Override
          public void onMessage(Message message) {
              TextMessage textMessage = (TextMessage) message;
              try {
                  if (textMessage != null) {
                      log.info("message come from: {}, content: {}", message.getJMSDestination(), textMessage.getText());
                  }
              } catch (JMSException e) {
                  e.printStackTrace();
              }
          }
      }
      
    • 6.2.2 TopicMessageListener.java
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.stereotype.Component;
      import javax.jms.JMSException;
      import javax.jms.Message;
      import javax.jms.MessageListener;
      import javax.jms.TextMessage;
      /**
       * 配置监听器
       */
      @Slf4j
      @Component
      public class TopicMessageListener implements MessageListener {
          @Override
          public void onMessage(Message message) {
              TextMessage textMessage = (TextMessage) message;
              try {
                  if (textMessage != null) {
                      log.info("topic01: message come from: {}, content: {}", message.getJMSDestination(), textMessage.getText());
                  }
              } catch (JMSException e) {
                  e.printStackTrace();
              }
          }
      }
      
  • 6.3 消息生产者
    • 6.3.1 QueueProducer.java
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.jms.core.JmsTemplate;
      import org.springframework.stereotype.Component;
      import javax.jms.Destination;
      /**
       * 消息发送
       */
      @Slf4j
      @Component
      public class QueueProducer {
          private JmsTemplate queueJmsTemplate;
          public QueueProducer(JmsTemplate queueJmsTemplate) {
              this.queueJmsTemplate = queueJmsTemplate;
          }
          public void sendMessageToDefaultDestination(String message) {
              Destination destination = queueJmsTemplate.getDefaultDestination();
              log.info("a message has send to {}",  destination);
              queueJmsTemplate.send(session -> session.createTextMessage(message));
          }
          public void sendMessage(Destination destination, String message) {
              log.info("a message has send to {}",  destination);
              queueJmsTemplate.send(destination, session -> session.createTextMessage(message));
          }
      }
      
    • 6.3.2 TopicProducer.java
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.beans.factory.annotation.Qualifier;
      import org.springframework.jms.core.JmsTemplate;
      import org.springframework.stereotype.Component;
      import javax.jms.Destination;
      /**
       * 消息发送
       */
      @Slf4j
      @Component
      public class TopicProducer {
          private JmsTemplate jmsTemplate;
          public TopicProducer(@Qualifier("topicJmsTemplate") JmsTemplate jmsTemplate) {
              this.jmsTemplate = jmsTemplate;
          }
          public void sendMessageToDefaultDestination(String message) {
              Destination destination = jmsTemplate.getDefaultDestination();
              log.info("a message has publish to {}",  destination);
              jmsTemplate.send(session -> session.createTextMessage(message));
          }
      }
      
  • 6.4 配置文件 SpringConfig.Java
    import lombok.extern.slf4j.Slf4j;
    import org.apache.activemq.command.ActiveMQQueue;
    import org.apache.activemq.command.ActiveMQTopic;
    import org.apache.activemq.spring.ActiveMQConnectionFactory;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.jms.connection.CachingConnectionFactory;
    import org.springframework.jms.core.JmsTemplate;
    import org.springframework.jms.listener.DefaultMessageListenerContainer;
    import javax.jms.Destination;
    import javax.jms.MessageListener;
    
    @Slf4j
    @Configuration
    @ComponentScan(value = "cn.techpan.happiness.mq.active_mq.spring.*")
    public class SpringConfig {
        /**
         * 创建ActiveMQ连接工厂,实现客户端与ActiveMQ服务提供者连接
         * @return ActiveMQConnectionFactory
         */
        @Bean
        public ActiveMQConnectionFactory getActiveMQConnectionFactory() {
            ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
            activeMQConnectionFactory.setUserName(ActiveMQConnectionFactory.DEFAULT_USER);
            activeMQConnectionFactory.setPassword(ActiveMQConnectionFactory.DEFAULT_PASSWORD);
            activeMQConnectionFactory.setBrokerURL(ActiveMQConnectionFactory.DEFAULT_BROKER_URL);
            return activeMQConnectionFactory;
        }
        /**
         * 创建Spring Caching连接工厂,并注入ActiveMQ连接工厂,用于对ActiveMQ连接工厂的管理
         * @param activeMQConnectionFactory ActiveMQ连接工厂
         * @return CachingConnectionFactory
         */
        @Bean
        public CachingConnectionFactory getCachingConnectionFactory(ActiveMQConnectionFactory activeMQConnectionFactory) {
            CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();
            cachingConnectionFactory.setTargetConnectionFactory(activeMQConnectionFactory);
            //设置缓存大小
            cachingConnectionFactory.setSessionCacheSize(100);
            return cachingConnectionFactory;
        }
        /**
         * 创建Destination
         * @return Queue
         */
        @Bean
        public ActiveMQQueue activeMQQueue() {
            return new ActiveMQQueue("spring-queue");
        }
        /**
         * 创建JmsTemplate(queue)
         * @param cachingConnectionFactory 连接工厂
         * @param activeMQQueue Destination
         * @return JmsTemplate
         */
        @Bean("queueJmsTemplate")
        public JmsTemplate getQueueJmsTemplate(CachingConnectionFactory cachingConnectionFactory,
                                               Destination activeMQQueue) {
            JmsTemplate queueJmsTemplate = new JmsTemplate(cachingConnectionFactory);
            //MQ 模型。 true - pub/sub     false - p2p
            queueJmsTemplate.setPubSubDomain(false);
            queueJmsTemplate.setDefaultDestination(activeMQQueue);
            return queueJmsTemplate;
        }
        /**
         * 消息监听容器 负责处理消息接收的注册、事务管理、资源获取与释放和异常转换等
         * @param cachingConnectionFactory 连接工厂
         * @param activeMQQueue Destination
         * @param queueMessageListener 消息监听器
         * @return DefaultMessageListenerContainer
         */
        @Bean
        public DefaultMessageListenerContainer queueMessageListenerContainer(CachingConnectionFactory cachingConnectionFactory,
                                                                             Destination activeMQQueue,
                                                                             MessageListener queueMessageListener) {
            DefaultMessageListenerContainer queueMessageListenerContainer = new DefaultMessageListenerContainer();
            //注入ConnectionFactory
            queueMessageListenerContainer.setConnectionFactory(cachingConnectionFactory);
            //设置Destination
            queueMessageListenerContainer.setDestination(activeMQQueue);
            //注册MessageListener
            queueMessageListenerContainer.setMessageListener(queueMessageListener);
            return queueMessageListenerContainer;
        }
        /**
         * 创建Destination
         * @return ActiveMQTopic
         */
        @Bean
        public ActiveMQTopic activeMQTopic() {
            return new ActiveMQTopic("spring-topic");
        }
        /**
         * 创建JmsTemplate(Topic)
         * @param cachingConnectionFactory 连接工厂
         * @param activeMQTopic Destination
         * @return JmsTemplate
         */
        @Bean("topicJmsTemplate")
        public JmsTemplate getTopicJmsTemplate(CachingConnectionFactory cachingConnectionFactory,
                                               Destination activeMQTopic) {
            JmsTemplate topicJmsTemplate = new JmsTemplate(cachingConnectionFactory);
            topicJmsTemplate.setPubSubDomain(true);
            topicJmsTemplate.setDefaultDestination(activeMQTopic);
            return topicJmsTemplate;
        }
        /**
         * 消息监听容器
         * @param cachingConnectionFactory 连接工厂
         * @param activeMQTopic Destination
         * @param topicMessageListener 消息监听器
         * @return DefaultMessageListenerContainer
         */
        @Bean
        public DefaultMessageListenerContainer topicMessageListenerContainer(CachingConnectionFactory cachingConnectionFactory,
                                                                             Destination activeMQTopic,
                                                                             MessageListener topicMessageListener) {
            DefaultMessageListenerContainer topicMessageListenerContainer = new DefaultMessageListenerContainer();
            topicMessageListenerContainer.setConnectionFactory(cachingConnectionFactory);
            topicMessageListenerContainer.setDestination(activeMQTopic);
            topicMessageListenerContainer.setMessageListener(topicMessageListener);
            return topicMessageListenerContainer;
        }  
    }
    
  • 6.5 消息发送服务
    • 6.5.1 QueueMessageService.java
      import cn.techpan.happiness.mq.active_mq.spring.jms.producer.QueueProducer;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.stereotype.Service;
      @Slf4j
      @Service
      public class QueueMessageService {
          //注入消息生产者
          private QueueProducer queueProducer;
          public QueueMessageService(QueueProducer queueProducer) {
              this.queueProducer = queueProducer;
          }
          public void sendMessage() {
              log.info("ready for sending message");
              log.info("producer is sending messages!");
              for (int i = 0; i < 10; i++) {
                  String message = "message NO." + i;
                  queueProducer.sendMessageToDefaultDestination(message);
              }
              log.info("message send over");
          }
      }
      
    • 6.5.2 TopicMessageService.java
      import cn.techpan.happiness.mq.active_mq.spring.jms.producer.TopicProducer;
      import lombok.extern.slf4j.Slf4j;
      import org.springframework.stereotype.Service;
      @Slf4j
      @Service
      public class TopicMessageService {
          //注入消息生产者
          private TopicProducer topicProducer;
          public TopicMessageService(TopicProducer topicProducer) {
              this.topicProducer = topicProducer;
          }
          public void sendMessage() {
              log.info("ready for publishing message");
              log.info("producer is publishing messages!");
              for (int i = 0; i < 10; i++) {
                  String message = "message NO." + i;
                  topicProducer.sendMessageToDefaultDestination(message);
              }
              log.info("message publish over");
          }
      }
      
  • 6.6 主运行类 SpringActiveMQApplication
    import cn.techpan.happiness.mq.active_mq.spring.config.SpringConfig;
    import cn.techpan.happiness.mq.active_mq.spring.message.QueueMessageService;
    import cn.techpan.happiness.mq.active_mq.spring.message.TopicMessageService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    public class SpringActiveMQApplication {
        public static void main(String[] args) {
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
            QueueMessageService queueMessageService = applicationContext.getBean(QueueMessageService.class);
            queueMessageService.sendMessage();
            TopicMessageService topicMessageService = applicationContext.getBean(TopicMessageService.class);
            topicMessageService.sendMessage();
        }
    }
    
  • 7 参考文档
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值