JMS Activemq实战例子demo

上一篇已经讲了JMS的基本概念,这一篇来上手练一练,如果对JMS基本概念还不熟悉,欢迎参靠JMS基本概


这篇文章所使用的代码已经不是我刚入手时的代码,已经经过我重构过的代码,便于理解,并且加了很多中文注释,希望对大家有所帮助。


在基本概念一篇中已经讲到,JMS有两种消息模型,一种是点对点,另一种的发布/订阅模式。本篇文章就基于这两种消息模型来写例子。


点对点模型


        先看一下生产者代码:

package com.darren.activemq.queue;

import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TextMessage;

import com.darren.activemq.ActivemqContants;
import com.darren.activemq.ProducerConsumer;

/**
 * 消息生产类
 * 
 * @author Darren
 *
 */
public class QueueProducer extends ProducerConsumer {
    private int startNumber;
    private int endNumber;

    public QueueProducer(String name) throws JMSException {
        this.name = name;

        // 通过连接工厂获取连接
        this.connection = this.getConnection();
        // 启动连接
        this.connection.start();
        // 创建Session
        this.session = this.connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
        // 创建消息队列
        this.destination = this.session.createQueue("test-queue");
        // 创建消息生产者
        this.messageProducer = this.session.createProducer(destination);
    }

    /**
     * 发送消息
     * 
     * @throws JMSException
     */
    public void sendMessage() throws JMSException {
        this.startNumber = this.endNumber;
        this.endNumber = this.startNumber + MESSAGE_COUNT;
        for (int i = this.startNumber; i < this.endNumber; i++) {
            TextMessage message = this.session.createTextMessage("I send the message " + i);
            System.out.println(message.getText());
            this.messageProducer.send(message);
        }
    }

    /**
     * 发送结束标志
     * 
     * @param times
     *            发送次数
     * @throws JMSException
     */
    public void sendFinishMessage(int times) throws JMSException {
        for (int i = 0; i < times; i++) {
            TextMessage message = this.session.createTextMessage(ActivemqContants.FINISH_FLAG);
            System.out.println("Send finish flag: " + message.getText());
            this.messageProducer.send(message);
        }
    }

    /**
     * 提交事务
     * 
     * @throws JMSException
     */
    public void commit() throws JMSException {
        this.session.commit();
    }
}

        我写了两种消息消费者,其实也就是上一篇讲的同步消费和异步消费。

        先来看异步消费者:


package com.darren.activemq.queue;

import javax.jms.JMSException;
import javax.jms.Session;
import com.darren.activemq.ProducerConsumer;
import com.darren.activemq.listener.ConsumerListener;

/**
 * 消息消费者
 * 
 * @author Darren
 *
 */
public class QueueListenerConsumer extends ProducerConsumer {

    public QueueListenerConsumer(String name) throws JMSException {
        this.name = name;

        // 通过连接工厂获取连接
        this.connection = this.getConnection();
        // 启动连接
        this.connection.start();
        // 创建Session
        this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 创建连接的消息队列
        this.destination = this.session.createQueue("test-queue");
        // 创建消息消费者
        this.messageConsumer = this.session.createConsumer(destination);
        // 设置消息监听
        this.messageConsumer.setMessageListener(new ConsumerListener("Listener 1:", this));
    }
}

        异步消费者通过设置监听器来达到异步消费消息的目的。其实这个监听器也很简单,我们来看一下。

package com.darren.activemq.listener;

import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.TextMessage;

import com.darren.activemq.ActivemqContants;
import com.darren.activemq.ProducerConsumer;

/**
 * 消息监听
 * 
 * @author Darren
 *
 */
public class ConsumerListener implements MessageListener {
    private String name;
    private ProducerConsumer producerConsumer;

    public ConsumerListener(String name, ProducerConsumer producerConsumer) {
        this.name = name;
        this.producerConsumer = producerConsumer;
    }

    @Override
    public void onMessage(Message message) {
        try {
            if (message instanceof TextMessage) {
                TextMessage textMessage = (TextMessage) message;
                System.out.println(name + " 接收到的消息 " + textMessage.getText());
                // 如果接收到结束标志,修改消费者的状态
                if (ActivemqContants.FINISH_FLAG.equals(textMessage.getText())) {
                    // 消费者消费完成,关闭连接
                    this.producerConsumer.closeConnection();
                }
            } else if (message instanceof ObjectMessage) {
                ObjectMessage objectMessage = (ObjectMessage) message;

                System.out.println(name + " 接收到的消息 " + objectMessage.getObject());
            } else {
                System.out.println("不支持的消息类型!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

        这里只有一个方法。

        为了便于理解,我先把父类贴出来,这样就可以基本完整的看了。


package com.darren.activemq;

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

import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;

public abstract class ProducerConsumer {
    protected static final String USERNAME = ActiveMQConnection.DEFAULT_USER;// 默认的连接用户名
    protected static final String PASSWORD = ActiveMQConnection.DEFAULT_PASSWORD;// 默认的连接密码
    protected static final String BROKERURL = ActiveMQConnection.DEFAULT_BROKER_URL;// 默认的连接地址
    protected static final int MESSAGE_COUNT = 10;// 发送的消息数量

    protected static ConnectionFactory connectionFactory;// 连接工厂
    protected Connection connection = null; // 连接
    protected Session session;// 会话 接受或者发送消息的线程
    protected Destination destination;// 消息的目的地
    protected MessageProducer messageProducer;// 消息生产者
    protected MessageConsumer messageConsumer;// 消息消费者

    // 状态
    protected volatile boolean isFinished = false;
    protected String name;

    static {
        getConnectionFactory();
    }

    /**
     * 获取连接工厂
     * 
     * @return
     */
    protected synchronized static ConnectionFactory getConnectionFactory() {
        if (connectionFactory == null) {
            connectionFactory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKERURL);
        }

        return connectionFactory;
    }

    /**
     * 获取连接
     * 
     * @return
     * @throws JMSException
     */
    protected Connection getConnection() throws JMSException {
        return connectionFactory.createConnection();
    };

    /**
     * 关闭连接
     * 
     * @throws JMSException
     */
    public void closeConnection() throws JMSException {
        if (this.connection != null) {
            this.connection.close();
        }

        System.out.println(this.name + " connection close!");

        this.isFinished = true;
    }

    /**
     * 获取状态
     * 
     * @return
     */
    public boolean isFinished() {
        return isFinished;
    }

    /**
     * 获取消费者
     * 
     * @return
     */
    public MessageConsumer getMessageConsumer() {
        return messageConsumer;
    }

    /**
     * 获取生产者或消费者名称
     * 
     * @return
     */
    public String getName() {
        return name;
    }
}

        用到的常量类

package com.darren.activemq;

public class ActivemqContants {
    public static final String FINISH_FLAG = "FINISHED";
}


        来个测试类吧,这样就完整了

package com.darren.activemq.queue;

import javax.jms.JMSException;

public class QueueTest {
    public static void main(String[] args) {
        Thread thread = null;
        try {
            // 启动消费者,消费者开始等待
            new QueueListenerConsumer("QueueListenerConsumer");
            //new QueueReceiveConsumer("QueueReceiveConsumer");

            thread = new Thread(new Runnable() {

                @Override
                public void run() {
                    try {
                        // 启动生产者,生产者定时生产消息
                        QueueProducer producer = new QueueProducer("QueueProducer");
                        Thread.sleep(2000);
                        // 第一次发送
                        producer.sendMessage();
                        producer.commit();

                        Thread.sleep(2000);
                        // 第二次发送
                        producer.sendMessage();
                        producer.commit();

                        Thread.sleep(2000);
                        // 第三次发送
                        producer.sendMessage();
                        producer.commit();

                        // 发送结束标志
                        producer.sendFinishMessage(1);
                        producer.commit();

                        // 生产者生产完成,关闭连接
                        producer.closeConnection();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}


        现在为止,一个异步消息的完整例子就真的完整了,我是为了便于看到消息的消费过程,所以把生产者放到了一个线程里来不断的生产消息,来通过观察消费者打印的消费信息,了解消费的过程。


        再来看看同步消息模式:

        同步消费的方式就是轮训的去要有没有消息,有没有消息。为了适应我的测试类,我把同步消息消费也按照异步消费的模式做了相应的改动,下边就是代码:

package com.darren.activemq.queue;

import javax.jms.JMSException;
import javax.jms.Session;
import com.darren.activemq.ProducerConsumer;
import com.darren.activemq.listener.UglyConsumerListener;

/**
 * 消息消费者
 * 
 * @author Darren
 *
 */
public class QueueReceiveConsumer extends ProducerConsumer {

    public QueueReceiveConsumer(String name) throws JMSException {
        this.name = name;

        // 通过连接工厂获取连接
        this.connection = this.getConnection();
        // 启动连接
        this.connection.start();
        // 创建Session
        this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 创建连接的消息队列
        this.destination = this.session.createQueue("test-queue");
        // 创建消息消费者
        this.messageConsumer = this.session.createConsumer(destination);

        // 启动一个异步线程接受消息,模拟一个消息监听器
        Thread thread = new Thread(new UglyConsumerListener("UglyListener 1:", this));
        thread.start();
    }
}


        在这里,我选择启动一个线程去设置一个监听器,为什么要启动线程,因为我上边提到了要适应我的测试类,我是先初始化消费者,如果不用线程,那么消费者就会在要消息的时候夯住,因为:

Message message = messageConsumer.receive(100000);


        这个方法是阻塞式方法。会程序在初始化消费者后就会夯住,然后生成者无法启动,无法生成消息,整个程序就死锁了,为了解决这个问题,所以采用模拟异步消费的方式来写同步消费例子。

        下边来看我写的丑陋的监听器:

package com.darren.activemq.listener;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.ObjectMessage;
import javax.jms.TextMessage;
import com.darren.activemq.ActivemqContants;
import com.darren.activemq.ProducerConsumer;

/**
 * 消息监听
 * 
 * @author Darren
 *
 */
public class UglyConsumerListener implements Runnable {
    private String name;
    private ProducerConsumer producerConsumer;

    public UglyConsumerListener(String name, ProducerConsumer producerConsumer) {
        this.name = name;
        this.producerConsumer = producerConsumer;
    }

    public void onMessage(Message message) {
        try {
            if (message instanceof TextMessage) {
                TextMessage textMessage = (TextMessage) message;
                System.out.println(name + " 接收到的消息 " + textMessage.getText());
                // 如果接收到结束标志,修改消费者的状态
                if (ActivemqContants.FINISH_FLAG.equals(textMessage.getText())) {
                    // 消费者消费完成,关闭连接
                    this.producerConsumer.closeConnection();
                }
            } else if (message instanceof ObjectMessage) {
                ObjectMessage objectMessage = (ObjectMessage) message;

                System.out.println(name + " 接收到的消息 " + objectMessage.getObject());
            } else {
                System.out.println("不支持的消息类型!");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void run() {
        while (!producerConsumer.isFinished()) {
            MessageConsumer messageConsumer = this.producerConsumer.getMessageConsumer();
            if (messageConsumer != null) {
                try {
                    Message message = messageConsumer.receive(100000);
                    this.onMessage(message);
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

        为什么称为丑陋的监听器,相信大家也不难发现,因为采用循环方式,并不是真正的监听器。 看了这两个监听器,那么我提出一个问题,这个问题也是在我写这篇文章的时候发现的,如果我再写一个抽象类去作为这两个监听器的父类,把onMessage方法提出来,是不是更加优化了,因为我发现两个onMessage方法其实一样,我也是故意给做的一样的。

        当然今天讲的并不是Java的重构,而是JMS,这个问题就作为题外话好了。


        现在你自己也可以去跑一跑这两个例子,细心的同学或发现在测试类中其实有一行有疑问。为什么发送结束消息的方法要加参数,可以设置发送多个结束消息。这个问题我们稍后介绍,下面我来运行一下代码,也许你就渐渐明白。


        先跑一个异步消费的例子:


 INFO | Successfully connected to tcp://localhost:61616
 INFO | Successfully connected to tcp://localhost:61616
I send the message 0
I send the message 1
I send the message 2
I send the message 3
I send the message 4
I send the message 5
I send the message 6
I send the message 7
I send the message 8
I send the message 9
Listener 1: 接收到的消息 I send the message 0
Listener 1: 接收到的消息 I send the message 1
Listener 1: 接收到的消息 I send the message 2
Listener 1: 接收到的消息 I send the message 3
Listener 1: 接收到的消息 I send the message 4
Listener 1: 接收到的消息 I send the message 5
Listener 1: 接收到的消息 I send the message 6
Listener 1: 接收到的消息 I send the message 7
Listener 1: 接收到的消息 I send the message 8
Listener 1: 接收到的消息 I send the message 9
I send the message 10
I send the message 11
I send the message 12
I send the message 13
I send the message 14
I send the message 15
I send the message 16
I send the message 17
I send the message 18
I send the message 19
Listener 1: 接收到的消息 I send the message 10
Listener 1: 接收到的消息 I send the message 11
Listener 1: 接收到的消息 I send the message 12
Listener 1: 接收到的消息 I send the message 13
Listener 1: 接收到的消息 I send the message 14
Listener 1: 接收到的消息 I send the message 15
Listener 1: 接收到的消息 I send the message 16
Listener 1: 接收到的消息 I send the message 17
Listener 1: 接收到的消息 I send the message 18
Listener 1: 接收到的消息 I send the message 19
I send the message 20
I send the message 21
I send the message 22
I send the message 23
I send the message 24
I send the message 25
I send the message 26
I send the message 27
I send the message 28
I send the message 29
Listener 1: 接收到的消息 I send the message 20
Listener 1: 接收到的消息 I send the message 21
Send finish flag: FINISHED
Listener 1: 接收到的消息 I send the message 22
Listener 1: 接收到的消息 I send the message 23
Listener 1: 接收到的消息 I send the message 24
Listener 1: 接收到的消息 I send the message 25
Listener 1: 接收到的消息 I send the message 26
Listener 1: 接收到的消息 I send the message 27
Listener 1: 接收到的消息 I send the message 28
Listener 1: 接收到的消息 I send the message 29
Listener 1: 接收到的消息 FINISHED
QueueProducer connection close!
QueueListenerConsumer connection close!

        然后再来跑一个同步消费的例子:

            //new QueueListenerConsumer("QueueListenerConsumer");
            new QueueReceiveConsumer("QueueReceiveConsumer");

        只需把注水换一换就行了。

 INFO | Successfully connected to tcp://localhost:61616
 INFO | Successfully connected to tcp://localhost:61616
I send the message 0
I send the message 1
I send the message 2
I send the message 3
I send the message 4
I send the message 5
I send the message 6
I send the message 7
I send the message 8
I send the message 9
UglyListener 1: 接收到的消息 I send the message 0
UglyListener 1: 接收到的消息 I send the message 1
UglyListener 1: 接收到的消息 I send the message 2
UglyListener 1: 接收到的消息 I send the message 3
UglyListener 1: 接收到的消息 I send the message 4
UglyListener 1: 接收到的消息 I send the message 5
UglyListener 1: 接收到的消息 I send the message 6
UglyListener 1: 接收到的消息 I send the message 7
UglyListener 1: 接收到的消息 I send the message 8
UglyListener 1: 接收到的消息 I send the message 9
I send the message 10
I send the message 11
I send the message 12
I send the message 13
I send the message 14
I send the message 15
I send the message 16
I send the message 17
I send the message 18
I send the message 19
UglyListener 1: 接收到的消息 I send the message 10
UglyListener 1: 接收到的消息 I send the message 11
UglyListener 1: 接收到的消息 I send the message 12
UglyListener 1: 接收到的消息 I send the message 13
UglyListener 1: 接收到的消息 I send the message 14
UglyListener 1: 接收到的消息 I send the message 15
UglyListener 1: 接收到的消息 I send the message 16
UglyListener 1: 接收到的消息 I send the message 17
UglyListener 1: 接收到的消息 I send the message 18
UglyListener 1: 接收到的消息 I send the message 19
I send the message 20
I send the message 21
I send the message 22
I send the message 23
I send the message 24
I send the message 25
I send the message 26
I send the message 27
I send the message 28
I send the message 29
Send finish flag: FINISHED
UglyListener 1: 接收到的消息 I send the message 20
UglyListener 1: 接收到的消息 I send the message 21
UglyListener 1: 接收到的消息 I send the message 22
UglyListener 1: 接收到的消息 I send the message 23
UglyListener 1: 接收到的消息 I send the message 24
UglyListener 1: 接收到的消息 I send the message 25
UglyListener 1: 接收到的消息 I send the message 26
UglyListener 1: 接收到的消息 I send the message 27
UglyListener 1: 接收到的消息 I send the message 28
UglyListener 1: 接收到的消息 I send the message 29
UglyListener 1: 接收到的消息 FINISHED
QueueReceiveConsumer connection close!
QueueProducer connection close!

        大家会发现和异步消费并未区别,因为其实也是异步消费。

        那么如果我现在把测试类的注释都打开,会是什么结果呢:

new QueueListenerConsumer("QueueListenerConsumer");
new QueueReceiveConsumer("QueueReceiveConsumer");

 INFO | Successfully connected to tcp://localhost:61616
 INFO | Successfully connected to tcp://localhost:61616
 INFO | Successfully connected to tcp://localhost:61616
I send the message 0
I send the message 1
I send the message 2
I send the message 3
I send the message 4
I send the message 5
I send the message 6
I send the message 7
I send the message 8
I send the message 9
Listener 1: 接收到的消息 I send the message 0
Listener 1: 接收到的消息 I send the message 2
UglyListener 1: 接收到的消息 I send the message 1
Listener 1: 接收到的消息 I send the message 4
Listener 1: 接收到的消息 I send the message 6
Listener 1: 接收到的消息 I send the message 8
UglyListener 1: 接收到的消息 I send the message 3
UglyListener 1: 接收到的消息 I send the message 5
UglyListener 1: 接收到的消息 I send the message 7
UglyListener 1: 接收到的消息 I send the message 9
I send the message 10
I send the message 11
I send the message 12
I send the message 13
I send the message 14
I send the message 15
I send the message 16
I send the message 17
I send the message 18
I send the message 19
Listener 1: 接收到的消息 I send the message 10
Listener 1: 接收到的消息 I send the message 12
Listener 1: 接收到的消息 I send the message 14
UglyListener 1: 接收到的消息 I send the message 11
Listener 1: 接收到的消息 I send the message 16
UglyListener 1: 接收到的消息 I send the message 13
Listener 1: 接收到的消息 I send the message 18
UglyListener 1: 接收到的消息 I send the message 15
UglyListener 1: 接收到的消息 I send the message 17
UglyListener 1: 接收到的消息 I send the message 19
I send the message 20
I send the message 21
I send the message 22
I send the message 23
I send the message 24
I send the message 25
I send the message 26
I send the message 27
I send the message 28
I send the message 29
Listener 1: 接收到的消息 I send the message 20
Listener 1: 接收到的消息 I send the message 22
Listener 1: 接收到的消息 I send the message 24
Listener 1: 接收到的消息 I send the message 26
Listener 1: 接收到的消息 I send the message 28
Send finish flag: FINISHED
UglyListener 1: 接收到的消息 I send the message 21
UglyListener 1: 接收到的消息 I send the message 23
UglyListener 1: 接收到的消息 I send the message 25
UglyListener 1: 接收到的消息 I send the message 27
UglyListener 1: 接收到的消息 I send the message 29
Listener 1: 接收到的消息 FINISHED
QueueProducer connection close!
QueueListenerConsumer connection close!

        这个时候有俩个消费者,一个消费者消费了一半的消息,这里我没有深入研究这种消费平均的方式是随机的还是真的平均,我猜猜可能是真的平均,因为我运行了多次都是同样的结果。

        通过我打出来的消息,发现只有一个消费者结束了,另一个并没有结束,因为没有收到结束消息标志,所以需要发送两个结束标志才能使两个消费者都能结束。

producer.sendFinishMessage(2);

        这也正是我设置参数的原因:

        改成2之后再来运行一次:

 INFO | Successfully connected to tcp://localhost:61616
 INFO | Successfully connected to tcp://localhost:61616
 INFO | Successfully connected to tcp://localhost:61616
I send the message 0
I send the message 1
I send the message 2
I send the message 3
I send the message 4
I send the message 5
I send the message 6
I send the message 7
I send the message 8
I send the message 9
Listener 1: 接收到的消息 I send the message 0
Listener 1: 接收到的消息 I send the message 2
UglyListener 1: 接收到的消息 I send the message 1
Listener 1: 接收到的消息 I send the message 4
UglyListener 1: 接收到的消息 I send the message 3
Listener 1: 接收到的消息 I send the message 6
UglyListener 1: 接收到的消息 I send the message 5
Listener 1: 接收到的消息 I send the message 8
UglyListener 1: 接收到的消息 I send the message 7
UglyListener 1: 接收到的消息 I send the message 9
I send the message 10
I send the message 11
I send the message 12
I send the message 13
I send the message 14
I send the message 15
I send the message 16
I send the message 17
I send the message 18
I send the message 19
Listener 1: 接收到的消息 I send the message 10
Listener 1: 接收到的消息 I send the message 12
Listener 1: 接收到的消息 I send the message 14
Listener 1: 接收到的消息 I send the message 16
Listener 1: 接收到的消息 I send the message 18
UglyListener 1: 接收到的消息 I send the message 11
UglyListener 1: 接收到的消息 I send the message 13
UglyListener 1: 接收到的消息 I send the message 15
UglyListener 1: 接收到的消息 I send the message 17
UglyListener 1: 接收到的消息 I send the message 19
I send the message 20
I send the message 21
I send the message 22
I send the message 23
I send the message 24
I send the message 25
I send the message 26
I send the message 27
I send the message 28
I send the message 29
Listener 1: 接收到的消息 I send the message 20
Listener 1: 接收到的消息 I send the message 22
Listener 1: 接收到的消息 I send the message 24
Listener 1: 接收到的消息 I send the message 26
Listener 1: 接收到的消息 I send the message 28
Send finish flag: FINISHED
Send finish flag: FINISHED
UglyListener 1: 接收到的消息 I send the message 21
UglyListener 1: 接收到的消息 I send the message 23
UglyListener 1: 接收到的消息 I send the message 25
UglyListener 1: 接收到的消息 I send the message 27
UglyListener 1: 接收到的消息 I send the message 29
Listener 1: 接收到的消息 FINISHED
UglyListener 1: 接收到的消息 FINISHED
QueueProducer connection close!
QueueReceiveConsumer connection close!
QueueListenerConsumer connection close!

        现在两个消费者都正常结束了。到此我的点对点模式就介绍完了。


订阅/发布模式


        生产者:

package com.darren.activemq.topic;

import javax.jms.JMSException;
import javax.jms.Session;
import javax.jms.TextMessage;

import com.darren.activemq.ActivemqContants;
import com.darren.activemq.ProducerConsumer;

public class TopicProducer extends ProducerConsumer {
    private int startNumber;
    private int endNumber;

    public TopicProducer(String name) throws JMSException {
        this.name = name;

        // 通过连接工厂获取连接
        this.connection = this.getConnection();
        // 启动连接
        this.connection.start();
        // 创建Session
        this.session = this.connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
        // 创建消息队列
        this.destination = this.session.createTopic("test-topic");
        // 创建消息生产者
        this.messageProducer = this.session.createProducer(destination);
    }

    /**
     * 发送消息
     * 
     * @throws JMSException
     */
    public void sendMessage() throws JMSException {
        this.startNumber = this.endNumber;
        this.endNumber = this.startNumber + MESSAGE_COUNT;
        for (int i = this.startNumber; i < this.endNumber; i++) {
            TextMessage message = this.session.createTextMessage("I send the message " + i);
            System.out.println(message.getText());
            this.messageProducer.send(message);
        }
    }

    /**
     * 发送结束标志
     * 
     * @throws JMSException
     */
    public void sendFinishMessage() throws JMSException {
        TextMessage message = this.session.createTextMessage(ActivemqContants.FINISH_FLAG);
        System.out.println("Send finish flag: " + message.getText());
        this.messageProducer.send(message);
    }

    /**
     * 提交事务
     * 
     * @throws JMSException
     */
    public void commit() throws JMSException {
        this.session.commit();
    }
}

        消费者:

package com.darren.activemq.topic;

import javax.jms.JMSException;
import javax.jms.Session;
import com.darren.activemq.ProducerConsumer;
import com.darren.activemq.listener.ConsumerListener;

public class TopicListenerConsumer extends ProducerConsumer {

    public TopicListenerConsumer(String name) throws JMSException {
        this.name = name;

        // 通过连接工厂获取连接
        this.connection = this.getConnection();
        // 启动连接
        this.connection.start();
        // 创建Session
        this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 创建连接的消息队列
        this.destination = this.session.createTopic("test-topic");
        // 创建消息消费者
        this.messageConsumer = this.session.createConsumer(destination);
        // 设置消息监听
        this.messageConsumer.setMessageListener(new ConsumerListener("Listener 1:", this));
    }
}

package com.darren.activemq.topic;

import javax.jms.JMSException;
import javax.jms.Session;
import com.darren.activemq.ProducerConsumer;
import com.darren.activemq.listener.UglyConsumerListener;

public class TopicReceiveConsumer extends ProducerConsumer {

    public TopicReceiveConsumer(String name) throws JMSException {
        this.name = name;

        // 通过连接工厂获取连接
        this.connection = this.getConnection();
        // 启动连接
        this.connection.start();
        // 创建Session
        this.session = this.connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 创建连接的消息队列
        this.destination = this.session.createTopic("test-topic");
        // 创建消息消费者
        this.messageConsumer = this.session.createConsumer(destination);

        // 启动一个异步线程接受消息,模拟一个消息监听器
        Thread thread = new Thread(new UglyConsumerListener("UglyListener 1:", this));
        thread.start();
    }
}

        大家可以看出相比点对点模式的例子,消息的订阅/发布模式只做了很小的改动:

this.destination = this.session.createQueue("test-queue");
//改成
this.destination = this.session.createTopic("test-topic");

        其他代码都重用了,接下来是测试类:

package com.darren.activemq.topic;

import javax.jms.JMSException;

public class TopicTest {
    public static void main(String[] args) {
        Thread thread = null;
        try {
            // 启动消费者,消费者开始等待
            new TopicListenerConsumer("TopicListenerConsumer");
            new TopicReceiveConsumer("TopicReceiveConsumer");

            thread = new Thread(new Runnable() {

                @Override
                public void run() {
                    try {
                        // 启动生产者,生产者定时生产消息
                        TopicProducer producer = new TopicProducer("TopicProducer");
                        Thread.sleep(2000);
                        // 第一次发送
                        producer.sendMessage();
                        producer.commit();

                        Thread.sleep(2000);
                        // 第二次发送
                        producer.sendMessage();
                        producer.commit();

                        Thread.sleep(2000);
                        // 第三次发送
                        producer.sendMessage();
                        producer.commit();

                        // 发送结束标志
                        producer.sendFinishMessage();
                        producer.commit();

                        // 生产者生产完成,关闭连接
                        producer.closeConnection();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

        运行这个例子:

 INFO | Successfully connected to tcp://localhost:61616
 INFO | Successfully connected to tcp://localhost:61616
 INFO | Successfully connected to tcp://localhost:61616
I send the message 0
I send the message 1
I send the message 2
I send the message 3
I send the message 4
I send the message 5
I send the message 6
I send the message 7
I send the message 8
I send the message 9
Listener 1: 接收到的消息 I send the message 0
Listener 1: 接收到的消息 I send the message 1
UglyListener 1: 接收到的消息 I send the message 0
Listener 1: 接收到的消息 I send the message 2
UglyListener 1: 接收到的消息 I send the message 1
Listener 1: 接收到的消息 I send the message 3
Listener 1: 接收到的消息 I send the message 4
UglyListener 1: 接收到的消息 I send the message 2
Listener 1: 接收到的消息 I send the message 5
UglyListener 1: 接收到的消息 I send the message 3
Listener 1: 接收到的消息 I send the message 6
Listener 1: 接收到的消息 I send the message 7
UglyListener 1: 接收到的消息 I send the message 4
Listener 1: 接收到的消息 I send the message 8
UglyListener 1: 接收到的消息 I send the message 5
Listener 1: 接收到的消息 I send the message 9
UglyListener 1: 接收到的消息 I send the message 6
UglyListener 1: 接收到的消息 I send the message 7
UglyListener 1: 接收到的消息 I send the message 8
UglyListener 1: 接收到的消息 I send the message 9
I send the message 10
I send the message 11
I send the message 12
I send the message 13
I send the message 14
I send the message 15
I send the message 16
I send the message 17
I send the message 18
I send the message 19
Listener 1: 接收到的消息 I send the message 10
Listener 1: 接收到的消息 I send the message 11
Listener 1: 接收到的消息 I send the message 12
Listener 1: 接收到的消息 I send the message 13
Listener 1: 接收到的消息 I send the message 14
Listener 1: 接收到的消息 I send the message 15
Listener 1: 接收到的消息 I send the message 16
Listener 1: 接收到的消息 I send the message 17
Listener 1: 接收到的消息 I send the message 18
Listener 1: 接收到的消息 I send the message 19
UglyListener 1: 接收到的消息 I send the message 10
UglyListener 1: 接收到的消息 I send the message 11
UglyListener 1: 接收到的消息 I send the message 12
UglyListener 1: 接收到的消息 I send the message 13
UglyListener 1: 接收到的消息 I send the message 14
UglyListener 1: 接收到的消息 I send the message 15
UglyListener 1: 接收到的消息 I send the message 16
UglyListener 1: 接收到的消息 I send the message 17
UglyListener 1: 接收到的消息 I send the message 18
UglyListener 1: 接收到的消息 I send the message 19
I send the message 20
I send the message 21
I send the message 22
I send the message 23
I send the message 24
I send the message 25
I send the message 26
I send the message 27
I send the message 28
I send the message 29
Listener 1: 接收到的消息 I send the message 20
Listener 1: 接收到的消息 I send the message 21
Listener 1: 接收到的消息 I send the message 22
Listener 1: 接收到的消息 I send the message 23
Listener 1: 接收到的消息 I send the message 24
Listener 1: 接收到的消息 I send the message 25
Listener 1: 接收到的消息 I send the message 26
Listener 1: 接收到的消息 I send the message 27
Send finish flag: FINISHED
UglyListener 1: 接收到的消息 I send the message 20
UglyListener 1: 接收到的消息 I send the message 21
Listener 1: 接收到的消息 I send the message 28
UglyListener 1: 接收到的消息 I send the message 22
Listener 1: 接收到的消息 I send the message 29
UglyListener 1: 接收到的消息 I send the message 23
UglyListener 1: 接收到的消息 I send the message 24
UglyListener 1: 接收到的消息 I send the message 25
UglyListener 1: 接收到的消息 I send the message 26
Listener 1: 接收到的消息 FINISHED
UglyListener 1: 接收到的消息 I send the message 27
UglyListener 1: 接收到的消息 I send the message 28
UglyListener 1: 接收到的消息 I send the message 29
UglyListener 1: 接收到的消息 FINISHED
TopicListenerConsumer connection close!
TopicReceiveConsumer connection close!
TopicProducer connection close!

        两个订阅者获取的消息一模一样,并不是平半分了消息,这就是订阅/发布与点对点的不同。

        注:订阅/发布模式必须要先订阅,这样订阅者才能收到消息。





ActiveMQ安装 : 1.下载:http://activemq.apache.org/download.html 最新Windows版本 2.配置jdk环境,解压ActiveMQ到本地 3.启动ActiveMQ服务器: 32位机 : 直接运行\bin\win32\activemq.bat 64位机 : 直接运行\bin\win64\activemq.bat 4.打开ActiveMQ消息管理后台系统 http://localhost:8161/admin/ 参考文章为 : HelloWorld : http://blog.csdn.net/baggio7095586/article/details/5708519 进阶 :http://haohaoxuexi.iteye.com/blog/1893038 helloworld阶段 : 一个发送消息的程序,一个接收消息的程序..实现最简单的JMS交流... 监听器阶段 : ⑴MessageListener,最简单的监听器..只是拿来监听接收信息的 ⑵SessionAwareMessageListener,有个session的参数..可以拿来对接收到的信息再做响应.. ⑶MessageListenerAdapter,将消息委托交给一个普通的java类来处理.. 转化器阶段 : 实现MessageConverter接口来转换JMS对象与java对象.. 其实在使用MessageListenerAdapter时,Spring默认为我们提供了SimpleMessageConverter转换器.. 事务(无代码): 在Spring整合JMS的应用中,如果我们要进行本地的事务管理的话非常简单,只需要在定义对应的消息监听容器时指定其sessionTransacted属性为true,如: <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer"> <property name="connectionFactory" ref="connectionFactory" /> <property name="destination" ref="queueDestination" /> <property name="messageListener" ref="consumerMessageListener" /> <property name="sessionTransacted" value="true"/> </bean> 该属性值默认为false,这样JMS在进行消息监听的时候就会进行事务控制,当在接收消息时监听器执行失败时JMS就会对接收到的消息进行回滚, 对于SessionAwareMessageListener在接收到消息后发送一个返回消息时也处于同一事务下,但是对于其他操作如数据库访问等将不属于该事务控制。 数据库跟JMS挂钩的事务先别理..
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值