040 RabbitMq及数据同步02
Posted luckyplj
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了040 RabbitMq及数据同步02相关的知识,希望对你有一定的参考价值。
1.
注意:Spring-amqp是对AMQP协议的抽象实现,而spring-rabbit 是对协议的具体实现,也是目前的唯一实现。底层使用的就是RabbitMQ。
(2)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
spring: rabbitmq: host: 127.0.0.1 username: guest password: guest virtual-host: /
(3)监听者
在SpringAmqp中,对消息的消费者进行了封装和抽象,一个普通的JavaBean中的普通方法,只要通过简单的注解,就可以成为一个消费者。
@Component public class Listener @RabbitListener(bindings = @QueueBinding( value = @Queue(value = "spring.test.queue", durable = "true"), exchange = @Exchange( value = "spring.test.exchange", ignoreDeclarationExceptions = "true", type = ExchangeTypes.TOPIC ), key = "#.#")) public void listen(String msg) System.out.println("接收到消息:" + msg);
-
@Componet
:类上的注解,注册到Spring容器 -
@RabbitListener
:方法上的注解,声明这个方法是一个消费者方法,需要指定下面的属性:-
bindings
:指定绑定关系,可以有多个。值是@QueueBinding
的数组。@QueueBinding
包含下面属性:-
value
:这个消费者关联的队列。值是@Queue
,代表一个队列 -
exchange
:队列所绑定的交换机,值是@Exchange
类型 -
key
:队列和交换机绑定的RoutingKey
-
-
类似listen这样的方法在一个类中可以写多个,就代表多个消费者。
(4)
-
指定交换机、RoutingKey和消息体
-
指定消息
-
@RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class MqDemo @Autowired private AmqpTemplate amqpTemplate; @Test public void testSend() throws InterruptedException String msg = "hello, Spring boot amqp"; this.amqpTemplate.convertAndSend("spring.test.exchange","a.b", msg); // 等待10秒后再结束 Thread.sleep(10000);
运行后查看日志:
2.搜索服务、商品静态页的数据同步
(1)思路分析
<1>发送方:商品微服务
-
当商品服务对商品进行写操作:增、删、改的时候,需要发送一条消息,通知其它服务。
-
发送什么内容?
对商品的增删改时其它服务可能需要新的商品数据,但是如果消息内容中包含全部商品信息,数据量太大,而且并不是每个服务都需要全部的信息。因此我们只发送商品id
-
搜索微服务:
-
增/改:添加新的数据到索引库
-
删:删除索引库数据
-
-
静态页微服务:
-
增/改:创建新的静态页
-
删:删除原来的静态页
-
(2)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
<2>配置文件
我们在application.yml中添加一些有关RabbitMQ的配置:
spring: rabbitmq: host: 127.0.0.1 username: guest password: guest virtual-host: / template: exchange: leyou.item.exchange publisher-confirms: true
-
-
exchange:缺省的交换机名称,此处配置后,发送消息如果不指定交换机就会使用这个
-
-
@Autowired
private AmqpTemplate amqpTemplate;
/** * 利用rabbitmq发送消息 * @param id * @param type */ private void sendMessage(Long id, String type) // 发送消息 try this.amqpTemplate.convertAndSend("item." + type, id); catch (Exception e) //logger.error("商品消息发送异常,商品id:", type, id, e);
/** * 新增商品 * @param spuBo */ @Override @Transactional //添加事务 public void saveGoods(SpuBo spuBo) // 01 新增spu // 设置默认字段 spuBo.setId(null); spuBo.setSaleable(true); //设置是否可售 spuBo.setValid(true); spuBo.setCreateTime(new Date()); //设置创建时间 spuBo.setLastUpdateTime(spuBo.getCreateTime()); //设置更新时间 this.spuMapper.insertSelective(spuBo); // 02 新增spuDetail SpuDetail spuDetail = spuBo.getSpuDetail(); spuDetail.setSpuId(spuBo.getId()); this.spuDetailMapper.insertSelective(spuDetail); saveSkuAndStock(spuBo); //发送rabbitmq消息 sendMessage(spuBo.getId(),"insert");
修改的时候调用:
/** * 更新商品 * @param spu */ @Override @Transactional public void updateGoods(SpuBo spu) // 查询以前sku Sku sku=new Sku(); sku.setSpuId(spu.getId()); List<Sku> skus = this.skuMapper.select(sku); // 如果以前存在,则删除 if(!CollectionUtils.isEmpty(skus)) List<Long> ids = skus.stream().map(s -> s.getId()).collect(Collectors.toList()); // 删除以前库存 Example example = new Example(Stock.class); example.createCriteria().andIn("skuId", ids); this.stockMapper.deleteByExample(example); // 删除以前的sku Sku record = new Sku(); record.setSpuId(spu.getId()); this.skuMapper.delete(record); // 新增sku和库存 saveSkuAndStock(spu); // 更新spu spu.setLastUpdateTime(new Date()); spu.setCreateTime(null); //不能更新的内容,设置为null spu.setValid(null); spu.setSaleable(null); this.spuMapper.updateByPrimaryKeySelective(spu); // 更新spu详情 this.spuDetailMapper.updateByPrimaryKeySelective(spu.getSpuDetail()); //发送rabbitmq消息 sendMessage(spu.getId(),"insert");
(3)
-
增:添加新的数据到索引库
-
删:删除索引库数据
-
改:修改索引库数据
因为索引库的新增和修改方法是合二为一的,因此我们可以将这两类消息一同处理,删除另外处理。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
<2>
spring: rabbitmq: host: 127.0.0.1 username: guest password: guest virtual-host: /
这里只是接收消息而不发送,所以不用配置template相关内容。
<3>
package lucky.leyou.listener; import lucky.leyou.service.SearchService; import org.springframework.amqp.core.ExchangeTypes; import org.springframework.amqp.rabbit.annotation.Exchange; import org.springframework.amqp.rabbit.annotation.Queue; import org.springframework.amqp.rabbit.annotation.QueueBinding; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class GoodsListener @Autowired private SearchService searchService; /** * 处理insert和update的消息 * * @param id * @throws Exception */ @RabbitListener(bindings = @QueueBinding( value = @Queue(value = "leyou.create.index.queue", durable = "true"), exchange = @Exchange( value = "leyou.item.exchange", ignoreDeclarationExceptions = "true", type = ExchangeTypes.TOPIC), key = "item.insert", "item.update")) public void listenCreate(Long id) throws Exception if (id == null) return; // 创建或更新索引 this.searchService.createIndex(id); /** * 处理delete的消息 * * @param id */ @RabbitListener(bindings = @QueueBinding( value = @Queue(value = "leyou.delete.index.queue", durable = "true"), exchange = @Exchange( value = "leyou.item.exchange", ignoreDeclarationExceptions = "true", type = ExchangeTypes.TOPIC), key = "item.delete")) public void listenDelete(Long id) if (id == null) return; // 删除索引 this.searchService.deleteIndex(id);
public void createIndex(Long id) throws IOException Spu spu = this.goodsClient.querySpuById(id); // 构建商品 Goods goods = this.buildGoods(spu); // 保存数据到索引库 this.goodsRepository.save(goods); public void deleteIndex(Long id) this.goodsRepository.deleteById(id);
创建索引的方法可以从之前导入数据的测试类中拷贝和改造。
以上是关于040 RabbitMq及数据同步02的主要内容,如果未能解决你的问题,请参考以下文章