ActiveMQ Java API 入门

  • 与其他框架一样,ActiveMQ也有一套Java的操作api。

1. JMS编码总体规范

  • JMS,即Java消息服务,与MySQL一样,有着一套编码的规范。
    在这里插入图片描述
  • 消息(也就是图中的Msg)由生产者(也就是图中的Message Producer)发出,发送到队列(Queue)或者主题(Topic)中(也就是图中的Destination);消费者(也就是图中的Message Consumer)通过Destination接收发过来的Msg。
  • 而无论是生产者还是消费者,都要通过ConnectionFactory获取Connection,再创建一个会话Session,在会话中实现自己想实现的功能。

1.1 JMS开发步骤

  1. 创建一个Connection Factory
  2. 通过Connection Factory创建JMS Connection
  3. 启动 JMS Connection
  4. 通过Connection 创建 Session
  5. 创建JMS destination
  6. 创建JSM producer 或者 创建JMS message并设置destination
  7. 创建JMS consumer 或者是注册一个JMS message listener
  8. 发送并接收JSM message
  9. 关闭所有JMS资源(connection、session、producer、consumer等)

1.2 Destination 简介

  • 也就是目的地。就是存储消息的地方。而Destination分为两种,队列(queue)和主题(topic)。
  • 也就是一对一和一对多的关系。
  • 队列是发送给一个消费者的。只要有一个消费者接收到了消息,消息就会在队列中删除。
  • 主题可以发送给多个订阅者。要订阅者提前预定自己想要的主题,当发布者发布消息的时候,所有订阅者都可以接收到消息。
    在这里插入图片描述

2. 队列入门案例

  • 导包
    <dependencies>
        <!--  activemq  所需要的jar 包-->
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-all</artifactId>
            <version>5.15.9</version>
        </dependency>
        <!--  activemq 和 spring 整合的基础包 -->
        <dependency>
            <groupId>org.apache.xbean</groupId>
            <artifactId>xbean-spring</artifactId>
            <version>3.16</version>
        </dependency>
        <!--    测试包    -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    

2.1 实现生产者

  • 创建ActiveMQConnectionFactory,通过工厂生产一个连接Connection,通过连接获取一个会话Session
    ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
    Connection connection = activeMQConnectionFactory.createConnection();
    connection.start(); // 开启连接
    Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// 不使用事务,使用自动签收
    

    ACTIVEMQ_URL 就是MQ服务器的那个IP加端口号,如tcp://192.168.233.133:61616

  • 通过Session 将生产者、目的地(队列)、消息对象都生产出来
    Queue queue = session.createQueue(DESTINATION_NAME);  // 创建一个消息队列出来,也就是目的地
    MessageProducer producer = session.createProducer(queue);  // 把目的地放进生产者里面
    TextMessage message = session.createTextMessage("message:" + i);  // 创建一个要发送的消息对象,该对象存储的是字符串
    producer.send(message);  // 生产者负责将消息对象发送出去
    

    其中DESTINATION_NAME就是你目的地的名称,自己取就好。

  • 至此,就完成了发送一条消息。如果不需要了,就可以关闭所有资源了。
    producer.close();
    session.close();
    connection.close();
    
  • 完整代码。示例通过junit完成,方便测试。
    public class QueueProducer {
      private static final String ACTIVEMQ_URL = "tcp://192.168.233.133:61616";  // 服务器端口号,地址
      private static final String DESTINATION_NAME = "queue01";  // 目的地名称
      private ActiveMQConnectionFactory activeMQConnectionFactory;
      private Connection connection = null;
      private Session session = null;
      private MessageProducer producer = null;
    
      @Before
      public void before() throws JMSException {
        activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        connection = activeMQConnectionFactory.createConnection();
        connection.start();
        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// 不使用事务,使用自动签收
      }
    
      @Test
      public void test1() throws JMSException {
        // 通过session把角色都创建出来
        Queue queue = session.createQueue(DESTINATION_NAME);  // 创建一个消息队列出来,也就是目的地
        producer = session.createProducer(queue);  // 把目的地放进生产者里面
        // 发送消息
        for (int i = 0; i < 10; i++) {
          TextMessage message = session.createTextMessage("message:" + i);// 创建一个要发送的消息对象,该对象存储的是字符串
          producer.send(message);// 生产者负责将消息对象发送出去
        }
        System.out.println("发送消息完毕");
      }
    
      @After
      public void after() throws JMSException {
        // 关闭资源
        producer.close();
        session.close();
        connection.close();
        System.out.println("资源关闭完毕");
      }
    }
    
  • 加入消息之后,可以在控制台页面查看状态。
    在这里插入图片描述
  • Number Of Pending Messages:等待消费的消息,这个是未出队列的数量,公式=总接收数-总出队列数。
  • Number Of Consumers:消费者数量,消费者端的消费者数量。
  • Messages Enqueued:进队消息数,进队列的总消息量,包括出队列的。这个数只增不减。
  • Messages Dequeued:出队消息数,可以理解为是消费者消费掉的数量

2.2 实现消费者

  • 消费者负责接收消息,这就牵扯到一个问题了,到底是消费者以同步的方式停下来等消息;还是以异步的方式,有消息过来了,监视器监视到了就执行指定的方法。
  • 而ActiveMQ两种实现方式都有。

2.2.1 同步接收消息

  • 创建ActiveMQConnectionFactory,通过工厂生产一个连接Connection,通过连接获取一个会话Session,操作与生产者一模一样,在此不再重复
  • 通过Session 将消费者、目的地(队列)都生产出来
    Queue queue = session.createQueue(DESTINATION_NAME);  // 目的地
    MessageConsumer consumer = session.createConsumer(queue);   // 消费者
    
  • 消费者等待。可以规定超时时间,如果没有,那就不超时。接收到消息之后,可以取出消息。
    TextMessage message = (TextMessage) consumer.receive();  // 没有超时时间
    TextMessage message = (TextMessage) consumer.receive(3000L);  // 定制超时时间3s
    String text = message.getText();
    
  • 可以使用一个死循环,里面一直监听,如果超过规定时间都没有消息,就退出循环
    while (true) {
      // 接受一个消息,并将消息转换为Text类型的消息
      TextMessage message = (TextMessage) consumer.receive(3000L);  // 定制超时时间3s
      //TextMessage message = (TextMessage) consumer.receive();    // 没有超时时间
      if (null != message) System.out.println(message.getText());
      else break;
    }
    

2.2.2 异步接收消息

  • 也可以在不使用reveive()方法,给Consumer内部设置一个监听器,触发之后就调用。
    consumer.setMessageListener((message) -> {
      if (message instanceof TextMessage) {
        TextMessage textMessage = (TextMessage) message;
        try {
          System.out.println("接收到一个消息:" + textMessage.getText());
        } catch (JMSException e) {
          e.printStackTrace();
        }
      }
    });
    
  • 监听器是一个很简单的接口,只有一个方法,因此可以使用lambda表达式
    public interface MessageListener {
      void onMessage(Message var1);
    }
    
  • 值得注意的是,由于现在是测试,如果让程序设置完监听器之后就执行完的话,是不能产生监听效果的。因此,要在结束之前增添一个阻塞进程结束的操作。
    System.in.read();
    
  • 完整代码
    public class QueueConsumer {
      private static final String ACTIVEMQ_URL = "tcp://192.168.233.133:61616";  // 服务器端口号,地址
      private static final String DESTINATION_NAME = "queue01";  // 目的地名称
      private ActiveMQConnectionFactory activeMQConnectionFactory;
      private Connection connection = null;
      private Session session = null;
      private MessageConsumer consumer = null;
    
      @Before
      public void before() throws JMSException {
        activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        connection = activeMQConnectionFactory.createConnection();
        connection.start();  // 开启连接
        session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// 不使用事务,使用自动签收
      }
    
      /**
       * 同步方法接收
       * @throws JMSException
       */
      @Test
      public void test1() throws JMSException {
        Queue queue = session.createQueue(DESTINATION_NAME);  // 目的地
        consumer = session.createConsumer(queue);   // 消费者
        // 监听,3s超时
        while (true) {
          // 接受一个消息,并将消息转换为Text类型的消息
          TextMessage message = (TextMessage) consumer.receive(3000L);  // 定制超时时间3s
          //TextMessage message = (TextMessage) consumer.receive();    // 没有超时时间
          if (null != message) System.out.println(message.getText());
          else break;
        }
      }
    
      /**
       * 异步方法接收
       * @throws JMSException
       * @throws IOException
       */
      @Test
      public void test2() throws JMSException, IOException {
        consumer = session.createConsumer(session.createQueue(DESTINATION_NAME));
        consumer.setMessageListener((message) -> {
          if (message instanceof TextMessage) {
            TextMessage textMessage = (TextMessage) message;
            try {
              System.out.println("接收到一个消息:" + textMessage.getText());
            } catch (JMSException e) {
              e.printStackTrace();
            }
          }
        });
        System.in.read();
      }
    
      @After
      public void after() throws JMSException {
        consumer.close();
        session.close();
        connection.close();
        System.out.println("关闭");
      }
    }
    

2.3 队列消息总结

2.3.1 两种消费方式

2.3.1.1 同步阻塞方式(receive)
  • 订阅者或接收者抵用MessageConsumerreceive()方法来接收消息,receive方法在能接收到消息之前(或超时之前)将一直阻塞。
2.3.1.2 异步非阻塞方式(监听器onMessage())
  • 订阅者或接收者通过MessageConsumersetMessageListener(MessageListener listener)注册一个消息监听器,当消息到达之后,系统会自动调用监听器MessageListeneronMessage(Message message)方法。

2.3.2 队列的特点

  • 这是个点对点的消息传递方式
  • 一个消息只能有一个消费者,被消费了就没了,不会再存储了
  • 消息的生产者和消费者之间没有时间上的相关性,也就是说,生产者发送消息的时候,消费者不必一定要在线;消费者消费的时候,生产者不必一定在线。
  • 值得一提的是,目前设置的都是不使用事务(自动提交)、自动签收的操作,后面还有更多的操作。
    在这里插入图片描述

2.3.3 Something interesting

  • 值得一提的是,当队列中存在消息的时候,如果分别开启两个消费者,第一个消费者会很快地把所有消息消费掉。
  • 当有两个消费者同时在运行,如果有多个消息一起进来了,消息就会平均分摊到两个消费者中。
    在这里插入图片描述
    在这里插入图片描述

3. 实现主题的入门案例

3.1 实现发布者

  • 操作和上面基本上一致,只是在创造生产者(发布者)的时候,放进去的对象不是队列Queue,而是主题Topic。其他地方不再赘述
    // 不一样的只有这两句
    Topic topic = session.createTopic(DESTINATION_NAME);
    MessageProducer producer = session.createProducer(topic);
    

3.2 实现订阅者

  • 与上面也基本一致。仅下面的操作不一致。
    Topic topic = session.createTopic(DESTINATION_NAME);
    MessageConsumer consumer = session.createConsumer(topic);
    

3.3 主题总结

3.3.1 topic特点

  1. 生产者将消息发布到topic中,每个消息可以有多个消费者,属于1:N的关系
  2. 生产者和消费者之间有时间上的相关性。订阅某一个主题的消费者只能消费自它订阅之后发布的消息。也就是说,要先开消费者在那等着生产者发布消息;否则,如果让生产者先发布了消息到目的地,再开消费者,消费者没办法接收到消息。
  3. 生产者生产时,topic不保存消息它是无状态的不落地,假如无人订阅就去生产,那就是一条废消息,所以,一般先启动消费者再启动生产者。
    在这里插入图片描述
  • 这是一个发布者+两个订阅者情况下,订阅者均收到消息的状态。可见,消息出队的个数是入队的个数的两倍。
    在这里插入图片描述

4. queue和topic对比

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值