MQ和Rpc都是常见的通信方式,它们并无优劣之分。然而,在不同的使用场景下,我们需要具体问题具体分析,选择最适合的通信方式。


1. 调用端关注调用结果,实时性强的场景,使用 rpc还是mq?
创建资源的流程通常需要对流程参数进行校验。如果参数不合理,将返回创建失败的具体错误码和错误信息。而创建成功则需要返回一些结果,如资源的唯一ID等。在这种情况下,使用消息队列(MQ)并不合适,因为生产者发送完MQ消息后无法知道消费者是否成功消费,也无法得知消费结果。相比之下,RPC更适合这个场景。因为被调用端通过定义RPC接口规定了请求的输入和输出格式,调用端可以像调用本地方法一样调用服务端提供的方法。
举个例子,当业务系统需要调用短信平台发送短信时,使用MQ并不适合。因为短信平台需要对短信模板和参数进行校验,确保它们的一致性和正确性。同时,还会校验短信长度是否符合要求,并将校验结果告知调用端。相比之下,RPC更适合这个场景。
考虑到发送短信接口可能会耗费较长时间,使用RPC可能造成调用端的阻塞。因此,服务端可以在完成短信请求的校验后,将请求存储到数据库中,并使用MQ或线程池进行异步发送短信。
在选择使用MQ还是RPC时,并无优劣之分,更多时候需要根据实际场景来选择合适的方案,并且它们通常需要相互配合使用。
2. 查询场景使用rpc还是mq?
由于查询场景的调用方必然关注调用结果,因此为了确保可靠性,我们在查询场景中应该使用Rpc而非MQ。这一点无需过多赘述。
3. 异步调用场景,使用Rpc还是mq?
MQ天然支持异步,生产者发送消息后即刻返回,消费者则采用推拉方式异步消费消息。因此,对于异步场景,MQ非常适合。然而,是否可以异步调用Rpc呢?
实际上,各种Rpc框架都支持异步调用,在以下三个方面体现:
- 业务线程将Rpc请求任务提交给线程池进行处理;
- Rpc使用Netty NIO方式,采用非阻塞式IO,在发送消息和处理请求时不会阻塞IO线程;
- 被调用端异步处理Rpc请求,将消息丢弃到线程池进行处理。
尽管Rpc支持异步处理请求,但相比MQ而言更为复杂。相较之下,MQ天然支持异步处理,所以在这种场景下,MQ更具优势。
4. 发布订阅模式,新增消费组时,生产者无需关注
当有多个潜在消费者关注时,如果使用Rpc,就需要调用端逐个调用生产者,需要使用线程池异步并行调用多个服务者进行处理,并且需要处理超时、处理失败、耗时高等场景。实现较为复杂。 更重要的是,生产者系统并不关心消费者,为什么要修改生产者所在系统呢?
而使用消息队列(MQ)更适用于这个场景。生产者将消息发送到消息队列后,就不再需要关心消息的处理结果。消息的可靠性投递、重试等特性都是由消费队列来实现。增加一个消费者,只需要在消息队列申请新的消费组即可。生产者系统无需任何改动。
在实际应用中,比如订单状态变更消息,有很多消费者订阅,且下游系统的很多业务逻辑基于订单状态。通过使用MQ作为中间层,可以解耦订单系统和下游系统。很难想象,如果依赖Rpc通信,订单系统要感知有多少下游系统,一个订单事件就要通知多少个下游系统。
5. MQ支持广播模式
MQ除了支持一个Topic被多个应用服务、多个消费组消费,还可以将消息广播给每个消费者实例。举个例子,在websocket场景中,由于浏览器端的websocket连接只能与一个服务端实例连接,因此需要将消息转发到该服务端实例。一种简单的方法是使用消息队列,将消息广播给所有的服务器实例,然后服务端查询自己是否管理相关的websocket连接。除此之外,其他场景,如缓存数据同步和缓存失效等,也可以通过消息广播来完成。相比之下,rpc并不适用于这种场景。
6. Mq接口格式更加灵活
一般情况下,Rpc接口需要通过IDL或者Java/C++等语言进行定义,服务端需要实现该接口,调用端需要引用接口依赖。而消息队列(MQ)在这方面更加灵活,生产者和消费者无需强依赖接口定义,只需要遵循相同的格式即可。例如,可以定义消息的JSON格式,生产者和消费者按照该格式进行解析。
相比于Rpc,消息队列在接口格式方面更加灵活。如果希望上下游接口的依赖更加灵活,可以考虑使用消息队列。
消息队列可以解耦生产者和消费者,主要体现在以下几个方面:
- 生产者和消费者可以以不同的速度进行操作,彼此之间不会产生直接的依赖。
- 无需强依赖于接口,生产者和消费者只需要遵循相同的消息格式即可进行通信。
- 异步调用可以通过消息队列来实现解耦,提高系统的性能和并发能力。
- 在订阅发布模式下,新增消费组可以无感知地进行扩展。
接下来,我们将讨论消息队列在性能和稳定性方面的优势。
7. MQ具备存储能力
消息投递到消息队列后,能够可靠地存储消息,并且提供了可视化页面以便查询消息列表。此外,还可以使用数据通道工具将kafka消息传递到Hive中,从而实现离线数据统计的能力。同时,MQ还具备消费端重新消费和回溯历史消息的能力,能够有效地保证重试消费,从而最大程度地确保处理流程的最终一致性。
相比于Rpc,MQ具备更强大的存储能力。
8. 使用MQ和binlog实现数据核对
MQ消费端涉及到数据库操作时会产生binlog日志。通过关联MQ和binlog,可以发现处理失败的情况。数据比对任务能够核对MQ消息和binlog日志,如果在一个窗口期内无法关联成功,说明该条消息处理存在问题。
将MQ消息离线同步到Hive,将数据库同步到Hive中,还可以实现对MQ消息和数据库的离线数据核对。
由于Rpc不具备存储能力,无法将Rpc请求与binlog关联,因此无法实现数据正确性核对能力。
在数据正确性核对方面,MQ更为优势。
9. Rpc & MQ谁的可靠性更高
如何处理写请求的失败和超时是业务上必须思考的问题。一般情况下,业务只需要确保最终一致性,通过重试可以有效解决超时和写失败的问题。
那么,如何实现Rpc的重试呢?我们可以借助Spring-Retry框架,并设置重试的间隔时间、重试次数等参数。然而需要注意的是,如果在Rpc的重试过程中遇到服务发布、宿主机重启等问题,重试可能会中断,因此这种方式并不可靠。
相比之下,MQ可以通过ack机制来实现重试。多次消费同一条消息,即使重试多次仍然失败,我们还可以将该消息投递到对应的死信队列,稍后继续重试和人工处理。消息队列有多种机制可以保证消费成功,而且消费者服务发布、宕机等因素也不会导致消息丢失或无法消费。
综上所述,相比Rpc,MQ在写场景下更安全可靠地实现重试,可提供更强的可靠性。
10. MQ & Rpc 吞吐量上谁更高
消息队列是一种解决生产者和消费者速度差异的有效工具。当生产者的速度超过消费者时,消息会堆积在消息队列中,消费者可以根据自身处理能力来调整消费速度。因此,生产者不会被消费者的速度拖累。相比之下,如果使用同步的远程过程调用(Rpc),当消费速度较慢时,大量的线程会被阻塞,从而降低了生产者的处理能力。
举个例子,结算场景中处理订单支付完成的消息可以通过消息队列来解耦。结算场景的处理并不会直接影响到终端用户的体验,因此对实时性的要求并不高。在高峰期,结算处理可能跟不上订单支付的速度。但如果使用同步的远程过程调用,结算性能的瓶颈将降低终端用户交易链路的稳定性。
消息队列的存储能力可以弥补消费端和生产端速度差异的问题。通过将消息队列作为中间缓冲,可以削平高峰期的请求,并避免因突发大量请求导致下游系统崩溃的风险,同时也避免了因消费端和生产端速度差异导致的服务雪崩问题。
相比而言,使用同步的远程过程调用,系统的吞吐量受限于处理能力较差的一方。而使用消息队列解耦则能最大程度地提高系统的吞吐量。
11. 消费端吞吐量的提升
Rpc服务端无法对多个rpc请求进行合并,进而批量处理。然而,MQ消费端可以采用拉取模式,批量拉取一批数据,将单次请求转换为批量请求,从而有效提高处理速度。
综上所述,对于查询场景和实时获取调用结果的写入链路,使用Rpc更为适合。而在异步、实时性要求较低、不需关注调用结果以及存在多个订阅者的场景中,则更应优先选择使用MQ。
总结
- 调用端关注调用结果,实时性强的场景,Rpc更优
- 查询场景只能使用Rpc
- 异步调用场景,Rpc和MQ均可
- 存在多个订阅者场景,更适合使用MQ
- 需要广播的场景,适合MQ
- MQ接口格式更加灵活
- MQ具备存储能力,可以基于MQ和binlog进行数据正确性核对。
- MQ处理消息可安全重试,可靠性更强
- MQ作为缓冲,可以削峰填谷,弥补生产消费两端的速率之差,避免系统雪崩
- MQ通过批量拉取批量消费,可以有效提升系统吞吐量。
我的开源项目
最后夹带一点私货,五阳最近花了3个月的时间完成一个开源项目。
开源3周以来,已有近 230 多个关注和Fork
Gitee:gitee.com/juejinwuyan…
GitHub github.com/juejin-wuya…
开源平台上有很多在线商城系统,功能很全,很完善,关注者众多,然而实际业务场景非常复杂和多样化,开源的在线商城系统很难完全匹配实际业务,广泛的痛点是
- 功能堆砌,大部分功能用不上,需要大量裁剪;
- 逻辑差异点较多,需要大量修改;
- 功能之间耦合,难以独立替换某个功能。
由于技术中间件功能诉求较为一致,使用者无需过多定制化,技术中间件开源项目以上的痛点不明显,然而电商交易等业务系统虽然通用性较多,但各行业各产品的业务差异化极大,所以导致以上痛点比较明显
所以我在思考,有没有一个开源系统,能提供电商交易的基础能力,能让开发者搭积木的方式,快速搭建一个完全契合自己业务的新系统呢?
- 他们可以通过编排和配置选择自己需要的功能,而无需在一个现成的开源系统上进行裁剪
- 他们可以轻松的新增扩展业务的差异化逻辑,不需要阅读然后修改原有的系统代码!
- 他们可以轻松的替换掉他们认为垃圾的、多余的系统组件,而不需要考虑其他功能是否会收到影响
开发者们,可以择需选择需要的能力组件,组件中差异化的部分有插件扩展点能轻松扩展。或者能支持开发者快速的重新写一个完全适合自己的新组件然后编排注册到系统中?
memberclub 就是基于这样的想法而设计的。 它的定位是电商类交易系统工具箱, 以SDK方式对外提供通用的交易能力,能让开发者像搭积木方式,从0到1,快速构建一个新的电商交易系统!
具体介绍可参见
Gitee开源地址:gitee.com/juejinwuyan…
GitHub开源地址 : github.com/juejin-wuya…
在这个项目中你可以学习到 SpringBoot 集成 以下框架或组件。
- Mybatis、Mybatis-plus 集成多数据源
- Sharding-jdbc 多数据源分库分表
- redis/redisson 缓存
- Apollo 分布式配置中心
- Spring Cloud 微服务全家桶
- RabbitMq 消息队列
- H2 内存数据库
- Swagger + Lombok + MapStruct
同时你也可以学习到以下组件的实现原理
- 流程引擎的实现原理
- 扩展点引擎实现原理
- 分布式重试组件实现原理
- 通用日志组件实现原理 参考:juejin.cn/post/740727…
- 商品库存实现原理: 参考:juejin.cn/post/731377…
- 分布式锁组件: 参考:
- Redis Lua的使用
- Spring 上下文工具类 参考: juejin.cn/post/746927…