什么是消息队列
消息队列是一种通信机制,用于在不同的应用程序或组件之间传递消息。它允许应用程序之间异步地发送和接收消息,而无需直接依赖彼此的可用性或性能。消息队列通常用于解耦不同组件,提高系统的可伸缩性和可维护性,以及处理异步任务和事件驱动的架构。
以下是一个简单的消息队列示例:
假设有一个电子商务网站,它需要处理来自不同来源的订单和付款通知。为了确保这些任务可以异步处理并且不会阻塞网站的核心功能,可以使用消息队列来协调这些任务。
-
订单服务:当客户下订单时,订单服务将订单数据包装成消息,并将其发布到消息队列中,例如 RabbitMQ 或 Apache Kafka。
-
付款服务:当客户成功支付订单时,付款服务将付款通知数据包装成消息,并将其发布到同一个消息队列。
-
处理订单:有一个独立的工作者服务,它监听消息队列上的消息。一旦它接收到订单消息,它会开始处理订单,可能包括验证订单、准备货物并发送通知邮件。
-
处理付款通知:另一个独立的工作者服务也监听消息队列上的消息,一旦它接收到付款通知消息,它会更新订单的支付状态。
使用消息队列的好处包括:
-
解耦:订单服务和付款服务不需要直接相互通信,它们只需将消息发布到队列中,而工作者服务负责处理消息。
-
异步处理:订单和付款通知可以异步处理,不会阻塞用户的操作,提高了网站的性能和响应时间。
-
可伸缩性:您可以轻松地增加工作者服务的数量以处理更多消息,从而实现系统的横向扩展。
-
容错性:即使工作者服务崩溃或不可用,消息队列仍然可以保存消息,以确保没有消息丢失。
这只是一个消息队列的简单示例,实际中有许多不同的消息队列系统和用例,可以满足不同的需求。
什么时候会用到消息队列
在系统架构中,消息队列的定位就是总线和管道,主要起到解耦上下游系统、数据缓存的作用。它不像数据库,会有很多计算、聚合、查询的逻辑,它的主要操作就是生产和消费。所以,我们在业务中不管是使用哪款消息队列,我们的核心操作永远是生产和消费数据。一般情况下,我们会在需要解耦上下游系统、对数据有缓冲缓存需求或者需要用到消息队列的某些功能(比如延时消息、优先级消息)的时候选择使用消息队列,然后再根据实际需求选型。
下面我们用经典的订单下单流程,来简要概括下对消息队列的使用情况。
下单流程是一个典型的系统解耦、消息分发的场景,一份数据需要被多个下游系统处理。另外一个经典场景就是日志采集流程,一般日志数据都很大,直接发到下游,下游系统可能会扛不住崩溃,所以会把数据先缓存到消息队列中。所以消息队列的基本特性就是高性能、高吞吐、低延时
消息队列网络模块高性能设计
1.如何高效管理大量的 TCP 连接
主流的消息队列 Kakfa、RocketMQ、Pulsar 的网络模块都是基于 IO 多路复用的思路开发的。IO 多路复用技术,是指通过把多个 IO 的阻塞复用到同一个 selector 的阻塞上,让系统在单线程的情况下可以同时处理多个客户端请求。最大的优势是系统开销小,系统不需要创建额外的进程或者线程,降低了维护的工作量,也节省了资源。目前支持 IO 多路复用的系统调用有 Select、Poll、Epoll 等,Java NIO 库底层就是基于Epoll 机制实现的
2.如何快速处理高并发请求
Reactor 模型是一种处理并发服务请求的事件设计模式,当主流程收到请求后,通过多路分离处理的方式,把请求分发给相应的请求处理器处理。如下图所示,Reactor 模式包含Reactor、Acceptor、Handler 三个角色。
当前业界消息队列的网络模型,比如 Pulsar、Kafka、RocketMQ,为了保证性能,都是基于主从 Reactor 多线程模型开发的。这种方案,优点是 Reactor 的主线程和子线程分工明确。主线程只负责接收新连接,子线程负责完成后续的业务处理。同时主线程和子线程的交互也很简单,子线程接收主线程的连接后,只管业务处理即可,无须关注主线程,可以直接在子线程把处理结果返回给客户端。所以,主从 Reactor 多线程模型适用于高并发场景,Netty 网络通信框架也采用了这种实现。缺点是如果基于 NIO 从零开始开发,开发的复杂度和成本较高。另外,Acceptor 是一个单线程,如果挂了,如何处理客户端新连接是一个风险点。为了解决 Acceptor 的单点问题,有些组件为了保证高可用性,会对主从 Reactor 多线程做一些优化,把 Acceptor 也变为多线程的形态。我们在公有云上商业化版本的 Kafka 就是使用的这种模型
Kafka 网络模型
一个 Acceptor 接收客户端建立连接的请求,创建 Socket 连接并分配给 Processor 处理。Processor 线程把读取到的请求存入 RequestQueue 中,Handler 线程从 RequestQueue队列中取出请求进行处理。Handler 线程处理请求产生的响应,会存放到 Processor 对应的 ResponseQueue 中,Processor 线程从其对应的 ResponseQueue 中取出响应信息,并返回给客户端。
RocketMQ 网络模型
RocketMQ 采用 Netty 组件作为底层通信库,遵循 Reactor 多线程模型,同时又在 Reactor模型上做了一些扩展和优化。所以它的网络模型是 Netty 的网络模型,Netty 底层采用的是主从 Reactor 多线程模型,模型的原理逻辑跟前面讲到的主从 Reactor 多线程模型是一样的