消息中间件基础
使用场景:在分布式系统之间,通过发送和接受消息来解耦服务,分摊单个应用同时调用多个服务的压力。
如何确保消息发送成功
先来看下传统的做法
1 | void foo1() { |
正常情况下,这两种方式出现不一致的概率可能并不大,但一旦出现比如 foo1() 消息服务器挂了等情况,那么消息就发送不出去了;或者 foo2() 消息成功,业务挂了,那么数据就不一致了。
有其他办法吗?
使用 JMS 的 XA 系列接口,通过 XA 接口对分布式事务的支持,来保证业务操作和发送消息的一致性。有两个缺点:
- 分布式事务性能开销大。
- 业务操作必须支持 XA 协议才能与发送消息一起来做分布式事务。
能不能综合一下?
1 | void foo3() { |
foo3() 比 foo2() 多增加了一次消息网络请求和状态更新操作,整体上额外的开销并不大。不过消息中间件与业务之间的依赖比较严重。
如何解决消息中间件与使用者的强依赖关系
可以考虑将本地磁盘作为一个消息存储的容器,通过应用内的消息中间件客户端,将应用内的消息写入本地磁盘,应为都在应用内,可以做成一个本地事务,保证应用内的业务和消息发送一定是成功的。
本地磁盘消息写入成功后,消息中间件客户端就可以通过(轮询本地消息等方式)控制将消息向服务端发送。
有没有问题?
当消息中间件挂了,并且本地磁盘数据也坏了的情况下,消息就丢失了。这种情况,只有在业务上进行消息的补发才是最彻底的容灾手段。
消息存储的几种方式
- 实现基于文件的消息存储
- 采用数据库进行消息存储
- 基于双击内存的消息存储
如何确保消息投递成功
投递是指:消息中间件服务端将消息发送给消息消费者。
当消息消费者显式的告诉中间件消费消费成功时,中间件才能确保消息投递成功了,从而删除消息。
重复消息的产生和方案
- 应用端消息的重复发送
如果中间件消息存储失败,应用重复发送,则是正常行为。
当中间件消息存储成功,但此时中间件服务器挂了,或者返回结果时网络出现故障,或者负载高导致响应超时,都会导致应用端收不到成功的通知,进而产生重复发送。
这种情况下一个解决办法是重试发消息时,使用同一个消息 id,而不用在消息中间件端产生消息 id。
- 消息到达了消息存储,由中间件向外投递时,产生重复。
消息消费者处理完毕后,自身的应用出问题了,或者网络出问题了,或者处理时间较长,导致返回超时,都会导致消息中间件收不到消费结果,进而重复投递。
也有可能消息中间件收到结果了,但是自身挂了,或者消息状态更新时出故障了,也会导致重复投递。
这种情况,可以让消息消费者来处理,即要求消费者对消息的处理的幂等的。
Title: 消息中间件基础
Author: mjd507
Date: 2019-06-09
Last Update: 2024-01-27
Blog Link: https://mjd507.github.io/2019/06/09/middle-ware-msg/
Copyright Declaration: This station is mainly used to sort out incomprehensible knowledge. I have not fully mastered most of the content. Please refer carefully.