淘淘商城44-使用消息队列activeMQ更新solr索引库--解决同步索引问题

版权声明:本文为博主原创文章,如有转载请注明出处,谢谢。 https://blog.csdn.net/pdsu161530247/article/details/82024811

 

目录

1.为什么要使用消息对列acitveMQ

1.1方案1

1.2方案2

1.3方案3

2.使用消息队列activeMQ修改索引库

2.1producer

2.1.1applicationContext-activemq.xml配置

2.1.2添加发送消息逻辑

2.2consumer

2.2.1功能分析

2.2.2dao层

2.2.3service层

2.2.4listener

2.2.5配置applicationContext-activemq.xml

3.注意


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功能分析

  1. 接收消息。需要创建MessageListener接口的实现类。
  2. 取消息,取商品id。
  3. 根据商品id查询数据库。
  4. 创建一SolrInputDocument对象。
  5. 使用SolrServer对象写入索引库。
  6. 返回成功,返回TaotaoResult。

2.2.2dao层

  1. 根据商品id查询商品信息。
  2. 创建一SolrInputDocument对象。
  3. 使用SolrServer对象写入索引库。
  4. 返回成功,返回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

业务逻辑:

  1. 调用searchDao的方法。
  2. 返回成功,返回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()。但是感觉这样太乱了,耦合死了,看着就难受。

这个坑一个以后解决吧!!!哎

猜你喜欢

转载自blog.csdn.net/pdsu161530247/article/details/82024811