文章目录
目标
- 理解消息中间件、JMS等概念
- 掌握JMS点对点与发布订阅模式的收发消息
- 掌握SpringJMS
- 完成商品审核导入索引库
- 完成商品删除移除索引库
- 完成商品审核生成商品详情页
- 完成商品删除完成删除商品详情页
1. JMS入门
1. 消息中间件
1. 品优购系统模块调用关系分析
先将品优购已完成的功能模块梳理
已经完成5个web模块和4个服务模块,其中运营商后台的调用关系最多,用到商家商品服务、广告内容服务、搜索服务和页面生成服务。这种模块之间的依赖也称为耦合。如何解耦?采用消息中间件。
2. 什么是消息中间件
消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并给予数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,可以在分布式环境下扩展进程间的通信。对于消息中间件,常见的角色有provider、consumer
常见的产品
- ActiveMQ
是Apache出品,最流行的、能力强劲的开源消息总线。完全支持JMS1.1和J2EE1.4规范的JMS Provider实现。
- RabbitMQ
AMOP协议的领导实现。支持多种场景。淘宝的Mysql集群内部使用它进行通讯。最先在金融行业得到运用。
- ZeroMQ
史上最快的消息队列系统
- kafka
apache下的一个子项目。特点:高吞吐。普通服务器上可达到10W/s的吞吐量。适合海量数据。
3. 改造系统模块调用
我们通过引入消息中间件activeMQ,使得运营商系统和搜索服务、页面生成服务解耦合
2. JMS简介
1. 什么是JMS
JMS(java messaging service)是java平台上有关面向消息中间件的技术规范。便于消息系统中的java应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接口简化企业应用的开发。
JMS本身只定义了一系列的接口规范,是一种与厂商无关的API,用来访问消息收发系统。类似JDBC。JMS使您能够通过消息收发服务从一个JMS客户机向另一个JML客户机发送消息。消息是JMS中的一种类型对象。由两部分组成:报头和消息主体。报头由路由信息以及有关该消息的元数据组成。消息主体携带着应用程序的数据或有效负荷。
JMS定义了五种不同的消息正文格式,以及调用的消息类型,允许你发送并接收以一些不同形式的数据,提供现有消息格式的一些级别的兼容性。
- TextMessage:一个字符串对象
- MapMessage:一套名称-值对
- ObjectMessage:一个序列化的java对象
- BytesMessage:一个字节的数据流
- StreamMessage:java原始值的数据流
2. JMS消息传递类型
对于消息的传递有两种类型
一种是点对点的,即一个生产者和一个消费者一一对应
另一种是发布/订阅模式,即一个生产者产生消息并发送后,可以由多个消费者进行接收
3. ActiveMQ的下载与安装
安装:
解压
tar zxvf apache-activemq-5.12.0-bin.tar.gz
赋权
chmod 777 apache-activemq-5.12.0
进入bin目录
赋予执行权限
chmod 755 activemq
linux 命令chmod 755的意思
chmod是Linux下设置文件权限的命令,后面的数字表示不同用户或用户组的权限。
一般是三个数字:
第一个数字表示文件所有者的权限
第二个数字表示与文件所有者同属一个用户组的其他用户的权限
第三个数字表示其它用户组的权限。
权限分为三种:读(r=4),写(w=2),执行(x=1) 。 综合起来还有可读可执行(rx=5=4+1)、可读可写(rw=6=4+2)、可读可写可执行(rwx=7=4+2+1)
所以,chmod 755 设置用户的权限为:
1.文件所有者可读可写可执行
2.与文件所有者同属一个用户组的其他用户可读可执行
3.其它用户组可读可执行
启动
./activemq start
打开浏览器输入http://192.168.25.128:8161
,进入ActiveMQ管理页面,点击Manage ActiveMQ broker
进入管理页面。
用户名和密码均为admin,主界面Queue为点对点,列表各列信息含义:
- Number Of Pending Messages:等待消费的消息 这个事当前未出队列的数量
- Number Of Consumers:消费者 这个是消费者端的消费者数量
- Messages Enqueued:进入队列的消息 进入队列的总数量,包括出队列的
- Messages Dequeue:出了队列的消息 消费掉的数量
2. JMS入门demo
1. 点对点模式
点对点的模式主要建立在一个队列上面,当连接一个队列时,发送端不需要知道接收端是否正在接收,可以直接向ActiveMQ发送消息,发送的消息,将会先进入队列中,如果有接收端在监听,则会发向接收端,如果没有接收端接收,会保存在activemq服务器,知道接收端接收消息,点对点的消息模式可以有多个发送端,多个接收端,但是一条消息,只会被一个接收端接收,哪个接收端先连上activeMQ,会先接收到,后来的接收端则接收不到那条消息。
应用场景:搜索服务、发送短信
1. 消息生产者
- 创建工程JMSDemo,引入依赖
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-client</artifactId>
<version>5.13.4</version>
</dependency>
- 创建类QueueProducer
public class QueueProducer {
public static void main(String[] args) throws JMSException {
// 1. 创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.128:61616");
// 2. 创建连接
Connection connection = connectionFactory.createConnection();
// 3. 启动连接
connection.start();
// 4. 获取session会话对象 参数1:是否启动事务 参数2 消息确认方式
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 5. 创建队列对象
Queue queue = session.createQueue("test-queue");
// 6. 创建消息生产者对象
MessageProducer producer = session.createProducer(queue);
// 7. 创建消息对象(文本消息)
TextMessage textMessage = session.createTextMessage("欢迎来到神奇的品优购世界");
// 8. 发送消息
producer.send(textMessage);
// 9. 关闭资源
producer.close();
session.close();
connection.close();
}
}
第四步参数1 是否启用事务;参数2 消息的确认模式
- AUTO_ACKNOWLEDGE=1 自动确认
- CLIENT_ACKNOWLEDGE=2 客户端手动确认
- DUPS_OK_ACKNOWLEDGE=3 自动批量确认
- SESSION_TRANSACTED=0 事务提交并确认
2. 消息消费者
创建类
public class QueueConsumer {
public static void main(String[] args) throws JMSException, IOException {
// 1. 创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.128:61616");
// 2. 创建连接
Connection connection = connectionFactory.createConnection();
// 3. 启动连接
connection.start();
// 4. 获取session会话对象 参数1:是否启动事务 参数2 消息确认方式
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 5. 创建队列对象
Queue queue = session.createQueue("test-queue");
// 6. 创建消费者对象
MessageConsumer consumer = session.createConsumer(queue);
// 7. 设置监听
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage)message;
try {
System.out.println("提取的消息为:"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
// 8. 等待键盘输入
System.in.read();
// 9. 关闭资源
consumer.close();
session.close();
connection.close();
}
}
2. 发布/订阅模式
应用场景:页面生成,很多商品详情页都需要该服务。
1. 消息生产者
public class TopicProducer {
public static void main(String[] args) throws JMSException {
// 1. 创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.128:61616");
// 2. 创建连接
Connection connection = connectionFactory.createConnection();
// 3. 启动连接
connection.start();
// 4. 获取session会话对象 参数1:是否启动事务 参数2 消息确认方式
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 5. 创建队列对象
Topic topic = session.createTopic("test-topic");
// 6. 创建消息生产者对象
MessageProducer producer = session.createProducer(topic);
// 7. 创建消息对象(文本消息)
TextMessage textMessage = session.createTextMessage("欢迎来到神奇的品优购世界");
// 8. 发送消息
producer.send(textMessage);
// 9. 关闭资源
producer.close();
session.close();
connection.close();
}
}
2. 消息消费者
public class TopicConsumer {
public static void main(String[] args) throws JMSException, IOException {
// 1. 创建连接工厂
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://192.168.25.128:61616");
// 2. 创建连接
Connection connection = connectionFactory.createConnection();
// 3. 启动连接
connection.start();
// 4. 获取session会话对象 参数1:是否启动事务 参数2 消息确认方式
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 5. 创建队列对象
Topic topic = session.createTopic("test-topic");
// 6. 创建消费者对象
MessageConsumer consumer = session.createConsumer(topic);
// 7. 设置监听
consumer.setMessageListener(new MessageListener() {
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage)message;
try {
System.out.println("提取的消息为:"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
});
// 8. 等待键盘输入
System.in.read();
// 9. 关闭资源
consumer.close();
session.close();
connection.close();
}
}
3.测试
同时开启多个消费者,再运行生产者,消费者都会收到消息
3. Spring整合JMS
1. 点对点模式
1. 消息生产者
- 创建工程springims-producer,在pom文件中引入springjms、activemq和junit等依赖
<properties>
<spring.version>4.2.4.RELEASE</spring.version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
<version>5.11.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
</dependencies>
- 在src/main/resources下创建spring配置文件applicationContext-jms-producer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms.xsd">
<context:component-scan base-package="cn.itcast.demo"></context:component-scan>
<!-- 真正可以产生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>
<!-- 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>
-->
</beans>
- 在cn.itcast.demo包下创建消息生产者
@Component
public class QueueProducer {
@Autowired
private JmsTemplate jmsTemplate;
@Autowired
private Destination queueTextDestination;
public void sendTextMessage(final String text){
jmsTemplate.send(queueTextDestination, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(text);
}
});
}
}
- 单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext-jms-producer.xml")
public class TestQueue {
@Autowired
private QueueProducer queueProducer;
@Test
public void testSend(){
queueProducer.sendTextMessage("send queue message");
}
}
2. 消息消费者
- 创建springjms_consumer,在pom文件中引入依赖
- 创建配置文件applicationContext-jms-consumer-queue.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms.xsd">
<!-- 真正可以产生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>
<!--这个是队列目的地,点对点的 文本信息-->
<bean id="queueTextDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="queue_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="queueTextDestination" />
<property name="messageListener" ref="myMessageListener" />
</bean>
</beans>
- 编写监听类
public class MyMessageListener implements MessageListener {
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage)message;
try {
System.out.println("接收到消息:"+textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
- 创建测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext-jms-consumer-queue.xml")
public class TestQueue {
@Test
public void testQueue(){
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. 发布/订阅模式
1. 消息生产者
- 在工程springjms_producer的applicationContext-jms-producer.xml增加配置
<!--这个是订阅模式 文本信息-->
<bean id="topicTextDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="topic_text"/>
</bean>
- 创建生产者类
@Component
public class TopicProducer {
@Autowired
private JmsTemplate jmsTemplate;
@Autowired
private Destination topicTextDestination;
public void sendTextMessage(final String text){
jmsTemplate.send(topicTextDestination, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(text);
}
});
}
}
- 编写测试类 同之前queue
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext-jms-producer.xml")
public class TestTopic {
@Autowired
private TopicProducer topicProducer;
@Test
public void testSend(){
topicProducer.sendTextMessage("send topic message");
}
}
2. 消息消费者
- 在activemq-spring-consumer工程创建配置文件applicationContext-jms-consumer-topic.xml 不同之处为destination
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms.xsd">
<!-- 真正可以产生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="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" />
<property name="messageListener" ref="myMessageListener" />
</bean>
</beans>
- 编写测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext-jms-consumer-topic.xml")
public class TestTopic {
@Test
public void testQueue(){
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. 商品审核-导入solr索引库
1. 需求分析
运用消息中间件activeMQ实现运营商后台和搜索服务的零耦合。运营商商品审核后,向activeMQ发送消息(SKU列表),搜索服务从activeMQ接收到消息并导入到solr索引库。
2. 消息生产者(运营商后台)
1. 解耦合
修改pinyougou-manager-web,移除搜索服务接口依赖
GoodsController.java删除调用搜索服务接口的代码
2. 准备工作
- 修改pinyougou-manager-web的pom.xml,引入依赖
- sellergoods-service工程添加jms-producer.xml修改名为spring-activemq.xml
<!--这个是队列目的地,点对点的 文本信息-->
<bean id="queueSolrDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="pinyougou_queue_solr"/>
</bean>
- 修改web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-*.xml</param-value>
</context-param>
3. 代码实现
修改manager-web的GoodsController.java
@Autowired
private Destination queueSolrDestination;
@Autowired
private JmsTemplate jmsTemplate;
...
@RequestMapping("/updateStatus")
public Result updateStatus(Long[] ids,String status){
try {
goodsService.updateStatus(ids,status);
if("1".equals(status)){//如果是审核通过
//*****导入到索引库
//得到需要导入的SKU列表
List<TbItem> itemList = goodsService.findItemListByGoodsIdAndStatus(ids, status);
//导入到solr
//itemSearchService.importList(itemList);
final String jsonString = JSON.toJSONString(itemList);//转换为json传输
jmsTemplate.send(queueSolrDestination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(jsonString);
}
});
//****生成商品详细页
/*for(final Long goodsId:ids){
// itemPageService.genItemHtml(goodsId);
jmsTemplate.send(topicPageDestination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(goodsId+"");
}
});
}*/
}
return new Result(true,"成功");
} catch (Exception e) {
e.printStackTrace();
return new Result(false,"失败");
}
}
使用时发现总是itemList为[ ]
,原因是goods和item未打通,goods审核通过,状态已经改变,但是具体SKU的item的status未改变。代码修改
sellergoods-service的GoodsServiceImpl的updateStatus方法修改如下
@Override
public void updateStatus(Long[] ids, String status) {
for (Long id : ids) {
TbGoods goods = goodsMapper.selectByPrimaryKey(id);
goods.setAuditStatus(status);
goodsMapper.updateByPrimaryKey(goods);
// goods对应的item 也就是SKU也要修改状态为1
TbItemExample example = new TbItemExample();
TbItemExample.Criteria criteria = example.createCriteria();
criteria.andGoodsIdEqualTo(id);
List<TbItem> itemList = itemMapper.selectByExample(example);
for (TbItem item : itemList) {
item.setStatus(status);
itemMapper.updateByPrimaryKey(item);
}
}
}
3. 消息消费者(搜索服务)
1. 准备工作
- 修改search-service,在pom.xml中添加activemq依赖,同上
- 添加spring配置文件applicationContext-jms-consumer.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms.xsd">
<!-- 真正可以产生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>
<!--这个是队列目的地,点对点的 文本信息-->
<bean id="queueSolrDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="pinyougou_queue_solr"/>
</bean>
<!-- 消息监听容器 -->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="queueSolrDestination" />
<property name="messageListener" ref="itemSearchListener" />
</bean>
</beans>
2. 代码实现
在search-service的com.pinyougou.search.service.impl新增监听类
@Component
public class ItemSearchListener implements MessageListener {
@Autowired
private ItemSearchService itemSearchService;
@Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage)message;
try {
String text = textMessage.getText();
System.out.println("listening message: "+text);
List<TbItem> itemList = JSON.parseArray(text, TbItem.class);
itemSearchService.importList(itemList);
System.out.println("importing to solr searching data");
} catch (JMSException e) {
e.printStackTrace();
}
}
}
5. 商品删除-移除solr索引库记录
1. 需求分析
通过消息中间件实现在商品删除时也同时移除索引库记录的功能
2. 消息生产者(运营商后台)
1. 配置文件
修改manager-web的spring-activemq.xml,添加bean配置
<bean id="queueSolrDeleteDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg value="pinyougou_queue_solr_delete"/>
</bean>
2. 代码实现
修改GoodsController.java
@RequestMapping("/delete")
public Result delete(Long [] ids){
try {
goodsService.delete(ids);
// 从索引库删除
// itemSearchService.deleteByGoodsId(Arrays.asList(ids));
jmsTemplate.send(queueSolrDeleteDestination, 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, "删除失败");
}
}
3. 消息消费者(搜索服务)
1. 配置文件
修改search-service的applicationContext-activemq-consumer.xml
<!--这个是队列目的地,导入索引库-->
<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="queueSolrDeleteDestination" />
<property name="messageListener" ref="itemDeleteListener" />
</bean>
2. 代码实现
com.pinyougou.search.service.impl包下创建监听类ItemDeleteListener
@Component
public class ItemDeleteListener implements MessageListener {
@Autowired
private ItemSearchService itemSearchService;
@Override
public void onMessage(Message message) {
ObjectMessage objectMessage = (ObjectMessage)message;
try {
Long[] goodsIds = (Long[]) objectMessage.getObject();
itemSearchService.deleteByGoodsId(Arrays.asList("listening message:"+goodsIds));
System.out.println("delete solr data");
} catch (JMSException e) {
e.printStackTrace();
}
}
}
6. 商品审核-执行网页静态化
1. 需求分析
运用消息中间件activeMQ实现运营商后台与网页生成服务的零耦合。运营商执行商品审核后,向activeMQ发送消息(商品ID),网页生成服务从activeMQ接收到消息后执行网页生成操作。
2. 消息生产者(运营商后台)
1. 解除耦合
修改manager-web,移除网页生成服务接口依赖
GoodsController.java删除网页生成服务接口相关代码
2.准备工作
修改配置文件spring-activemq.xml,添加配置
<!--这个是订阅模式 文本信息-->
<bean id="topicPageDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="pinyougou_topic_page"/>
</bean>
3. 代码实现
//***************生成商品详细页*****************
for(final Long goodsId:ids){
// itemPageService.genItemHtml(goodsId);
jmsTemplate.send(topicPageDestination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(goodsId+"");
}
});
}
3. 消息消费者(页面生成服务)
1. 接触dubbox依赖
修改page-service,删除dubbox相关依赖,dubbo,zookeeper和zkclient
修改ItemPageServiceImpl类的@Service
注解,为springframework的注解
2. 准备工作
- 修改applicationContext-service.xml,添加配置
<context:component-scan base-package="com.pinyougou.page.service.impl"/>
- pom.xml引入activeMQ客户端依赖
- 添加spring配置文件applicationContext-jms-consumer.xml,同之前
<!--这个是队列目的地,导入索引库-->
<bean id="topicPageDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="pinyougou_topic_page"/>
</bean>
<!-- 消息监听容器 导入索引库-->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="topicPageDestination" />
<property name="messageListener" ref="pageListener" />
</bean>
3. 代码编写
创建消息监听类PageListener
@Component
public class PageListener implements MessageListener {
@Autowired
private ItemPageService itemPageService;
@Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage)message;
try {
String text = textMessage.getText();
System.out.println("get message: "+text);
boolean b = itemPageService.genItemHtml(Long.parseLong(text));
System.out.println("result of genItemHtml: "+b);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
7. 商品删除-删除商品详情页
1. 需求分析
执行商品删除后,同时删除每个服务器上的商品详情页
2. 消息生产者(运营商后台)
1. 配置文件
修改spring-activemq.xml,添加配置
<!--这个是订阅模式 文本信息-->
<bean id="topicPageDeleteDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="pinyougou_topic_page_delete"/>
</bean>
2. 代码实现
修改GoodsController.java
@RequestMapping("/delete")
public Result delete(Long [] ids){
try {
goodsService.delete(ids);
// 从索引库删除
// itemSearchService.deleteByGoodsId(Arrays.asList(ids));
jmsTemplate.send(topicPageDeleteDestination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
// 直接传输对象
return session.createObjectMessage(ids);
}
});
// 删除每个服务器上的商品详情页
jmsTemplate.send(queueSolrDeleteDestination, 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, "删除失败");
}
}
3. 消息消费者(页面生成服务)
1. 配置文件
修改page-service的applicationContext-activemq-consumer.xml
<!--这个是主题目的地,生成页面-->
<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="topicPageDeleteDestination" />
<property name="messageListener" ref="pageDeleteListener" />
</bean>
2. 代码实现
- 修改page-interface的ItemPageService.java
// 删除商品详情页
public boolean deleteItemHtml(Long[] goodsIds);
- 修改page-service的ItemPageServiceImpl.java
@Override
public boolean deleteItemHtml(Long[] goodsIds) {
try {
for (Long goodsId : goodsIds) {
new File(pagedir+goodsId+".html").delete();
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
- 创建监听类PageDeleteListener
@Component
public class PageDeleteListener implements MessageListener {
@Autowired
private ItemPageService itemPageService;
@Override
public void onMessage(Message message) {
ObjectMessage objectMessage = (ObjectMessage)message;
try {
Long[] goodsIds = (Long[]) objectMessage.getObject();
System.out.println("get message: "+goodsIds);
boolean b = itemPageService.deleteItemHtml(goodsIds);
System.out.println("delete html: "+b);
} catch (JMSException e) {
e.printStackTrace();
}
}
}
测试通过!