同步/异步通讯
首先,我们知道MQ(Message Queue,消息队列)是一种通信机制,那通讯机制又是什么呢?
- 通讯机制又分为同步通讯和异步通讯
- 同步通讯可以看作是用手机给别人打电话,双方的交互是实时的,这时可以立即得到响应,但是你却不能跟多个人同时通话。
- 异步通讯就好比微信发送消息,双方的交互并不是实时的,你可以立即回复消息,也可以等一段时间回复,这样就会使得消息之间的传递有延迟,但是你还可以同时与多个人收发消息。
所以,如果我们的业务需要实时得到服务提供方的响应,则应该选择同步通讯(同步调用),而如果我们追求更高的效率,并且不需要实时响应,则应该选择异步通讯(异步调用)。
接下来我们再来理解一下同步调用和异步调用。
同步调用
以下根据一个支付服务对同步业务进行解析
首先解释一下业务流程:
- 支付服务需要先调用用户服务完成余额扣减
- 然后支付服务自己要更新支付流水单的状态
- 然后支付服务调用交易服务,更新业务订单状态为已支付
三个步骤依次执行。
看似没有问题,逻辑清晰,但是其中存在三个问题
- 拓展性差
如果在业务后期需要给它加入一个短信通知业务,积分业务等等。。
每次添加业务都需要修改大量代码(不仅要在支付服务的接口中定义新的接口,新的接口又与旧的接口中有交互),非常臃肿,不符合开闭原则,拓展性不好。
- 性能差
调用者需要等待上一个服务执行完之后,有结果后,才能继续向下执行,也就是说每次调用,调用者都是处于阻塞等待状态。最终整个业务的响应时长就是每次调用的执行时长之和:
假如每个微服务的执行时长都是50ms,则最终整个业务的耗时可能高达300ms,性能太差了,用户都等炸毛了。
- 级联失败
- “级联失败”(Cascading Failure)是一个在分布式系统中常见的现象,指的是一个系统中的某个组件或服务发生故障时,导致其他相关联的组件或服务也相继发生故障,从而引发整个系统的崩溃或性能大幅下降。
由于我们是基于OpenFeign调用交易服务、通知服务。当交易服务、通知服务出现故障时,整个事务都会回滚,交易失败。
如果只是因为短信通知故障,导致之前收到的钱又返还了,是很得不偿失的,因为你不知道这个用户是否还会在你这里进行购物。。。。
(OpenFeign 是一个用于简化 HTTP 请求的 Java 库,主要用于服务间的通信。它通过声明式的方式,使得开发者能够更简洁地发起 RESTful 请求,而不需要显式地编写底层的 HTTP 客户端代码。在这里不用过多了解)
这其实就是同步调用的级联失败问题。
针对于这三个问题,我们就必须用异步调用的方式来代替同步调用。
异步调用
异步调用方式其实就是基于消息通知的方式,一般包含三个角色:
- 消息发送者:投递消息的人,就是原来的调用方(对应上面的例子就是支付服务)
- 消息Broker:管理、暂存、转发消息,你可以把它理解成微信服务器。
- 消息接收者:接收和处理消息的人,就是原来的服务提供方(对应上面的交易服务,通知服务等等。。)
在异步调用中,发送者不再直接同步调用接收者的业务接口,而是发送一条消息投递给消息Broker。
然后接收者根据自己的需求从消息Broker那里订阅消息。每当发送方发送消息后,接受者都能获取消息并处理。
这样,发送消息的人和接收消息的人就完全解耦了。
就好比送外卖,同步调用就是外卖小哥外卖送到了必须等你在他手上把外卖拿走了才能送下一旦,异步调用就是他送到了,直接把外卖放在外卖柜上(Broker),然后给你发个消息“外卖到了”,你也只用接收“外卖到了”这个消息就好。
针对于刚刚的例子,只用在用户服务完成之后,返回支付服务发送一条消息,然后接下来的交易服务,通知服务,积分服务都只需要监听这个消息即可,他们之间是相互独立的,并发的。
不管后期增加了多少消息订阅者,作为支付服务来讲,执行问扣减余额、更新支付流水状态后,发送消息即可,不再需要在代码中增加对其他新的业务的调用。业务耗时仅仅是这三部分业务耗时,仅仅100ms,大大提高了业务性能。
另外,不管是交易服务、通知服务,还是积分服务,他们的业务与支付关联度低,现在采用了异步调用,解除了耦合,他们即便执行过程中出现了故障,也不会影响到支付服务,解决了级联失败。
同时有业务功能的添加也不需要动源代码,只需接收他们发出的消息即可,拓展性更好。
这样的话它的压力主要集中在发送消息这一步,后续的服务可以根据自己的能力按需处理消息,相对来说很平稳,这种现象我们可以理解为削峰。
MQ
综上来看,MQ的本质就是一个阻塞队列, 只不过它在阻塞队列的基础上增加了重试, 消息持久化等等功能.。
那它有什么优缺点呢?
我们用去咖啡店买咖啡为例,如果是采用同步通讯的方法去买咖啡,我们给咖啡馆店员点餐后,我们还需要站在柜台等待他把咖啡做完,然后下一位顾客才可以进行点单,整个一条队伍把咖啡都买完的时间非常久。
- 异步
如果我们在其中运用MQ的思维,在吧台准备一个自动点餐机,用户可以在上面自行点餐,然后生成一张小票,点完之后,用户就可以走了,不用等待店员把咖啡做好,而咖啡馆店员也不用在吧台等别人来点餐,他只需要在后台备料,等有订单了,再去着手做咖啡。这就是异步。
- 解耦
在点单过后,店员就着手于做咖啡,而顾客也不用站在队伍里等候,顾客可以去上厕所,可以去买早餐,这就达到了解耦的目的。
- 削峰
在人流量很大的时候,比如早高峰,很多人都需要购买咖啡,然后不同的店员做咖啡的效率不一样,每个顾客点的咖啡做的难度也不一样,店员谁有空谁就去做下一杯,就可以避免一个店员过载导致的等待时间过长,这就是削峰。
虽然有很多优点,但是这种模式下也会有缺点。
- 可用性降低
系统不同服务之间就靠一个MQ连接,如果MQ挂了,整个服务器就崩溃了(虽然不太可能)。
- 复杂度更高
消息的重复出现,消息是否会失效,还有顺序问题都导致了系统复杂的更高。
- 数据一致性的问题
系统中难免会发生某一个业务出现问题,就比如B,C,D三个服务都需要对同一个事务进行数据库的写入或者修改,B,C都成功了,D失败了,整个事务在数据库中的状态就不一样了,这也是需要考虑的点。