Spring-Rabbit
Spring项目
简介
使用
消费者
生产者
配置文件
1、 定义连接工厂
2、 定义模板(可以指定交换机或队列)
3、 定义队列、交换机、以及完成队列和交换机的绑定
4、 定义监听
5、 定义管理,用于管理队列、交换机等:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<!-- 定义RabbitMQ的连接工厂 -->
<rabbit:connection-factory id="connectionFactory"
host="127.0.0.1" port="5672" username="taotao" password="taotao"
virtual-host="/taotao" />
<!-- 定义Rabbit模板,指定连接工厂以及定义exchange -->
<rabbit:template id="amqpTemplate" connection-factory="connectionFactory" exchange="fanoutExchange" />
<!-- <rabbit:template id="amqpTemplate" connection-factory="connectionFactory"
exchange="fanoutExchange" routing-key="foo.bar" /> -->
<!-- MQ的管理,包括队列、交换器等,来完成自动声明 -->
<rabbit:admin connection-factory="connectionFactory" />
<!-- 定义队列,自动声明 -->
<rabbit:queue name="myQueue" auto-declare="true"/>
<!-- 定义交换器,自动声明 -->
<!-- 自动声明:使用交换机的时候触发,如果交换机不存在就创建,存在就忽略 -->
<rabbit:fanout-exchange name="fanoutExchange" auto-declare="true">
<rabbit:bindings>
<rabbit:binding queue="myQueue"/>
</rabbit:bindings>
</rabbit:fanout-exchange>
<!-- <rabbit:topic-exchange name="myExchange">
<rabbit:bindings>
<rabbit:binding queue="myQueue" pattern="foo.*" />
</rabbit:bindings>
</rabbit:topic-exchange> -->
<!-- 队列监听 -->
<rabbit:listener-container connection-factory="connectionFactory">
<rabbit:listener ref="foo" method="listen" queue-names="myQueue" />
</rabbit:listener-container>
<bean id="foo" class="cn.itcast.rabbitmq.spring.Foo" />
</beans>
持久化交换机和队列
持久化:将交换机或队列的数据保存到磁盘,服务器宕机或重启之后依然存在。
非持久化:将交换机或队列的数据保存到内存,服务器宕机或重启之后将不存在。
非持久化的性能高于持久化。
如何选择持久化?非持久化? – 看需求。
非持久化设置:
实现商品数据的同步
使用什么队列模式?
使用通配符模式。
在后台系统中发送消息到交换机
导入依赖
队列和交换机的绑定关系
实现:
1、 在配置文件中将队列和交换机完成绑定
2、 可以在管理界面中完成绑定
a) 绑定关系如果发生变化,需要修改配置文件,并且服务需要重启
b) 管理更加灵活
c) 更容易对绑定关系的权限管理,流程管理
配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<!-- 定义RabbitMQ的连接工厂 -->
<rabbit:connection-factory id="connectionFactory"
host="${rabbitmq.host}" port="${rabbitmq.port}" username="${rabbitmq.username}"
password="${rabbitmq.password}" virtual-host="${rabbitmq.vhost}" />
<!-- 管理 -->
<rabbit:admin connection-factory="connectionFactory" />
<!-- 定义交换机 -->
<rabbit:topic-exchange name="TAOTAO-ITEM-EXCHANGE"
auto-declare="true" durable="true" />
<!-- 定义模板 -->
<rabbit:template id="rabbitTemplate"
connection-factory="connectionFactory" exchange="TAOTAO-ITEM-EXCHANGE" />
</beans>
消息内容
方案:
1、 将Item对象做json序列化发送
a) 数据大
b) 有些数据其他人是可能用不到的
2、 发送商品的id、操作类型
实现
package com.taotao.manage.service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.abel533.entity.Example;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.taotao.common.bean.EasyUIResult;
import com.taotao.common.service.ApiService;
import com.taotao.manage.mapper.ItemMapper;
import com.taotao.manage.pojo.Item;
import com.taotao.manage.pojo.ItemDesc;
import com.taotao.manage.pojo.ItemParamItem;
@Service
public class ItemService extends BaseService<Item> {
@Autowired
private ItemMapper itemMapper;
@Autowired
private ItemDescService itemDescService;
@Autowired
private ItemParamItemService itemParamItemService;
@Autowired
private ApiService apiService;
// @Value("${TAOTAO_WEB_URL}")
// private String TAOTAO_WEB_URL;
@Autowired
private RabbitTemplate rabbitTemplate;
private static final ObjectMapper MAPPER = new ObjectMapper();
public Boolean saveItem(Item item, String desc, String itemParams) {
// 初始值
item.setStatus(1);
item.setId(null); // 出于安全考虑,强制设置id为null,通过数据库自增长得到
Integer count1 = super.save(item);
// 保存商品描述数据
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(item.getId());
itemDesc.setItemDesc(desc);
Integer count2 = this.itemDescService.save(itemDesc);
// 保存规格参数数据
ItemParamItem itemParamItem = new ItemParamItem();
itemParamItem.setItemId(item.getId());
itemParamItem.setParamData(itemParams);
Integer count3 = this.itemParamItemService.save(itemParamItem);
//发送消息到MQ的交换机,通知其他系统
sendMsg(item.getId(), "insert");
return count1.intValue() == 1 && count2.intValue() == 1 && count3.intValue() == 1;
}
public EasyUIResult queryItemList(Integer page, Integer rows) {
// 设置分页参数
PageHelper.startPage(page, rows);
Example example = new Example(Item.class);
// 安装创建时间排序
example.setOrderByClause("created DESC");
List<Item> items = this.itemMapper.selectByExample(example);
PageInfo<Item> pageInfo = new PageInfo<Item>(items);
return new EasyUIResult(pageInfo.getTotal(), pageInfo.getList());
}
public Boolean updateItem(Item item, String desc, String itemParams) {
item.setStatus(null);// 强制设置状态不能被修改
Integer count1 = super.updateSelective(item);
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(item.getId());
itemDesc.setItemDesc(desc);
Integer count2 = this.itemDescService.updateSelective(itemDesc);
// 更新规格参数数据
Integer count3 = this.itemParamItemService.updateItemParamItem(item.getId(), itemParams);
// try {
// // 通知其他系统该商品已经更新
// String url = TAOTAO_WEB_URL + "/item/cache/" + item.getId() + ".html";
// this.apiService.doPost(url);
// } catch (Exception e) {
// e.printStackTrace();
// }
//发送消息到MQ的交换机,通知其他系统
sendMsg(item.getId(), "update");
return count1.intValue() == 1 && count2.intValue() == 1 && count3.intValue() == 1;
}
private void sendMsg(Long itemId, String type){
try {
//发送消息到MQ的交换机,通知其他系统
Map<String, Object> msg = new HashMap<String, Object>();
msg.put("itemId", itemId);
msg.put("type", type);
msg.put("date", System.currentTimeMillis());
this.rabbitTemplate.convertAndSend("item." + type, MAPPER.writeValueAsString(msg));
} catch (Exception e) {
e.printStackTrace();
}
}
}
前台系统接收消息
导入依赖
配置
具体处理逻辑
package com.taotao.web.mq.handler;
import org.springframework.beans.factory.annotation.Autowired;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.taotao.common.service.RedisService;
import com.taotao.web.service.ItemService;
public class ItemMQHandler {
@Autowired
private RedisService redisService;
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 删除缓存中的商品数据,完成数据同步
*
* @param msg
*/
public void execute(String msg) {
try {
JsonNode jsonNode = MAPPER.readTree(msg);
Long itemId = jsonNode.get("itemId").asLong();
String key = ItemService.REDIS_KEY + itemId;
this.redisService.del(key);
} catch (Exception e) {
e.printStackTrace();
}
}
}
在界面管理工具中完成绑定关系
搜索系统中接收消息
导入依赖
配置
加入其它需要的依赖和service:
处理业务逻辑
package com.taotao.search.mq.handler;
import org.apache.commons.lang3.StringUtils;
import org.apache.solr.client.solrj.impl.HttpSolrServer;
import org.springframework.beans.factory.annotation.Autowired;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.taotao.search.bean.Item;
import com.taotao.search.service.ItemService;
public class ItemMQHandler {
private static final ObjectMapper MAPPER = new ObjectMapper();
@Autowired
private HttpSolrServer httpSolrServer;
@Autowired
private ItemService itemService;
/**
* 处理消息,新增、修改、删除的消息,将商品数据同步到solr中
* 消息中并没有包含商品的基本数据,需要通过id到后台系统提供的接口中获取
*
* @param msg
*/
public void execute(String msg) {
try {
JsonNode jsonNode = MAPPER.readTree(msg);
Long itemId = jsonNode.get("itemId").asLong();
String type = jsonNode.get("type").asText();
if (StringUtils.equals(type, "insert") || StringUtils.equals(type, "update")) {
Item item = this.itemService.queryById(itemId);
this.httpSolrServer.addBean(item);
this.httpSolrServer.commit();
} else if (StringUtils.equals(type, "delete")) {
this.httpSolrServer.deleteById(String.valueOf(itemId));
this.httpSolrServer.commit();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
ItemService:
package com.taotao.search.service;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.taotao.common.service.ApiService;
import com.taotao.search.bean.Item;
@Service
public class ItemService {
@Autowired
private ApiService apiService;
@Value("${TAOTAO_MANAGE_URL}")
private String TAOTAO_MANAGE_URL;
private static final ObjectMapper MAPPER = new ObjectMapper();
public Item queryById(Long itemId) {
try {
String url = TAOTAO_MANAGE_URL + "/rest/api/item/" + itemId;
String jsonData = this.apiService.doGet(url);
if (StringUtils.isNotEmpty(jsonData)) {
return MAPPER.readValue(jsonData, Item.class);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
在管理工具中绑定队列和交换机
测试
测试结果:商品数据已经完成和索引数据的同步。
总结
使用MQ实现商品数据的同步优势:
1、 降低系统间耦合度
2、 便于管理数据的同步