ActiveMQ实践

title:ActiveMQ实践

date:2017年10月27日00:03:29


在介绍了一些JMS规范和概念之后,我们开始在Java程序中使用ActiveMQ。

首先,我们编写一个发送者程序。

package com.wangcc.activemq.cluster.queue;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnectionFactory;

public class QueueSender {
    public static void main(String[] args) throws Exception {
      //1
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://10.*.*.66:61616");
        //2
      Connection connection = connectionFactory.createConnection();
        // 默认是关闭,所以这里需要开启
        //3
      connection.start();
        /*
         * 当没有事务的时候,是由签收模式来决定是否签收的
         * 
         */
      //4
        Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
        //5
      Destination destination = session.createQueue("my-queue");
      //6
        MessageProducer producer = session.createProducer(destination);
        for (int i = 0; i < 3; i++) {
          //7
            TextMessage textMessage = session.createTextMessage();
            textMessage.setText("KOBEeee" + i);
//8
            producer.send(textMessage);
        }
      //9
        session.commit();
        session.close();
        connection.close();
    }
}

1.创建ActiveMQConnectionFactory链接工厂。当没有手动的去更改用户名和密码时(如何更改密码已经在之前的博客linux下安装activemq中说过了),不需要在新建ActiveMQConnectionFactory时显示指定用户名和密码,而只需要指定url链接。

2.通过链接工厂创建链接ActiveMQ消息服务器的链接,注意链接默认是关闭的。(Connection代表了应用程序和消息服务器之间的通信链路)

3.手动开启链接,在实际使用中,何时开启链接是有讲究的。

4.通过链接connection创建session,session是发送或接收消息的一个会话,代表这接收或发送消息的上下文环境,是发送或接收消息的核心对象。注意创建session的时候我们需要指定两个属性,一个是否开启事务,一个是指定签收模式,这里开启事务,签收模式随意。具体什么是签收模式后面会细讲。

5.通过Sesion创建Destination对象,当PTP模式下是队列,在pub/sub模式下是主题(TOPIC),指定发送的目的地。

6.通过Session创建发送消息的生产者

7.定义JMS规范的消息类型,填写消息内容

8.生产者发送消息。

9.session.commit()在commit的时候真正发送消息,而且是3条消息一起发送,使用事务,可以批量发送。

最后释放链接。

写好发送QUEUE程序后,我们直接运行该程序,注意,此时接收方程序还尚未编写。

这时候我们去访问控制台,发现队列中新增了个叫my-queue的队列,并且显示有三个消息进入队列了。

此时我们编写接收方。

package com.wangcc.activemq.cluster.queue;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.activemq.ActiveMQConnectionFactory;

/**
 * @ClassName: QueueReceiver
 * @Description:
 * 
 *              QUEUE默认持久化,默认持久化方式kahadb,所以在发送端发送消息的时候,消费者不要求一定在线
 *               即使当ActiveMQ宕机了之后,重启AcitveMQ后,消费者还是能取到之前生产者发送的消息
 * 
 * 
 * 
 * @author wangcc
 * @date 2017年10月26日 下午6:58:15
 * 
 */
public class QueueReceiver {
    public static void main(String[] args) throws JMSException {
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://10.*.*.66:61616");
        Connection connection = connectionFactory.createConnection();
        connection.start();
        // 当有事务时,是由session.commit事务提交与否来确定是否消费者确认签收,我们测试下,我们不commit,我们可以无数次接受到该消息

        Session session = connection.createSession(Boolean.TRUE, Session.AUTO_ACKNOWLEDGE);
        Destination destination = session.createQueue("my-queue");
        MessageConsumer messageConsumer = session.createConsumer(destination);
        int i = 0;
        while (i < 3) {
            TextMessage textMessage = (TextMessage) messageConsumer.receive();
            System.out.println("接收的消息:" + textMessage.getText());

            i++;

        }
        session.commit();
        session.close();
        connection.close();
    }
}
  • 我们首先在创建Session的时候使用事务,然后运行程序,我们会发现,我们能够接收到三条消息,然后程序停止运行。

这说明一点,对于QUEUE类型的消息,生产者和消费者之间是没有时间相关性的,QUEUE队列的发送是默认持久化的,当生产者发送消息之后,ActiveMQ就会将消息存起来,然后等到消费者上线之后去收消息,MQ就会把消息给消费者,消费者无需在生产者发送消息之前存在。但是还有一点需要明确,就是一个队列形式发送的消息只能被一个消费者消费,如果被一个消费者消费之后呢,另一个消费者就没有办法在消费那条消息了,注意了,是一个队列形式的消息对应一个消费者,而不是一个队列对应一个消费者,一个队列可以包含多条信息,也就意外着可能有多个消费者去分享这个消息,但是每一条都只能被一个消费者消费。这一个点和后面讲的TOPIC是不同的。

我们再次运行接收方程序,我们发现没有任何的消息再次被接收了。现在我们将上述程序中的session.commit();给注释掉,这个时候我们再次启动发送方发送三条消息,然后我们运行接收方,运行第一次,接收三条消息,没问题,然后再运行第二次,仍然可以接收三条消息,第三次第四次等等,你发现,每次都能再接收消息,这是为什么呢?这是因为当有事务时,是由session.commit事务提交与否来确定是否消费者确认签收

那什么叫做签收呢,也就是说消费者消费之后向MQ告知我已经收到这个消息,你可以把这个消息给干掉了,免得浪费你的空间。一旦告知MQ已经接收到这个消息后,再次请求这个消息,MQ就不会再发给你了。

  • 接着我们将创建Session时的事务关掉,这个时候签收模式就会有创建Session时指定的第二个参数来决定。

    ​ 第二个参数一共有三个可选择的值:

    • Session.AUTO_ACKNOWLEDGE,当客户成功的从receive方法返回或者从MessageListener.onMessage中返回的时候,会话自动确认客户收到的信息。
    • Session.CLIENT_ACKNOWLEDGE,客户通过调用消息的acknowledge方法确认消息。需要注意的是,在这种模式中,确认是在会话层进行的,确认一个消息自动确认所有已被会话消费的消息。例如,如果一个消费者消费了10个消息,然后确认第五个信息,那么所有的10个消息都会被确认。
    • Session.DUPS_OK_ACKNOWLEDGE,签不签收无所谓了,只要消费者能够容忍重复的消息接受,当然这样会降低Session的开销

    那么在实际的开发中呢,我们是推荐使用CLIENT_ACKNOWLEDGE接收端手动确认的,因为我们成功的接收并不代表我们接收端成功的处理了消息,我们最好是在客户端正确的处理完消息后,再由客户端手动确认签收比较好。

    这里我们可以测试一下CLIENT_ACKNOWLEDGE

    将接收端的程序稍微改一下

    while (i < 3) {
            TextMessage textMessage = (TextMessage) messageConsumer.receive();
            System.out.println("接收的消息:" + textMessage.getText());
    if (i == 1) {
                        textMessage.acknowledge();
                    }
            i++;
    
        }

    发送端执行一次,发送三条消息,然后执行接收端,成功收到三条消息,然后再执行一次接收端,你会发现你这次只接收到了一条消息。这是为什么呢?
    ​ 当第一次执行接收端程序时,当i=1时,执行了 textMessage.acknowledge();进行了手动的签收,此时消费者一共接收了两条消息,这个时候也就代表着消费者确认了两条消息,因为上面说了,在CLIENT_ACKNOWLEDGE模式中,确认是在会话层进行的,确认一个消息自动确认所有已被会话消费的消息。那么也就是说队列中还有一个消息未被确认,这是运行第二次接收端,当这个循环执行第一次的时候,会接收到之前还没确认接收的一条消息。之后你继续运行n次接收端,都只会接收到这一条消息。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值