如何在分布式环境中同步solr索引库和缓存信息
搜索无处不在,相信各位每天都免不了与它的亲密接触,那么我想你确实有必要来了解一下它们,就上周在公司实现的一个小需求来给各位分享一下:如何在分布式环境下同步索引库?
需求分析
公司数据库中的数据信息每天都免不了增、删、改操作,在执行这些简单的更新操作时,我们不仅将变更后的数据要更新到数据库中,同时还要马上同步索引库中的数据,有的时候还要同步一下缓存中的数据(本文只分享如何同步solr索引库)。
分析方案
当我们在后台管理系统中触发了更新操作时,不会紧跟着调用同步功能去更新索引库和缓存这种机制去实现,因为耦合性太高了,容易影响正常的业务流程。那么,既然我们不做,做的话就要影响业务,所以我们就有必要请一位私人秘书来替我们完成同步操作了,既然请了秘书,就没必要再去关心同步操作,而是我们只需要在更新完数据后通知这位秘书,让它去完成同步操作,岂不更妙?好了,说了这么久,这位秘书就是英俊潇洒不可或缺的消息队列——MQ,为什么使用它?主要还是开源、解耦。废话不说了,一起从简,开始上码。
哦,对了到这儿我就有必要说一下MQ的俩种使用模式,因为这个确实有点用,我就爬过这坑。主要分为2种:点对点(Queue)和发布订阅(Topic)模式。
从上图可以看出,这俩种模式最主要的区别就是发送出去的消息可以由多少个消费者来接受,很明显:
发布订阅模式:需要一个生产者发送消息到主题版块(Topic)中,可以有多个消费者订阅该版块来接受消息。消费者接受消息时,必须处于运行状态,而且只能接受运行之后的消息。
点对点模式:需要一个生产者发送消息到队列版块(Queue)中,只能有一个消费者从该队列(Queue)中接受该消息。生产者发送消息时,消费者不需要处于运行状态。
好,明确这点就够了,我们先用起来,至于它的一些细节,你们自己去找找资料好好读读,因为本人也是初次使用到,后期有机会再和大家共勉。
一路走好,码到成功
步骤一:安装MQ(activeMQ)
狠简单,解压即用。如有需要请参考http://www.cnblogs.com/1315925303zxz/p/6377551.html。
步骤二:spring整合MQ
1 <!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 -->
2 <bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
3 <property name="brokerURL" value="tcp://192.168.136.139:61616"/>
4 </bean>
5 <!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->
6 <bean id="connectionFactory"
7 class="org.springframework.jms.connection.SingleConnectionFactory">
8 <!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->
9 <property name="targetConnectionFactory" ref="targetConnectionFactory"/>
10 </bean>
11
12 <!-- 生产者 -->
13 <!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->
14 <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
15 <!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
16 <property name="connectionFactory" ref="connectionFactory"/>
17 </bean>
18 <!--这个是队列目的地:(俩种配置方式)
19 一、点对点模式:需要一个生产者发送消息到队列版块(Queue)中,只能有一个消费者从该队列(Queue)中接受该消息。
20 【生产者发送消息时,消费者不需要处于运行状态】。
21 二、发布订阅模式:需要一个生产者发送消息到主题版块(Topic)中,可以有多个消费者订阅该版块来接受消息。
22 【生产者发送消息时,消费者必须处于运行状态,而且只能接受运行之后的消息】。
23 -->
24 <!-- 点对点模式 -->
25 <!-- <bean id="testQueue" class="org.apache.activemq.command.ActiveMQQueue">
26 <constructor-arg>
27 <value>test-queue</value>
28 </constructor-arg>
29 </bean> -->
30 <!-- 发布订阅模式 -->
31 <bean id="testTopic" class="org.apache.activemq.command.ActiveMQTopic">
32 <constructor-arg value="test-topic"/>
33 </bean>
34
35
36 <!-- 消费者 -->
37 <!-- 配置自定义消息监听器 -->
38 <bean id="myMessageListener" class="cn.soa.mq.MyMessageListener"></bean>
39 <!-- 配置MessageListenerContainer -->
40 <bean id="jmsContainer"
41 class="org.springframework.jms.listener.DefaultMessageListenerContainer">
42 <property name="connectionFactory" ref="connectionFactory"/>
43 <property name="destination" ref="testTopic"/> <!-- 这儿注意生产者使用的是那种模式并且用哪个队列来发送消息的 -->
44 <property name="messageListener" ref="myMessageListener"/>
45 </bean>
步骤三:执行更新操作时,通知秘书去同步索引库、缓存等
1 @Autowired
2 private ItemMapper itemMapper;
3 //消息队列
4 @Autowired
5 private JmsTemplate jmsTemplate;
6 @Resource(name="testTopic")
7 private Destination testTopic;
8 @Override
9 public int saveItem(Item item) {
10 final String itemId = UUIDUtils.getUUID();
11 if(StringUtil.isNullOrBlank(item.getId())){
12 //如果商品主键为空,则设置一个ID
13 item.setId(itemId);
14 }
15 if(StringUtil.isNullOrBlank(String.valueOf(item.getCreateTime()))){
16 //如果创建时间为空,则设置当前时间为创建时间
17 item.setCreateTime(DateTimeUtils.getCurrentDate());
18 }
19 int save = itemMapper.saveItem(item);
20 if(save == 1){
21 //如果新增商品成功,则发送商品ID到消息队列中,目的同步索引库、缓存等
22 jmsTemplate.send(testTopic, new MessageCreator(){
23 @Override
24 public Message createMessage(Session session) throws JMSException {
25 // 将商品ID发送出去
26 logger.error("发送新增商品的ID到MQ消息队列中:{}==============================");
27 TextMessage message = session.createTextMessage(itemId);
28 return message;
29 }
30 });
31 }
32 return save;
33 }
步骤四:使用MQ监听器同步索引库(监听器需在spring配置文件中配置)
1 public class MyMessageListener implements MessageListener{
2
3 private final static Logger logger = LoggerFactory.getLogger(MyMessageListener.class);
4
5 @Autowired
6 private SolrServer solrServer;
7
8 @Autowired
9 private ItemService itemService;
10
11 /**
12 * 根据监听到的商品ID来同步索引库数据。
13 */
14 @Override
15 public void onMessage(Message message) {
16 logger.info("============开始同步索引库================");
17 // 根据不同业务逻辑进行相应处理
18 if(message instanceof TextMessage){
19 try {
20 TextMessage textMessage = (TextMessage) message;
21 String ID = textMessage.getText(); //监听到新商品ID
22 Item newItem = itemService.findItemById(ID); //根据新主键查询到商品信息
23 // 将商品数据封装到SolrInputDocument对象
24 SolrInputDocument doc = new SolrInputDocument();
25 doc.addField("id", newItem.getId());
26 doc.addField("product_catalog_name", "忠哥系列");
27 doc.addField("product_price", newItem.getPrice());
28 doc.addField("product_name", newItem.getItemName());
29
30 // 添加到索引库
31 solrServer.add(doc);
32 // 提交
33 solrServer.commit();
34 } catch (Exception e) {
35 logger.error("同步索引库失败:{}"+e.getMessage());
36 }
37 }
38 }
39 }
步骤五:校验数据是否同步成功,马上就可以在索引库中搜到我们刚刚新增的信息
与君共勉!每天都有新技能!
- 为什么区块链会成为消除数字化营销障碍的解决方案
- TinyOS和Deluge的安装模拟(二)
- Kubernetes的服务网格(第4部分):通过流量切换持续部署
- QTableView表格视图的列宽设置
- OpenProcess打开进程返回错误的问题
- Python标准库01 正则表达式 (re包)
- 剑指OFFER之栈的压入、弹出序列(九度OJ1366)
- Python标准库03 路径与文件 (os.path包, glob包)
- AI人工智能时代已经到来 “北斗即时判”实现纯语音交互
- 剑指OFFER之链表中倒数第k个节点(九度OJ1517)
- 用Qt写软件系列四:定制个性化系统托盘菜单
- Linux简介与厂商版本
- 用Qt写软件系列三:一个简单的系统工具之界面美化
- VS编译链接时错误(Error Link2005)的解决方法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 理解ECMAScript规范(2)
- 算法篇:数的转换
- React 开发要知道的 34 个技巧
- 算法篇:求1的个数
- 新特性解读 | MySQL 8.0 语句摘要功能介绍
- 10个不那么知名但很实用的Web API
- 技术分享 | 一文了解高并发限流算法
- prometheus-operator 监控 k8s 外部集群
- Kubernetes 通过statefulset部署redis cluster集群
- 猿实战13——实现你没听说过的前台类目
- 猿实战14——前台类目之广告牌设置
- 猿实战15——关联你所不明白的前后台类目
- 完美解决方案-雪花算法ID到前端之后精度丢失问题
- 猿实战16——承运商之搭建你的运费基石
- List对象去重及按属性去重的8种方法-java基础总结第六篇