1、什么是activemq

ActiveMQ 是Apache出品,最流行的、能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的JMS Provider实现。

主要特点:

1. 多种语言和协议编写客户端。

2. 对Spring的支持,ActiveMQ可以很容易内嵌到使用Spring的系统里面去。

3. 支持多种传送协议:in-VM,TCP,SSL,NIO,UDP,JGroups,JXTA

4. 支持Ajax

5. 支持与Axis的整合

6. 可以很容易得调用内嵌JMS provider,进行测试

2、ActiveMQ的消息形式

对于消息的传递有两种类型:

一种是点对点的,即一个生产者和一个消费者一一对应,这种方式基于queue队列

一种是发布/订阅模式,即一个生产者产生消息并进行发送后,可以由多个消费者进行接收(一对多,这种方式基于topic

activemq 版本如何选择 javav activemq入门_对象创建

对于点对点的消息传输模型来说,可以在同一个queue上面注册多个生产者和多个消费者,当消息的生产者发送一条消息时,只有其中的一个消息消费者接收到消息生产者发送的消息,而不是所有消息消费者都会接收到该消息。

对于发布/订阅者模式来说,消息的发布者需将消息投递给topic,而消息的订阅者则需要在相应的topic进行注册,以便接受相应topic的消息。与点对点传输模型不同的是,消息发布者的消息将被自动发送给所有订阅了该topic的消息订阅者。当消息订阅者某段时间由于某种原因断开了与消息发布者的连接时,这个时间段的消息将会丢失,除非将消息的订阅模式设置为持久订阅,这时消息的发布者将会为消息的订阅者保留这段时间所产生的消息。当消息的订阅者重新连接消息发布者时,消息订阅者仍然可以获得这部分消息,而不至于丢失这部分消息。

JMS定义了五种不同的消息正文格式,以及调用的消息类型,允许你发送并接收一些不同形式的数据,提供现有消息格式的一些级别的兼容性。

  · StreamMessage -- Java原始值的数据流

  · MapMessage--一套名称-值对

  · TextMessage--一个字符串对象

  · ObjectMessage--一个序列化的 Java对象

  · BytesMessage--一个字节的数据流

2、如何安装activemq

第一步:进入http://activemq.apache.org/下载ActiveMQ

第二步:将下载好的安装包上传到linux系统中,并解压(tar -zxvf命令)

第三步:启动。 ./activema start

使用bin目录下的activemq命令启动:
[root@localhost bin]# ./activemq start

关闭:
[root@localhost bin]# ./activemq stop

查看状态:
[root@localhost bin]# ./activemq status

 

第四步:在浏览器中输入http://你的ip:8161/admin, 用户名:admin, 密码:admin,可以看到activemq的后台管理页面

activemq 版本如何选择 javav activemq入门_activemq_02

3、activemq的使用

在maven项目中导入activemq的依赖包:

<dependency>
     <groupId>org.apache.activemq</groupId>
     <artifactId>activemq-all</artifactId>
     <version>5.11.2</version>
</dependency>

   3.1 点对点(queue)

       3.1.1 生产者代码

public void testQueueProducer() throws Exception {
        //第一步:创建connectionFactory, 指定ip和端口
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://你的ip:61616");
        //第二步:通过connectionFactory创建connection
        Connection connection = connectionFactory.createConnection();
        // 第三步:开启连接,调用Connection对象的start方法。
        connection.start();
        // 第四步:使用Connection对象创建一个Session对象。
        //第一个参数:是否开启事务。true:开启事务,第二个参数忽略。
        //第二个参数:当第一个参数为false时,才有意义。消息的应答模式。1、自动应答2、手动应答。一般是自动应答。
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 第五步:使用Session对象创建一个Destination对象(topic、queue),此处创建一个Queue对象。
        //参数:队列的名称。
        Destination destination = session.createQueue("test-queue");
        // 第六步:使用Session对象创建一个Producer对象。
        MessageProducer producer = session.createProducer(destination);
        // 第七步:创建一个Message对象,创建一个TextMessage对象。
        /*TextMessage message = new ActiveMQTextMessage();
        message.setText("hello activemq");*/
        TextMessage message = session.createTextMessage("hello activemq queue");
        // 第八步:使用Producer对象发送消息。
        producer.send(destination, message);
        // 第九步:关闭资源。
        producer.close();
        session.close();
        connection.close();
    }

       3.1.2 消费者代码

public void testQueueConsumer() throws Exception {
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://你的ip:61616");
        Connection connection = connectionFactory.createConnection();
        connection.start();
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Destination destination = session.createQueue("test-queue");
        MessageConsumer consumer = session.createConsumer(destination);

        //为consumer设置一个监听器
        consumer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message) {
                try {
                    TextMessage msg = (TextMessage)message;
                    System.out.println(msg.getText());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });


        //等待键盘输入
        System.in.read();

        //关闭资源
        consumer.close();
        session.close();
        connection.close();
    }

首先运行生产者的代码,在后台管理界面看到已经创建了一个叫test-queue的队列,并且有一条的消息入队。

activemq 版本如何选择 javav activemq入门_对象创建_03

再运行消费者代码:可以看到刚才未发送的消息已经出队了,并且在控制台接收到了发送的消息

activemq 版本如何选择 javav activemq入门_solr_04

activemq 版本如何选择 javav activemq入门_spring_05

 

   3.2 发布/订阅模式(topic)

       3.2.1 生产者

public void testTopicProducer() throws Exception {
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://你的ip:61616");
        //第二步:通过connectionFactory创建connection
        Connection connection = connectionFactory.createConnection();
        // 第三步:开启连接,调用Connection对象的start方法。
        connection.start();
        // 第四步:使用Connection对象创建一个Session对象。
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 第五步:使用Session对象创建一个Destination对象
        Destination destination = session.createTopic("test-topic");
        // 第六步:使用Session对象创建一个Producer对象。
        MessageProducer producer = session.createProducer(destination);
        // 第七步:创建一个Message对象,创建一个TextMessage对象。
        TextMessage message = session.createTextMessage("hello activemq topic");
        // 第八步:使用Producer对象发送消息。
        producer.send(destination, message);
        // 第九步:关闭资源。
        producer.close();
        session.close();
        connection.close();
    }

       3.2.2 消费者(为了便于测试,设置了如下的3个消费者)

public void testTopicConsumer01() throws Exception {
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://你的ip:61616");
        Connection connection = connectionFactory.createConnection();
        connection.start();
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        Destination destination = session.createTopic("test-topic");
        MessageConsumer consumer = session.createConsumer(destination);

        //为consumer设置一个监听器
        consumer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message) {
                try {
                    TextMessage msg = (TextMessage)message;
                    System.out.println(msg.getText());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        System.out.println("topic的消费端01。。。。。");

        //等待键盘输入
        System.in.read();

        //关闭资源
        consumer.close();
        session.close();
        connection.close();
    }

由于点对点这种模式,消息默认是持久化到activemq中的,即使消费者暂时不在线,但是当消费者上线时,还是会收到消息。而发布者/订阅者模式默认则不会持久化,当消费者暂时断线时,则这段时间的消息就会丢失。因此我们先将3个消费者运行起来,再运行生产者。

4、activemq与spring整合

生产者配置文件:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">

    <!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 -->
    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="tcp://你的ip:61616" />
    </bean>
    <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->
    <bean id="connectionFactory"
          class="org.springframework.jms.connection.SingleConnectionFactory">
        <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->
        <property name="targetConnectionFactory" ref="targetConnectionFactory" />
    </bean>

    <!-- 配置生产者 -->
    <!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="connectionFactory"></property>
    </bean>

    <!--队列目的地址-->
    <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg name="name" value="item-change-queue"></constructor-arg>
    </bean>

    <!-- 主题目的地址 -->
    <bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
        <constructor-arg name="name" value="item-change-topic"></constructor-arg>
    </bean>
</beans>

activemq 版本如何选择 javav activemq入门_对象创建_06

业务逻辑:在添加新的商品时,同时生成该商品的索引。

@Override
    public TaotaoResult addItem(TbItem item, String desc, String itemParams) {
        //生成商品id
        long itemId = IDUtils.genItemId();
        item.setId(itemId);
        //设置商品状态 '商品状态,1-正常,2-下架,3-删除',
        item.setStatus((byte) 1);
        //设置商品创建时间
        Date date = new Date();
        item.setCreated(date);
        item.setUpdated(date);

        itemMapper.insert(item);

        //商品描述
        TbItemDesc itemDesc = new TbItemDesc();
        itemDesc.setItemId(itemId);
        itemDesc.setItemDesc(desc);
        itemDesc.setCreated(date);
        itemDesc.setUpdated(date);
        itemDescMapper.insert(itemDesc);

        //添加一个商品信息后,向activemq的topic中发送商品id
        jmsTemplate.send(topicDestination, new MessageCreator() {
            @Override
            public Message createMessage(Session session) throws JMSException {
                TextMessage message = session.createTextMessage(itemId + "");
                return message;
            }
        });

        return TaotaoResult.ok();
    }

 

消费者配置文件:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">

    <!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 -->
    <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
        <property name="brokerURL" value="tcp://你的ip:61616" />
    </bean>
    <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->
    <bean id="connectionFactory"
          class="org.springframework.jms.connection.SingleConnectionFactory">
        <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->
        <property name="targetConnectionFactory" ref="targetConnectionFactory" />
    </bean>

    <!--队列目的地址-->
    <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
        <constructor-arg name="name" value="item-change-queue"></constructor-arg>
    </bean>
    <!-- 主题目的地址 -->
    <bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
        <constructor-arg name="name" value="item-change-topic"></constructor-arg>
    </bean>

    <!-- 配置消息监听器 -->
    <bean id="messageListener" class="com.taotao.search.listener.ItemChangeMessageListener"></bean>
    <!-- 消息监听容器 -->
    <bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
        <property name="connectionFactory" ref="connectionFactory"></property>
        <property name="destination" ref="topicDestination"></property>
        <property name="messageListener" ref="messageListener"></property>
    </bean>
</beans>

监听器代码:

public class ItemChangeMessageListener implements MessageListener {

    @Autowired
    private SearchItemService searchItemService;

    @Override
    public void onMessage(Message message) {
        try {
            TextMessage textMessage = null;
            Long itemId = null;
            if(message instanceof TextMessage) {
                textMessage = (TextMessage)message;
                itemId = Long.parseLong(textMessage.getText());
            }

            //向索引库添加文档
            searchItemService.addDocument(itemId);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

监听器监听到来自新增商品的id后,在数据库中查询到该商品的详细信息,通过solrJ为该商品新建索引。

@Override
    public TaotaoResult addDocument(long itemId) throws Exception {
        SearchItem searchItem = searchItemMapper.getItemById(itemId);
        SolrInputDocument document = new SolrInputDocument();
        document.addField("id", searchItem.getId());
        document.addField("item_title", searchItem.getTitle());
        document.addField("item_sell_point", searchItem.getSell_point());
        document.addField("item_price", searchItem.getPrice());
        document.addField("item_image", searchItem.getImage());
        document.addField("item_category_name", searchItem.getCategory_name());
        document.addField("item_desc", searchItem.getItem_desc());
        solrServer.add(document);
        solrServer.commit();
        return TaotaoResult.ok();
    }