首先说一下我们的需求:一个添加商品和一个搜索商品功能,搜索功能我们使用了Solr索引库进行索引,所以当我们添加一个商品时就应该将此商品添加到索引库。如果不是用JMS,添加商品模块就需要依赖搜索商品模块,这样形成了耦合关系,项目启动顺序也会存在问题,所以我们采用了ActiveMQ来实现添加商品和搜索商品模块之间的通信,解决了耦合问题。当我们添加商品时就给服务器发送添加商品的消息(可将商品id发送给服务器),搜索商品监听到该消息,就去执行添加索引库的操作。
【消息发送方】
第一步:引入相关jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
</dependency>
第二步:配置Activemq整合spring。配置:ConnectionFactory;生产者,使用JMSTemplate对象,发送消息;Destination
<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 -->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.20.216: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</value>
</constructor-arg>
</bean>
<!--这个是主题目的地,一对多的 -->
<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg>
<value>itemAddTopic</value>
</constructor-arg>
</bean>
第三步:测试:
因为项目启动时已经初始化spring容器了,只需从容器中获得JMSTemplate对象和一个Destination对象,因为使用JMSTemplate对象发送消息,需要知道Destination
@Autowired
private JmsTemplate jmsTemplate;
@Resource
private Destination topicDestination;
@Override
public E3Result addItem(TbItem item, String desc) {
//生成商品id
final long itemId = IDUtils.genItemId();
//向商品表添加数据
itemMapper.insert(item);
//发送商品添加消息
jmsTemplate.send(topicDestination, new MessageCreator(){
@Override
public Message createMessage(Session session) throws JMSException {
TextMessage textMessage =session.createTextMessage(itemId+"");
return textMessage;
}
});
//返回成功
return E3Result.ok();
}
【消息接受者】
第一步:相关配置如上面的第一步
第二步:配置Activemq整合spring。配置:ConnectionFactory;Destination;监听的消息;监听器Listener
<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 -->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.20.216: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="queueDestination" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg>
<value>spring-queue</value>
</constructor-arg>
</bean>
<!--这个是主题目的地,一对多的 -->
<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="itemAddTopic" />
</bean>
<!-- 接收消息 -->
<!-- 监听商品添加消息,同步索引库 -->
<bean id="itemAddMessageListener" class="cn.e3mall.search.message.ItemAddMessageListener"/>
<!-- 消息监听器 -->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="topicDestination" />
<property name="messageListener" ref="itemAddMessageListener" />
</bean>
第三步:配置监听的消息
其中接受者在程序启动时就要一直监听,所以要写一个实现MessageListener接口的类,写成Bean交给spring容器管理,使其向启动时就在监听,当监听到消息时就进行一系列想要进行的操作,如我们本系统当监听到有商品添加时,就去同步索引库,代码如下:
public class ItemAddMessageListener implements MessageListener{
@Autowired
private ItemMapper itemMapper;
@Autowired
private SolrServer solrServer;
@Override
public void onMessage(Message message) {
// 从消息中取商品id
TextMessage textMessage =(TextMessage)message;
String text;
try {
text = textMessage.getText();
Long itemId = new Long(text);
//等待事物提交
Thread.sleep(1000);
//根据商品id查询商品信息
SearchItem searchItem = itemMapper.getItemById(itemId);
//创建一个文档对象
SolrInputDocument document = new SolrInputDocument();
//向文档对象中添加域
document.addField("id", searchItem.getId());
。。。
//把文档写入索引库
solrServer.add(document);
//提交
solrServer.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
}
因为发送消息是个很快的过程,此时可能商品还没添加到数据库中,所以可能存在根据id去查询数据库时查不到,我们采用的办法就是,等待事物提交:Thread.sleep(1000);,然后再去查询数据库