学习淘淘商城第五十九课(ActiveMQ持久化Topic消息)

来源:互联网 发布:淘宝男装潜力卖家入口 编辑:程序博客网 时间:2024/05/29 03:15

      我们上节课一起学习了产生和消费topic消息,但是有个问题就是topic消息没有持久化,也就意味着,如果消息发送者发送消息的时候,如果消费者没有运行的话,它将无法消费这个消息了(即使它启动也无法再接收到那条topic消息了),这样问题就来了,如果那条消息非常重要呢?我们不能容忍接收不到消息的情况。

      下面来实现topic消息的持久化,分以下四步来进行

第一步:写生产者方法

       我们新建一个生产topic消息的生产者方法,这个方法与我们上节课生产topic消息的方法略有不同(不同之处我已在下图标出),如下图所示。


         该方法的全部代码如下:

/** * 持久化topic消息,生产者 * @throws JMSException */@Testpublic void testTopicPersistenceProducer() throws JMSException{//1.创建一个连接工厂对象ConnectionFactory对象。需要指定mq服务的ip及端口号。注意参数brokerURL的开头是//tcp://而不是我们通常的http://,端口是61616而不是我们访问activemq后台管理页面所使用的8161ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.156.30:61616");//2.设置使用异步发送消息,这样可以显著提高发送性能connectionFactory.setUseAsyncSend(true);//3.使用ConnectionFactory创建一个连接Connection对象Connection connection = connectionFactory.createConnection();//4.对于每一个生产者来说,其clientID的值必须唯一connection.setClientID("producer1");//5.开启连接。调用Connection对象的start方法connection.start();//6.使用Connection对象创建一个Session对象//第一个参数是是否开启事务,一般不使用分布式事务,因为它特别消耗性能,而且顾客体验特别差,现在互联网的//做法是保证数据的最终一致(也就是允许暂时数据不一致),比如顾客下单购买东西,一旦订单生成完就立刻响应给用户//下单成功。至于下单后一系列的操作,比如通知会计记账、通知物流发货、商品数量同步等等都先不用管,只需要//发送一条消息到消息队列,消息队列来告知各模块进行相应的操作,一次告知不行就两次,直到完成所有相关操作为止,这//也就做到了数据的最终一致性。如果第一个参数为true,那么第二个参数将会被忽略掉。如果第一个参数为false,那么//第二个参数为消息的应答模式,常见的有手动和自动两种模式,我们一般使用自动模式。Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//7.使用Session对象创建一个Destination对象,两种形式queue、topic。现在我们使用topic//参数就是消息队列的名称Topic topic = session.createTopic("test-topic");//8.使用Session对象创建一个Producer对象MessageProducer producer = session.createProducer(topic);//9.DeliveryMode设置为PERSISTENT(持久化)producer.setDeliveryMode(DeliveryMode.PERSISTENT);//10.创建一个TextMessage对象//有两种方式,第一种方式://TextMessage textMessage = new ActiveMQTextMessage();//textMessage.setText("hello,activemq!!!");//第二种方式:TextMessage textMessage = session.createTextMessage("hello,activemq topic111");//11.发送消息producer.send(textMessage);//12.关闭资源producer.close();session.close();connection.close();}

第二步:写消费者方法
        配好了生产者,我们再来配置下消费者,在创建ConnectionFactory时便与上节课学习的创建方式有所不同(不同之处我已经标出来了),如下图所示。


       消费者方法全部代码如下:

/** * 持久化topic消息,消费者 * @throws Exception */@Testpublic void testTopicPersistenceConsumer() throws Exception{//1.创建一个连接工厂对象ConnectionFactory对象。需要指定mq服务的ip及端口号。注意参数brokerURL的开头是//tcp://而不是我们通常的http://,端口是61616而不是我们访问activemq后台管理页面所使用的8161ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.156.30:61616");//2.设置使用异步接收消息,这样可以显著提高接收性能connectionFactory.setUseAsyncSend(true);//3.使用ConnectionFactory创建一个连接Connection对象Connection connection = connectionFactory.createConnection();//4.设置消费者ID,每个消费者的clientID都不能相同!connection.setClientID("consumer1");//5.开启连接。调用Connection对象的start方法connection.start();//6.使用Connection对象创建一个Session对象//第一个参数是是否开启事务,一般不使用分布式事务,因为它特别消耗性能,而且顾客体验特别差,现在互联网的//做法是保证数据的最终一致(也就是允许暂时数据不一致),比如顾客下单购买东西,一旦订单生成完就立刻响应给用户//下单成功。至于下单后一系列的操作,比如通知会计记账、通知物流发货、商品数量同步等等都先不用管,只需要//发送一条消息到消息队列,消息队列来告知各模块进行相应的操作,一次告知不行就两次,直到完成所有相关操作为止,这//也就做到了数据的最终一致性。如果第一个参数为true,那么第二个参数将会被忽略掉。如果第一个参数为false,那么//第二个参数为消息的应答模式,常见的有手动和自动两种模式,我们一般使用自动模式。Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);//7.使用Session对象创建一个Destination对象,两种形式queue、topic。现在我们使用queue//参数就是消息队列的名称Topic topic = session.createTopic("test-topic");//8.使用Session对象创建一个Consumer对象MessageConsumer consumer = session.createDurableSubscriber(topic, "consumer1");//9.向Consumer对象中设置一个MessageListener对象,用来接收消息consumer.setMessageListener(new MessageListener() {@Overridepublic void onMessage(Message message) {if(message instanceof TextMessage) {TextMessage textMessage = (TextMessage)message;try {String text = textMessage.getText();System.out.println(text);} catch (JMSException e) {e.printStackTrace();}}}});//10.程序等待接收用户结束操作//程序自己并不知道什么时候有消息,也不知道什么时候不再发送消息了,这就需要手动干预,//当我们想停止接收消息时,可以在控制台输入任意键,然后回车即可结束接收操作(也可以直接按回车)。System.out.println("topic消费者1111。。。。。");System.in.read();//11.关闭资源consumer.close();session.close();connection.close();}
第三步:配置activemq.xml文件
        我们还需要配置下activemq的activemq.xml文件,只需要添加一句配置,就是在<broker的末尾添加一句关于持久化的配置persistent="true"即可。


      <broker>添加持久化后的代码如下:

<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}" persistent="true">

第四步:测试

       为了测试topic消息的持久化,我们需要先启动消费者,因为只有消费者先在服务端做下登记后,才能保证发送的topic消息被消费者所消费。我们先启动clientId为"consumer1"的消费者,如下图所示。


         为了测试多消费者的情况,我们再启动一个消费者,做法很简单,就是修改下消费者的clientId,然后把标志信息"topic消费者1111。。。。。"改为"topic消费者2222。。。。。"即可(如下图红色方框框住的部分就是要修改的地方),然后保存并重新运行testTopicPersistenceConsumer这个方法。


      创建了两个消费者后,我们再来创建两个生产者,先执行一遍testTopicPersistenceProducer方法,这样便创建了一个clientId为"producer1"的生产者。之后我们修改下clientId的值,改为"producer2",然后把发送的消息也改一下,以示区分,如下图所示(红色方框框住的内容是修改的内容),修改后保存,然后再运行一下该方法,这样我们便有两个生产者了。


    既然消息发送者发送了消息,我们看下当前两个消费者有没有接收到消息,如下图所示,发现两个消费者都接收到两个生产者发来的消息了。


        我们再来看下activemq的后台管理系统,先看下"Topics",可以看到名称为"test-topic"的消息队列,该队列有两个消费者,刚才一共压入队列两条消息,与我们刚才的操作完全相符。


          我们再点击"Subscribers"进行查看,可以看到有两个消费者,当前两个消费者接收到2条消息,并且已消费2条消息,当前待接收的消息数为0。


        当然,上面只是测试的生产者和消费者都在线的情况,我们现在要测试消费者不在线的时候,生产者发送消息,等消费者启动后还能不能接收到消息。做法也很简单,我们先把两个消费者的进程关掉。然后修改生产者方法中的消息信息,比如我们把"producer1"的消息信息改为"hello,activemq topic333",运行生产者方法,然后再修改clientId为"producer2",消息信息修改为"hello,activemq topic444",再运行生产者方法。

        这时我们再来看下activemq的后台管理系统界面,还是先看"Topics",消息队列的消费者还是显示的是2个(这时消费者处于关闭状态),这说明一旦消费者在服务端登记后,服务端便一直记着这两个消费者,当前压入消息队列的消息是4条(原来的两条再加上刚添加的两条,共四条)


         再看下"Subscribers"一栏,可以看到当前两个消费者处于离线状态,两个消费者都有两个待接收的消息(Pending Queue Size),这说明,虽然消费者当前不在线,但是服务端依然为它们保留着信息,并未丢失。当前进入消息队列共4条消息(Enqueue Counter ) ,消费的消息还是两条(Dequeue Counter),这是因为新发送的两条消息消费者还没有消费掉。 


        这时我们启动两个消费者(记得做下区分,在上面已经说过了,就不再啰嗦了),启动完后,我们查看下两个消费者的控制台信息,如下图所示。可以看到两个消费者都正常消费了刚才它们不在线时消息生产者发送的消息,没有造成消息丢失。


         我们再看下activemq的后台管理系统界面,"Topics"一栏没有变化,我就不贴图了,我们看下"Subscribers",可以看到,消息队列中待发送的消息数变成0了,这是因为所有消息都已经被消费完了。两个消费者都显示消息入队4条,出队4条,消费完毕。


    

         那么,有的同学会疑问,那重启activemq服务呢?消息还能不丢失吗?那我们做下实验便知道了,我们还是先把两个消费者关掉,修改消息生产者方法的消息内容,重新发送两条消息。然后,我们关掉activemq并重新启动activemq,如下所示。

[root@activemq bin]# ./activemq stopINFO: Loading '/usr/local/apache-activemq-5.12.0//bin/env'INFO: Using java '/usr/local/jdk1.7.0_80/bin/java'INFO: Waiting at least 30 seconds for regular process termination of pid '1879' : Java Runtime: Oracle Corporation 1.7.0_80 /usr/local/jdk1.7.0_80/jre  Heap sizes: current=63360k  free=62649k  max=1013632k    JVM args: -Xms64M -Xmx1G -Djava.util.logging.config.file=logging.properties -Djava.security.auth.login.config=/usr/local/apache-activemq-5.12.0//conf/login.config -Dactivemq.classpath=/usr/local/apache-activemq-5.12.0//conf:/usr/local/apache-activemq-5.12.0//../lib/ -Dactivemq.home=/usr/local/apache-activemq-5.12.0/ -Dactivemq.base=/usr/local/apache-activemq-5.12.0/ -Dactivemq.conf=/usr/local/apache-activemq-5.12.0//conf -Dactivemq.data=/usr/local/apache-activemq-5.12.0//dataExtensions classpath:  [/usr/local/apache-activemq-5.12.0/lib,/usr/local/apache-activemq-5.12.0/lib/camel,/usr/local/apache-activemq-5.12.0/lib/optional,/usr/local/apache-activemq-5.12.0/lib/web,/usr/local/apache-activemq-5.12.0/lib/extra]ACTIVEMQ_HOME: /usr/local/apache-activemq-5.12.0ACTIVEMQ_BASE: /usr/local/apache-activemq-5.12.0ACTIVEMQ_CONF: /usr/local/apache-activemq-5.12.0/confACTIVEMQ_DATA: /usr/local/apache-activemq-5.12.0/data..Connecting to pid: 1879..Stopping broker: localhost.. TERMINATED
[root@activemq bin]# ./activemq startINFO: Loading '/usr/local/apache-activemq-5.12.0//bin/env'INFO: Using java '/usr/local/jdk1.7.0_80/bin/java'INFO: Starting - inspect logfiles specified in logging.properties and log4j.properties to get detailsINFO: pidfile created : '/usr/local/apache-activemq-5.12.0//data/activemq.pid' (pid '2140')[root@activemq bin]# 

       重启activemq后,我们查看activemq的后台管理系统界面,在"Topics"一栏,我们可以看到消息队列有两个消费者(与重启之前一样),入队的消息数量显示为0(这是表示 重启后压入消息队列的消息,重启后没有发送任何消息,因此该数量为0)


       我们再看"Subscribers"一栏,显示两个消费者的消息队列中有两条消息没有消费。


          我们现在启动两个消费者,启动完后,我们观察两个消费者的控制台信息,如下图所示,发现正常接收到了activemq服务重启之前的消息!!!


          我们最后再看一眼activemq的后台,可以看到消息都被消费完了。至于没有保留重启前消息的数量,这个没有问题,因为消息都已经被消费完了,保不保存曾经被消费的消息数量没有什么意义,只要保证消息未被消费的消息不丢失就可以了!!!


        


2 0