引入消息中间件的作用:
- 复杂系统的解耦
- 复杂链路的异步调用
- 瞬时高峰的削峰处理
瞬时高峰期,一下涌入大量的请求,就可以积压在MQ里面,然后慢慢的处理和消费。
引入消息中间件后的缺点:
- 系统可用性降低(MQ挂掉【依赖】)
- 系统稳定性降低(网络故障- -消息丢失;消息重复- -脏数据的产生;宕机,无法消费消息- -消息积压)
- 分布式一致性问题
系统C现在处理自己本地数据库成功了,然后发送了一个消息给MQ,系统D也确实是消费到了。但系统D操作自己本地数据库失败了。系统C成功了,D失败,导致系统整体数据不一致。
消息中间件在项目里的落地
【Java进阶面试系列之三】消息中间件在你们项目里是如何落地的?
使用消息中间件来进行异步化调用(仓储调度发货耗时几十秒)
RabbitMQ使用
两个服务之间的通信:
订单服务可启动多个,不同的订单服务都可以往一个RabbitMQ的queue里推送消息。(生产者Producer 生产消息投递到MQ)
仓储服务也可以启动多个,多个仓储服务会采用round-robin的轮询算法,每个服务实例都可以从RabbitMQ queue里消费到一部分的消息。(消费者Consumer 从MQ消费消息)
【Java进阶面试系列之四】线上服务宕机时,如何保证数据不丢失
MQ中间件使用中的基础技术问题
意外宕机,问题凸现
RabbitMQ中间件默认行为,就是只要仓储服务收到一个订单消息,RabbitMQ就会立马把这条订单消息给标记为删除,这个行为叫做自动ack,也就是投递完成一条消息就自动确认这个消息处理完毕了。
如果仓储服务收到了一个订单消息,但是还没来得及对仓库系统完成商品的调度发货,结果直接就宕机了。这个订单消息就丢失了。
(用户支付了8999元,对一个iphone8下了订单,结果呢,死等活等了好几天,就是不见网站上显示他的iphone8在发货。)
channel.basicConsume(QUEUE_NAME,true,deliverCallback,consumerTag->{});
参数true(默认配置)表示RabbitMQ 只要把一个消息投递到仓储服务上,立马就把这个消息标记为删除。
如果希望不要因为仓储服务的突然宕机导致一条订单消息丢失,需要把那个参数从true改为false。(关闭autoAck,不默认消息处理成功)
改造处理订单消息的代码
在对订单完成了调度发货之后,在finally代码块中手动执行了ack操作。