ActiveMQ 进阶(2) 案例:同步solr索引库

我们在添加商品时需要与索引库进行同步,这样每添加一个商品索引库就多一个文档,这样做的好处是不用把数据库中的所有数据进行同步,大大提高了性能节约了时间。

要发送topic消息,就要在activemq的配置文件中做下配置,我们将原来配置的topic做下简单的修改,根据命名有意义原则,我们给起名为"itemAddTopic",给消息起名为"item-add-topic",如下图所示。

在这里插入图片描述
修改后topic如下:

<!--这个是主题目的地,一对多的 -->
	<bean id="itemAddTopic" class="org.apache.activemq.command.ActiveMQTopic">
		<constructor-arg value="item-add-topic" />
	</bean>

activemq的配置文件

<?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">
	
	<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 -->
	<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
		<property name="brokerURL" value="tcp://192.168.25.130: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="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
		<constructor-arg value="spring-queue"/>
	</bean>
	<!--这个是主题目的地,一对多的 -->
	<bean id="itemAddTopic" class="org.apache.activemq.command.ActiveMQTopic">
		<constructor-arg value="item-add-topic" />
	</bean>


</beans>


下一步是我们找到添加商品的实现类"ItemServiceImpl",在该类中注入jmsTemplate和Destination,如下图所示。

在这里插入图片描述
代码:

	@Autowired
	private JmsTemplate jmsTemplate;
	@Resource(name="itemAddTopic")
	private Destination destination;

然后我们找到添加商品的方法,在添加完商品后,发送消息,这里需要考虑一个问题,那就是消息的内容应该是什么?既然是添加商品,消费者肯定是要知道添加的商品是哪个商品,同时本着简单的原则,我们只需要传新增商品的ID即可,如下图所示。
在这里插入图片描述
代码如下:

//发送activemq消息
		jmsTemplate.send(destination,new MessageCreator() {
			
			@Override
			public Message createMessage(Session session) throws JMSException {
				System.out.println("发送activemq消息");
				TextMessage textMessage = session.createTextMessage(itemId+"");
				System.out.println("send success");
				return textMessage;
			}
		});

当前ItemServiceImpl类的所有代码如下:

package com.meiyou.service.impl;

import java.util.Date;
import java.util.List;

import javax.annotation.Resource;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.meiyou.common.pojo.EasyUIDataGridResult;
import com.meiyou.common.pojo.MeiyouResult;
import com.meiyou.common.utils.IDUtils;
import com.meiyou.jedis.service.JedisClient;
import com.meiyou.mapper.TbItemDescMapper;
import com.meiyou.mapper.TbItemMapper;
import com.meiyou.pojo.TbItem;
import com.meiyou.pojo.TbItemDesc;
import com.meiyou.pojo.TbItemExample;
import com.meiyou.service.ItemService;

@Service
public class ItemServiceImpl implements ItemService {
	
	@Autowired
	private TbItemMapper itemMapper;
	@Autowired
	private TbItemDescMapper itemDescMapper;
	@Autowired
	private JedisClient jedisClient;
	@Value("${ITEM_INFO}")
	private String ITEM_INFO;
	@Value("${ITEM_EXPIRE}")
	private Integer ITEM_EXPIRE;
	
	@Autowired
	private JmsTemplate jmsTemplate;
	@Resource(name="itemAddTopic")
	private Destination destination;

	@Override
	public TbItem getItemById(long itemId) {
		//查询数据库之前先查询缓存
		try {
			String json = jedisClient.get(ITEM_INFO+":"+itemId+":BASE");
			//判断json为空,"",whitespace
			if(!StringUtils.isBlank(json)){
				//把json转换成对象
				return JSON.parseObject(json, TbItem.class);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		TbItem tbItem = itemMapper.selectByPrimaryKey(itemId);
		//把查询结果添加到缓存
		try {
			//把查询结果添加到缓存
			//ITEM_INFO+":"+itemId+":BASE"为缓存的key
			jedisClient.set(ITEM_INFO+":"+itemId+":BASE", JSON.toJSONString(tbItem));
			//设置过期时间,提高缓存的利用率
			jedisClient.expire(ITEM_INFO+":"+itemId+":BASE", ITEM_EXPIRE);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return tbItem;
	}

	@Override
	public TbItemDesc getItemDescById(long itemId) {
		//查询数据库之前先查询缓存
		try {
			String json = jedisClient.get(ITEM_INFO+":"+itemId+":DESC");
			if(!StringUtils.isBlank(json)){
				//把json转换成对象
				return JSON.parseObject(json, TbItemDesc.class);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		TbItemDesc tbItemDesc = itemDescMapper.selectByPrimaryKey(itemId);
		//把查询结果添加到缓存
		try {
			//把查询结果添加到缓存
			jedisClient.set(ITEM_INFO+":"+itemId+":DESC", JSON.toJSONString(tbItemDesc));
			//设置过期时间,提高缓存的利用率
			jedisClient.expire(ITEM_INFO+":"+itemId+":DESC", ITEM_EXPIRE);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return tbItemDesc;
	}
	
	@Override
	public EasyUIDataGridResult getItemList(Integer page, Integer rows) {
		//设置分页信息
		PageHelper.startPage(page, rows);
		//执行查询
		TbItemExample example = new TbItemExample();
		List<TbItem>  list = itemMapper.selectByExample(example);
		//获取查询结果
		PageInfo<TbItem> pageInfo = new PageInfo<>(list);
		EasyUIDataGridResult result = new EasyUIDataGridResult();
		result.setRows(list);
		result.setTotal(pageInfo.getTotal());
		//返回结果
		return result;
	}

	@Override
	public MeiyouResult createItem(TbItem tbItem, String desc) throws Exception{
		//生成商品ID
		long itemId = IDUtils.genItemId();
		//补全item的属性
		tbItem.setId(itemId);
		//商品状态,1-正常,2-下架,3-删除
		tbItem.setStatus(((byte) 1));
		tbItem.setCreated(new Date());
		tbItem.setUpdated(new Date());
		itemMapper.insert(tbItem);
		//添加商品描述
		insertItemDesc(itemId, desc);
		System.out.println("添加商品描述");
		//发送activemq消息
		jmsTemplate.send(destination,new MessageCreator() {
			
			@Override
			public Message createMessage(Session session) throws JMSException {
				System.out.println("发送activemq消息");
				TextMessage textMessage = session.createTextMessage(itemId+"");
				System.out.println("send success");
				return textMessage;
			}
		});
		return MeiyouResult.ok();
	}
	
	//添加商品描述
	private void insertItemDesc(long itemId,String desc){
		//创建一个商品描述表对应的pojo
		TbItemDesc itemDesc = new TbItemDesc();
		//补全pojo的属性
		itemDesc.setItemId(itemId);
		itemDesc.setItemDesc(desc);
		itemDesc.setCreated(new Date());
		itemDesc.setUpdated(new Date());
		//向商品描述表插入数据
		itemDescMapper.insert(itemDesc);
	}

}

上面是发送activemq消息,下面我们再来写下接收消息,接收消息是在meiyou-search-service工程,我们先要做的事情是修改Mybatis文件,添加一个根据商品ID来查询商品详情的方法,如下图所示。
在这里插入图片描述
上图添加的代码如下:

<select id="getItemById" parameterType="long" resultType="com.meiyou.common.pojo.SearchItem">
  	SELECT
		a.id,
		a.title,
		a.sell_point,
		a.price,
		a.image,
		b.`name` as category_name,
		c.item_desc
	FROM
		tb_item a
	LEFT JOIN tb_item_cat b ON a.cid = b.id
	LEFT JOIN tb_item_desc c ON a.id = c.item_id
	WHERE
		a.`status` = 1
	AND a.id = #{itemId}
  </select>

接着,在接口类中添加更加商品ID查询商品详情的接口,如下图所示。
在这里插入图片描述
现在我启动的是单机版的solr服务器,因此要保证solr配置文件当前切换到单机版,如下图所示。
在这里插入图片描述

<?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 class="org.apache.solr.client.solrj.impl.HttpSolrServer">
		<constructor-arg name="baseURL" value="http://192.168.25.130:8080/solr"></constructor-arg>
	</bean>
	
</beans>

下面我们需要创建一个监听器类ItemAddMessageListener,在该类中处理同步索引库的逻辑,如下图所示。
在这里插入图片描述
ItemAddMessageListener.java类代码如下:

package com.meiyou.search.listener;

import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;

import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.common.SolrInputDocument;
import org.springframework.beans.factory.annotation.Autowired;

import com.meiyou.common.pojo.SearchItem;
import com.meiyou.search.mapper.SearchItemMapper;

public class ItemAddMessageListener implements MessageListener {
	@Autowired
	private SearchItemMapper searchItemMapper;
	@Autowired
	private SolrServer solrServer;

	@Override
	public void onMessage(Message message) {
		try {
			//从消息中取出商品id
			System.out.println("onMessage");
			TextMessage textMessage = (TextMessage)message;
			String text = textMessage.getText();
			long itemId = Long.valueOf(text);
			//根据商品id查询商品详情,这里需要注意的是消息发送方法
			//有可能还没有提交事务,因此这里是有可能取不到商品信息
			//的,为了避免这种情况出现,我们最好等待事务提交,这里
			//我采用3次尝试的方法,每尝试一次休眠一秒
			SearchItem searchItem = null;
			for(int i=0;i<3;i++){
				try {
					Thread.sleep(1000);//休眠一秒
					searchItem = searchItemMapper.getItemById(itemId);
					//如果获取到了商品信息,那就退出循环。
					if(searchItem != null){
						break;
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			//创建文档对象
			SolrInputDocument document = new SolrInputDocument();
			//向文档对象中添加域
			/*document.setField("id", searchItem.getId());
			document.setField("item_title", searchItem.getTitle());
			document.setField("item_sell_point", searchItem.getSell_point());
			document.setField("item_price", searchItem.getPrice());
			document.setField("item_image", searchItem.getImage());
			document.setField("item_category_name", searchItem.getCategory_name());
			document.setField("item_desc", searchItem.getItem_desc());*/
			
			document.addField("id", searchItem.getId().toString());//这里是字符串需要转换
			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());
			
			System.out.println("document");
			
			
			
			
			
			//把文档写入索引库
			solrServer.add(document);
			System.out.println("写入索引库");
			//提交
			solrServer.commit();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}


正如在代码中提到的那样,在添加商品的时候(如下图所示)是涉及到事务的,事务提交之后才能在数据库中查询到商品信息,假如网络不济,造成事务还没提交,接收消息的一端想去查询商品信息,这时显然是查询不到的,为了等待事务提交,采用三次尝试的机制,如上面代码所示。

在这里插入图片描述

写完监听器我们再配置下接收的topic(要与meiyou-manager-service当中配置的topic一致),以及设置监听器,如下图所示。

在这里插入图片描述

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">
	
	<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 -->
	<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
		<property name="brokerURL" value="tcp://192.168.25.130: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="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
		<constructor-arg value="spring-queue"/>
	</bean>
	<!--这个是主题目的地,一对多的 -->
	<bean id="itemAddTopic" class="org.apache.activemq.command.ActiveMQTopic">
		<constructor-arg value="item-add-topic" />
	</bean>
	
	<!-- 配置监听器 -->
	<bean id="itemAddMessageListener" class="com.meiyou.search.listener.ItemAddMessageListener"/>
	<!-- 消息监听容器 -->
	<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
	    <property name="connectionFactory" ref="connectionFactory"/>
	    <property name="destination" ref="itemAddTopic"/>
	    <property name="messageListener" ref="itemAddMessageListener"/>
	</bean>


	<!-- 接收消息 -->
	<!-- 配置监听器 -->
	<bean id="messageListener" class="com.meiyou.search.listener.MyMessageListener"/>
	<!-- 消息监听容器 -->
	<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
	    <property name="connectionFactory" ref="connectionFactory"/>
	    <property name="destination" ref="queueDestination"/>
	    <property name="messageListener" ref="messageListener"/>
	</bean>
</beans>


好了,写完了代码,现在我们来测试一下,我们依次启动taotao-manager、taotao-content、taotao-search、taotao-manager-web、taotao-port-web、taotao-search-web工程。启动完之后,我们访问http://localhost:8081来访问淘淘商城管理后台,添加一款商品,这里我添加的是华为P10手机,如下图所示。

在这里插入图片描述

添加完商品后,我们到淘淘商城首页进行搜索,看能不能搜索到我们刚才添加的手机,如下图所示,发现可以正常搜索到刚才添加的华为手机!!说明我们的消息机制没问题。

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值