1.RabbitMQ
1.1.搜索与商品服务的问题
目前我们已经完成了商品详情和搜索系统的开发。我们思考一下,是否存在问题?
-
商品的原始数据保存在数据库中,增删改查都在数据库中完成。
-
搜索服务数据来源是索引库,如果数据库商品发生变化,索引库数据不能及时更新。
-
商品详情做了页面静态化,静态页面数据也不会随着数据库商品发生变化。
如果我们在后台修改了商品的价格,搜索页面和商品详情页显示的依然是旧的价格,这样显然不对。该如何解决?
为什么要解决消息不同步得问题,主要是为了解决代码得冗余,降低代码得耦合度
这个RabbitMq,其实也就是会用就可以了,关于消息队列还得在真实得实际开发场景中应用映像才能更加得深刻
关于下载和安装其实和官网得例子一样,没有什么区别,直接安装下一步就可以了,按照笔记得内容,基本都没什么问题,基本很快就可以安装完成得
主要是对消息得队列得原理得内容得理解
2.1.4.消息确认机制(ACK)
通过刚才的案例可以看出,消息一旦被消费者接收,队列中的消息就会被删除。
那么问题来了:RabbitMQ怎么知道消息被接收了呢?
如果消费者领取消息后,还没执行操作就挂掉了呢?或者抛出了异常?消息消费失败,但是RabbitMQ无从得知,这样消息就丢失了!
因此,RabbitMQ有一个ACK机制。当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收。不过这种回执ACK分两种情况:
-
自动ACK:消息一旦被接收,消费者自动发送ACK
-
手动ACK:消息接收后,不会发送ACK,需要手动调用
大家觉得哪种更好呢?
这需要看消息的重要性:
-
如果消息不太重要,丢失也没有影响,那么自动ACK会比较方便
-
如果消息非常重要,不容丢失。那么最好在消费完成后手动ACK,否则接收消息后就自动ACK,RabbitMQ就会把消息从队列中删除。如果此时消费者宕机,那么消息就丢失了。
我们之前的测试都是自动ACK的,如果要手动ACK,需要改动我们的代码:
下面是关于RabbitMq中的项目的数据同步,代码都比较简单,就几行代码,主要是注解比较多,以及注解的内容可能附加繁杂
先给生产者和消费者添加依赖和对应的配置文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
spring:
rabbitmq:
host: 192.168.56.101
username: leyou
password: leyou
virtual-host: /leyou
template:
exchange: leyou.item.exchange
publisher-confirms: true
-
template:有关
AmqpTemplate
的配置-
exchange:缺省的交换机名称,此处配置后,发送消息如果不指定交换机就会使用这个
-
-
publisher-confirms:生产者确认机制,确保消息会正确发送,如果发送失败会有错误回执,从而触发重试
生产者中固定的方法内容为:GoodsService
这里主要在商品的新增和更新的时候执行消息的发送,所以消息的发送主要的方法写在Service中
下来就是改造消费者:主要是SearchService,GoodsWeb这两个微服务
首先是GoodsWeb(其实只要一个消费者声明写好了,第二个消费者的内容和第一个消费者的内容基本一直没有什么区别)
@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.goodsHtmlService.createHtml(id);
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "LEYOU.CREATE.DELETE.QUEUE", durable = "true"),
exchange = @Exchange(
value = "LEYOU.ITEM.EXCHANGE",
ignoreDeclarationExceptions = "true",
type = ExchangeTypes.TOPIC),
key = {"item.delete"}))
public void delete(Long id){
if (id == null) {
return;
}
this.goodsHtmlService.deleteHtml(id);
}
接下来是搜索微服务里面的消费者
@RabbitListener(bindings = @QueueBinding(
value = @Queue(value = "LEYOU.SEARCH.SAVE.QUEUE",durable = "true"),
exchange = @Exchange(value = "LEYOU.ITEM.EXCHANGE",ignoreDeclarationExceptions = "true",type = ExchangeTypes.TOPIC),
key = {"item.insert","item.update"}
))
public void save(Long id) throws IOException {
if (id==null){
return;
}
this.searchService.save(id);
}
其实主要的就是注解的编写,显示Queue,Exchange,Key,队列,交换机,路由,只要这三个注解定了,方法基本没有什么可写的
这个里面有一些坑希望注意一下,第一个就是这个交换机Exchange必须在一个里面才能监听的到,之前定义的时候出现了一个比较严重的BUG:我在生产者也就是商品微服务里面:这个交换机之前是小写的,后来给改成大写的了,而我在消费者:这个里面也配置的是一样的
但是在配置注解的时候:这个注解里面定义的交换机竟然配置成大写的了,和之前的交换机明显不是一个,路由也没有错但是就是数据不能同步过来,后来进行了修改,基本问题就解决了
修改商品的价格
搜索微服务中商品价格做出了改变
商品详情中的微服务也做出来改变
下来是RabbitMq:b捕捉的修改的信息,进行同步