目录
2.1.1applicationContext-activemq.xml配置
2.2.5配置applicationContext-activemq.xml
1.为什么要使用消息对列acitveMQ
在我们的后台,增加、修改、删除商品时,这里以修改为例。
修改商品时,修改的是数据库中的数据。但是用户在商品搜索时,搜索的是solr索引库中的数据,所以用户并不会搜到新修改商品的信息。后台虽然有一个导入所有商品信息到索引库的,但是每次导入的信息过大,而我们只需要修改索引库中的一个商品信息,所以我们需要编写一个,修改单个商品索引的功能。
我们有三种方案:
1.1方案1
在taotao-manager-service中,添加商品的业务逻辑中,添加一个同步索引库的业务逻辑。
缺点:业务逻辑耦合度非常高,业务拆分不明确
1.2方案2
业务逻辑在taotao-search中实现,调用服务在taotao-manager实现。业务逻辑分开。
缺点:
服务之间的耦合度变高,启动有先后顺序。
随着调用的服务会越来越多,服务之间的调用越来越复杂,难以管理。
1.3方案3
使用消息队列
实现效果:
后台需要增删改商品信息很大的时候,只需要taotao-manager-service在数据库修改商品信息后,向MQ发送一个被修改商品的id,然后直接返回提示,修改成功。然后在taotao-search-service写一个监听器,专门接收MQ发送的消息,修改索引库即可。
存在的问题:
1、如果MQ挂了,所有相关的服务都挂了
2、MQ有性能的瓶颈,尽量减少消息的内容的大小
2.使用消息队列activeMQ修改索引库
需要在商品的添加/修改的时候,同步索引库。将数据从数据库中查询出来导入到索引库更新。
消息的发送方为:所在工程taotao-manager-service
消息的接收方为:所在工程taotao-search-service
两个工程都需要依赖activmq:
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
</dependency>
2.1producer
在taotao-manager-service工程中发送消息。
功能分析:
当商品添加完成后发送一个TextMessage,包含一个商品id即可。
接收端接收到商品id通过数据库查询到商品的信息(搜索的结果商品的信息)再同步索引库。
2.1.1applicationContext-activemq.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: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">
<bean id="targetConnection" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.25.130:61616"></property>
</bean>
<!-- 通用的connectionfacotry 指定真正使用的连接工厂 -->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="targetConnection"></property>
</bean>
<!-- 接收和发送消息时使用的类 -->
<bean class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory"></property>
</bean>
<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg name="name" value="item-change-topic"></constructor-arg>
</bean>
</beans>
2.1.2添加发送消息逻辑
首先注入消息模板对象与发送消息的目标
@Autowired
private JmsTemplate jmsTemplate;
@Resource(name="topicDestination")
private Destination topicDestination;
添加发送消息的代码
// 发送一个商品添加消息
jmsTemplate.send(topicDestination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
TextMessage textMessage = session.createTextMessage(itemId + "");
return textMessage;
}
});
2.2consumer
在taotao-search-service中消费消息。
需要加入activmq的依赖。
2.2.1功能分析
- 接收消息。需要创建MessageListener接口的实现类。
- 取消息,取商品id。
- 根据商品id查询数据库。
- 创建一SolrInputDocument对象。
- 使用SolrServer对象写入索引库。
- 返回成功,返回TaotaoResult。
2.2.2dao层
- 根据商品id查询商品信息。
- 创建一SolrInputDocument对象。
- 使用SolrServer对象写入索引库。
- 返回成功,返回TaotaoResult。
在taotao-search-service的com.taotao.search.mapper编写mapper接口与mapper映射文件,用于根据id查询数据库,返回searchItem
mapper接口
/**
* 根据id查询SearchItem
* @param itemId
* @return
*/
public SearchItem getItemById(Long itemId);
mapper映射文件
<select id="getItemById" parameterType="long" resultType="com.taotao.common.pojo.SearchItem">
SELECT
a.id,
a.title,
a.sell_point,
a.price,
a.image,
b. NAME category_name,
c.item_desc
FROM
tb_item a
JOIN tb_item_cat b ON a.cid = b.id
JOIN tb_item_desc c ON a.id = c.item_id
WHERE a.status = 1
AND a.id=#{itemId}
</select>
索引库dao
用于更新索引库
在taotao-search-service的com.taotao.search.dao的SearchDao中
/**
* 商品增改时,更新索引库
* @param itemId
* @return
* @throws Exception
*/
public TaotaoResult updateSearchItemById(Long itemId) throws Exception {
//修改、新增
//1.调用mapper中的方法
SearchItem searchItem = searchItemMapper.getItemById(itemId);
//2.创建solrinputdocument
SolrInputDocument document = new SolrInputDocument();
//3.向文档中添加域
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());
//4.添加文档到索引库中
solrServer.add(document);
//5.提交
solrServer.commit();
return TaotaoResult.ok();
}
2.2.3service层
参数:商品ID
业务逻辑:
- 调用searchDao的方法。
- 返回成功,返回TaotaoResult。
在taotao-search-interface的com.taotao.search.service包下SearchService
/**
* 根据id更新商品索引库
* @param itemId
* @return
* @throws Exception
*/
TaotaoResult updateSearchItemById(Long itemId) throws Exception;
在taotao-search-service的com.taotao.search.service.impl包下SearchServiceImpl实现接口
/**
* 根据id更新商品索引库
*/
@Override
public TaotaoResult updateSearchItemById(Long itemId) throws Exception {
return searchDao.updateSearchItemById(itemId);
}
2.2.4listener
在taotao-search-service下创建com.taotao.search.listener包,在com.taotao.search.listener包下创建listener用于接收消息队列中的id消息。
package com.taotao.search.listener;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import org.springframework.beans.factory.annotation.Autowired;
import com.taotao.search.service.SearchService;
/**
* 接收消息的监听器
*
* @author Administrator
*
*/
public class ItemChangeMessageListener implements MessageListener {
// 注入service 直接调用 方法更新即可
@Autowired
private SearchService searchService;
@Override
public void onMessage(Message message) {
// 判断消息类型是否为textmessage
if (message instanceof TextMessage) {
// 如果是 获取商品的id
TextMessage message2 = (TextMessage) message;
try {
String id = message2.getText();
// 通过商品的id查询数据 需要开发mapper 通过id查询商品(搜索时)的数据
// 更新索引库
Long itemId = Long.parseLong(id);
searchService.updateSearchItemById(itemId);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
2.2.5配置applicationContext-activemq.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: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">
<bean id="targetConnection" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://47.100.54.177:61616"></property>
</bean>
<!-- 通用的connectionfacotry 指定真正使用的连接工厂 -->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="targetConnection"></property>
</bean>
<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg name="name" value="item-change-topic"></constructor-arg>
</bean>
<!-- messagelistener的初始化 -->
<bean id="itemChangeMessageListener" 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="itemChangeMessageListener"></property>
</bean>
</beans>
3.注意
使用消息队列activeMQ更新solr索引库,用于管理员更新商品时,用户可以搜到最新的商品信息,就分析和编写完毕。
但是有一点遗憾的地方,这里并不能用作商品的删除、下架、上架
因为我当初在编写这个三个功能的时候偷懒了,将三个功能放在一起,根据方法名判断删除、下架、上架,而且参数为List类型。
有一种可行的方法我将List<Long> ids,用逗号分割转为String,最后加上delete、reshelf、instock这样的标识
比如:"15214,41358,123231,delete"、"152745,86358,123231,reshelf",在searchDao做字符串split,判断最末尾是delete还是reshelf等,执行solrServer.delete()、solrServer.add()。但是感觉这样太乱了,耦合死了,看着就难受。
这个坑一个以后解决吧!!!哎