电商项目日总结(第十三天消息中间件解决方案JMS)

1.消息中间件解决方案出现的背景:

目前父工程pyg_parent模块下有多个子模块,而且子模块之间的调用关系很多,如shop_web(商家模块),调用关系最多,用到了图下四种服务,这些模块之间的依赖关系称为耦合,耦合越多,维护就越困难.由此为了改善系统模块调用关系、减少模块之间的耦合,消息中间件就产生了

2.什么是消息中间件

消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。对于消息中间件,常见的角色大致也就有Producer(生产者)、Consumer(消费者),常见的消息中间件的产品:ActiveMQ,RabbitMQ,ZeroMQ,Kafka

3.项目中引入消息中间件activeMQ后,使pyg_shop_web商家系统与solr搜索服务、freemarker静态页面生成服务解除了耦合,如图:

4.JMS就是Java平台上有关面向消息中间件的技术规范,它便于消息系统中的Java应用程序进行消息交换,JMS就是一种提供给外界厂商使用的接口规范,用来访问消息收发系统,类似于JDBC(Java DataBase Connectivity)

5.JMS 定义的五种不同的消息类型:

TextMessage-----一个字符串对象

MapMessage-----一套名称-值对

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

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

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

6.JMS的两种消息传递类型

一是点对点模式,即一个生产者和一个消费者一一对应.这种模式可以先发消息,然后再开启监听.可以有多个消费者,但生产者发送一个消息只能有一个消费者接收,其他消费者不接收

二是发布/ 订阅模式,即一个生产者产生消息并进行发送后,可以由多个消费者进行接收.这种模式是先在consumer消费方提供监听并先于producer生产方开启.一个消息发送可以被多个消费者接收(例子:老师在讲课发送消息,全班的学生接收消息)

7.今日完成了在虚拟机的Linux服务器上安装ActiveMQ

将apache-activemq-5.14.5-linux.tar.gz拖到Linux服务器上的soft目录下,并完成解压

接着进入解压后的apache-activemq-5.14.5文件目录下的bin目录下,开启activeMQ的服务

在本机上谷歌浏览器上访问http://192.168.25.128:8161(activemq的后台页面,控制台)测试activemq服务是否开启成功,成功页面如下:

输入用户名和密码即可访问(都是admin)

8.完成了一个Spring整合JMS的Demo

创建一个新模块SpringJmsDemo,在模块中完成练习

在pom文件中导入依赖activemq相关的依赖

<properties>
    <spring.version>4.2.4.RELEASE</spring.ve
</properties>
<dependencies>
    <!-- Spring -->
    <dependency>
        <groupId>org.springframework</groupI
        <artifactId>spring-context</artifact
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupI
        <artifactId>spring-beans</artifactId
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupI
        <artifactId>spring-jms</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupI
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.activemq</groupI
        <artifactId>activemq-all</artifactId
        <version>5.11.2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.9</version>
    </dependency>
</dependencies>

在resources目录下新建一个spring目录

分别拷贝applicationContext-activemq-producer.xml配置文件

<context:component-scan base-package="cn.itcast.demo"></context:component-scan>     
   
<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->  
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFacto
    <property name="brokerURL" value="tcp://192.168.25.128:61616"/>
</bean>
   
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->  
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnecti
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
    <property name="targetConnectionFactory" ref="targetConnectionFactory"/>  
</bean>  
	   
<!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->  
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">  
    <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->  
    <property name="connectionFactory" ref="connectionFactory"/>  
</bean>      
<!--这个是队列目的地,点对点的  文本信息-->  
<bean id="queueTextDestination" class="org.apache.activemq.command.ActiveMQQueue">  
    <constructor-arg value="queue_text"/>  
</bean>    
<!--这个是订阅模式  文本信息-->  
<bean id="topicTextDestination" class="org.apache.activemq.command.ActiveMQTopic">  
    <constructor-arg value="topic_text"/>  
</bean>  

和applicationContext-activemq-consumer.xml的配置文件

<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->  
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
    <property name="brokerURL" value="tcp://192.168.25.128:61616"/>
</bean>
   
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->  
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFacto
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
    <property name="targetConnectionFactory" ref="targetConnectionFactory"/>  
</bean>  
<!--这个是队列目的地,点对点的  文本信息-->  
<bean id="queueTextDestination" class="org.apache.activemq.command.ActiveMQQueue">  
    <constructor-arg value="queue_text"/>  <!--value值必须和producter配置文件中消息的名称对应-->
</bean>
<!-- 我的监听类 -->
<bean id="myMessageListener" class="cn.itcast.demo.MyMessageListener"></bean>
<!-- 消息监听容器 -->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
	<property name="connectionFactory" ref="connectionFactory" />
	<property name="destination" ref="queueTextDestination" />
	<property name="messageListener" ref="myMessageListener" />
</bean>

一、练习通过SpringJMS发送Queue的消息

首先根据applicationContext-activemq-producer.xml配置文件,创建一个包扫描的目录,并在目录下创建一个对象,用来发送消息

@Component
public class SpringJmsQueueProductSend {
    @Autowired
    private JmsTemplate jmsTemplate;//JavaJms整合用来操作activemq的工具类
    @Autowired
    private ActiveMQQueue queueTextDestination;//这个是Queue的类型的消息,看applicationContext-activemq-producer.xml配置文件

    @Test
    public void sendQueue(final String value){//匿名内部类只可以引用外面被final修饰的参数
        //参数传一个Queue类型的消息和一个文本类型的消息
        jmsTemplate.send(queueTextDestination, new MessageCreator() {
            @Override
            public Message createMessage(Session session) throws JMSException {
                //原生的是从连接中获取session,再通过session发送信息
                return session.createTextMessage(value);
            }
        });
    }
}

建立一个测试方法,用来模拟一个发送Queue消息的服务器,调用sendQueue方法并传递参数

ApplicationContext ac = new ClassPathXmlApplicationContext("spring/applicationContext-activemq-producer.xml");
SpringJmsQueueProductSend sqps= (SpringJmsQueueProductSend) ac.getBean("springJmsQueueProductSend");
sqps.sendQueue("spring整合的jms消息中间件生产者发送的queue");
System.out.println("发送完毕");

根据applicationContext-activemq-consumer.xml配置文件在对应的目录下建立一个监听器,名称必须对应,而且这个监听器必须实现MessageListener接口,实现接口中onMessage方法

@Override
public void onMessage(Message message) {
    //根据消息发出的类型来转成对应的类型(看session.create*****什么方法)
    TextMessage textMessage= (TextMessage) message;
    try {
        System.out.println(textMessage.getText());
    } catch (JMSException e) {
        e.printStackTrace();
    }
}

建立一个类用来读取applicationContext-activemq-consumer.xml配置文件中的配置,生成监听器,并且模拟服务器一直开启的状态,监听器处于一直监听状态

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/applicationContext-activemq-consumer.xml")
public class SpringQueueReceiverTest {
    @Test
    public void run(){
        while (true){

        }
    }
}

开始验证:顺序:先发送消息(运行测试方法传递参数),然后开启监听,结果是监听器收到Text类型的消息

发送端:

监听端:

验证成功,发送的Queue模式的消息如果有多个监听器,但只发送一个消息,结果只有一个监听器会监听到

二、练习通过SpringJMS发送Topic的消息

在配置文件中的包扫描路径下建立一个类发送Topic类型的消息

@Component
public class SpringJmsTopicProductSend {
    @Autowired
    private JmsTemplate jmsTemplate;
    @Autowired
    private ActiveMQTopic topicTextDestination;
    @Test
    public void sendTopic(){
        jmsTemplate.send(topicTextDestination, new MessageCreator() {
            @Override
            public Message createMessage(Session session) throws JMSException {
                MapMessage mapMessage = session.createMapMessage();
                mapMessage.setString("name","LBH");
                mapMessage.setString("password","123");
                return mapMessage;
            }
        });
    }
}

还是看配置文件,传入配置文件中的Topic(订阅模式)的消息

在applicationContext-activemq-consumer.xml的配置文件中添加配置监听器,需要加上Topic类型的消息配置,在下面配置以下内容

<!--这个是订阅模式  文本信息-->
<bean id="topicTextDestination" class="org.apache.activemq.command.ActiveMQTopic">
	<constructor-arg value="topic_text"/>
</bean>
<!-- 我的监听类 -->
<bean id="myMessageListener" class="cn.itcast.demo.MyMessageListener"></bean>
<!-- 消息监听容器 -->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
	<property name="connectionFactory" ref="connectionFactory" />
	<property name="destination" ref="topicTextDestination" /><!--监听的目标Topic类型的消息-->
	<property name="messageListener" ref="myMessageListener" />
</bean>

创建监听器来监听Topic类型的消息,实现MessageListener接口

public void onMessage(Message message) {
    MapMessage mapMessage= (MapMessage) message;
    try {
        System.out.println(mapMessage.getString("name"));
        System.out.println(mapMessage.getString("password"));
    } catch (JMSException e) {
        e.printStackTrace();
    }
}

还是建立一个类用来读取applicationContext-activemq-consumer.xml配置文件中的配置,生成监听器,并且模拟服务器一直开启的状态,监听器处于一直监听状态

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/applicationContext-activemq-consumer.xml")
public class SpringQueueReceiverTest {
    @Test
    public void run(){
        while (true){

        }
    }
}

开始验证,Topic订阅模式要先开启监听器,然后在发送消息(注意顺序)

发送端:

接收端:

Topic模式验证成功

9.解除了pyg_shop_web模块与search_web、page_web模块之间的耦合,在商品上架成功之后去solr库添加商品数据(Queue消息)、生成商品静态页面(Topic消息),商品下架之后去solr库中删除数据、删除商品静态页面.(原理:通过activemq发送消息给search模块和page模块)

步骤:

删除shop_web的pom文件中与search和page模块之间的依赖,弃用dubbo服务

删除之后shop_web模块的pom文件

<dependencies>
    <dependency>
        <groupId>com.fighting</groupId>
        <artifactId>pyg_sellergoods_interface</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>com.fighting</groupId>
        <artifactId>pyg_common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

在shop_web模块的spring目录下添加spring-activemq.xml配置文件

<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->  
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
    <property name="brokerURL" value="tcp://192.168.25.128:61616"/>
</bean>
   
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->  
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFacto
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
    <property name="targetConnectionFactory" ref="targetConnectionFactory"/>  
</bean>  
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">  
    <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->  
    <property name="connectionFactory" ref="connectionFactory"/>  
</bean>   
<!-- solr库添加内容的queue消息-->  
<bean id="queueSolrCreateDestination" class="org.apache.activemq.command.ActiveMQQueue">  
    <constructor-arg value="pinyougou_queue_solr_create"/>  
</bean>
<!-- solr库删除内容的queue消息-->  
<bean id="queueSolrDeleteDestination" class="org.apache.activemq.command.ActiveMQQueue">  
    <constructor-arg value="pinyougou_queue_solr_delete"/>  
</bean>  
<!-- 生成页面的topic消息 -->
<bean id="topicPageCreateDestination" class="org.apache.activemq.command.ActiveMQTopic">  
    <constructor-arg value="pinyougou_topic_page_create"/>  
</bean>   
<!-- 删除页面的topic消息 -->
<bean id="topicPageDeleteDestination" class="org.apache.activemq.command.ActiveMQTopic">  
    <constructor-arg value="pinyougou_topic_page_delete"/>  
</bean>  

修改web.xml配置文件

shop_web模块下的GoodsController中:

@Autowired
private JmsTemplate jmsTemplate;
//引入四个消息
@Autowired
private ActiveMQQueue queueSolrCreateDestination;
@Autowired
private ActiveMQQueue queueSolrDeleteDestination;
@Autowired
private ActiveMQTopic topicPageCreateDestination;
@Autowired
private ActiveMQTopic topicPageDeleteDestination;


@RequestMapping("/updateStatus")
public Result updateStatus(Long[] ids, String status) {
    try {
        goodsService.updateStatus(ids, status);
        //如果商品上架或者是下架,就去把数据添加到solr中或者从solr中移除
        if ("5".equals(status)) {
            //就是上架,就要把新的SKU数据添加到solr中
            //itemSearchService.importItems(ids);解除原来的dubbo耦合去找itemSearchService类
            //用ActiveMq去发送Queue(队列)类型的消息
            jmsTemplate.send(queueSolrCreateDestination, new MessageCreator() {
                @Override
                public Message createMessage(Session session) throws JMSException {
                    return session.createObjectMessage(ids);
                }
            });
            //如果商品上架成功,就根据FreeMarker的模板根据id创建指定的商品页面,一个id一个网页
            //一个Good一个网页
            /*for (Long id : ids) {
                //生成静态页面
                itemPageService.createItemHtml(id);
            }*/
            jmsTemplate.send(topicPageCreateDestination, new MessageCreator() {
                @Override
                public Message createMessage(Session session) throws JMSException {
                    return session.createObjectMessage(ids);
                }
            });
        }
        if ("6".equals(status)) {
            //就是下架,将solr中对应的SKU数据删除
            //itemSearchService.removeItems(ids);
            jmsTemplate.send(queueSolrDeleteDestination, new MessageCreator() {
                @Override
                public Message createMessage(Session session) throws JMSException {
                    return session.createObjectMessage(ids);
                }
            });
            //如果商品下架,删除对应的静态页面
            //itemPageService.removeItemHtml(ids);
            jmsTemplate.send(topicPageDeleteDestination, new MessageCreator() {
                @Override
                public Message createMessage(Session session) throws JMSException {
                    return session.createObjectMessage(ids);
                }
            });
        }
        return new Result(true, "修改商品状态成功");
    } catch (Exception e) {
        e.printStackTrace();
        return new Result(false, "修改商品状态失败");
    }
}

pyg_search_service模块中spring目录下添加配置文件applicationContext-activemq-consumer.xml,控制监听的,接收消息的

<context:component-scan base-package="com.pinyougou.search.service.impl"/>
<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->  
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
    <property name="brokerURL" value="tcp://192.168.25.128:61616"/>
</bean>
   
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->  
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">  
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
    <property name="targetConnectionFactory" ref="targetConnectionFactory"/>  
</bean>  
 <!-- solr库添加内容的queue消息-->  
<bean id="queueSolrCreateDestination" class="org.apache.activemq.command.ActiveMQQueue">  
    <constructor-arg value="pinyougou_queue_solr_create"/>  
</bean>
<!-- solr库删除内容的queue消息-->  
<bean id="queueSolrDeleteDestination" class="org.apache.activemq.command.ActiveMQQueue">  
    <constructor-arg value="pinyougou_queue_solr_delete"/>  
</bean>  
<!-- 消息监听容器 -->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
	<property name="connectionFactory" ref="connectionFactory" />
	<property name="destination" ref="queueSolrCreateDestination" />
	<property name="messageListener" ref="itemSearchCreateListener" />
</bean>
<!-- 消息监听容器 -->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
	<property name="connectionFactory" ref="connectionFactory" />
	<property name="destination" ref="queueSolrDeleteDestination" />
	<property name="messageListener" ref="itemSearchDeleteListener" />
</bean>

根据applicationContext-activemq-consumer.xml配置文件在包扫描的目录下建立两个监听去solr库里生成和删除商品数据消息的类

ItemSearchCreateListener中:

//根据配置文件创建一个监听器(负责接收去solr库中添加商品的监听器)
@Component
public class ItemSearchCreateListener implements MessageListener {
    @Autowired
    private ItemSearchService itemSearchService;
    @Override
    public void onMessage(Message message) {
        ObjectMessage objectMessage= (ObjectMessage) message;
        try {
            Long[] ids= (Long[]) objectMessage.getObject();
            //去solr库中添加商品
            itemSearchService.importItems(ids);
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

ItemSearchDeleteListener中:

@Component
public class ItemSearchDeleteListener implements MessageListener {
    @Autowired
    private ItemSearchService itemSearchService;
    @Override
    public void onMessage(Message message) {
        ObjectMessage objectMessage= (ObjectMessage) message;
        try {
            Long[] ids= (Long[]) objectMessage.getObject();
            itemSearchService.removeItems(ids);
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

pyg_page_service模块中也同样添加一个监听静态页面生成的配置文件applicationContext-activemq-consumer.xml

applicationContext-activemq-consumer.xml中:

<context:component-scan base-package="com.pinyougou.page.service.impl"/>
<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供-->  
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">  
    <property name="brokerURL" value="tcp://192.168.25.128:61616"/>
</bean>
   
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->  
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">  
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->  
    <property name="targetConnectionFactory" ref="targetConnectionFactory"/>  
</bean>  
<!-- 生成页面的topic消息 -->
<bean id="topicPageCreateDestination" class="org.apache.activemq.command.ActiveMQTopic">  
    <constructor-arg value="pinyougou_topic_page_create"/>  
</bean>   
<!-- 删除页面的topic消息 -->
<bean id="topicPageDeleteDestination" class="org.apache.activemq.command.ActiveMQTopic">  
    <constructor-arg value="pinyougou_topic_page_delete"/>  
</bean>  
<!-- 消息监听容器 -->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
	<property name="connectionFactory" ref="connectionFactory" />
	<property name="destination" ref="topicPageCreateDestination" />
	<property name="messageListener" ref="itemPageCreateListener" />
</bean>
<!-- 消息监听容器 -->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
	<property name="connectionFactory" ref="connectionFactory" />
	<property name="destination" ref="topicPageDeleteDestination" />
	<property name="messageListener" ref="itemPageDeleteListener" />
</bean>

ItemPageCreateListener中:

@Component
public class ItemPageCreateListener implements MessageListener {
    @Autowired
    private ItemPageService itemPageService;
    @Override
    public void onMessage(Message message) {
        ObjectMessage objectMessage= (ObjectMessage) message;
        try {
            Long[] ids= (Long[]) objectMessage.getObject();
            for (Long id : ids) {
                itemPageService.createItemHtml(id);
            }
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

ItemPageDeleteListener中:

@Component
public class ItemPageDeleteListener implements MessageListener {
    @Autowired
    private ItemPageService itemPageService;
    @Override
    public void onMessage(Message message) {
        ObjectMessage objectMessage= (ObjectMessage) message;
        try {
            Long[] ids= (Long[]) objectMessage.getObject();
            itemPageService.removeItemHtml(ids);
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

完成!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值