RocketMQ5.0 Proxy

RocketMQ 5.0 为了更好地拥抱云原生,引入了无状态的 Proxy 模块,新的架构图如下:

引入 Proxy 模块后,Proxy 承担了协议适配、权限管理、消息管理等计算功能,Broker 则更加专注于存储。这样存储和计算相分离,在云原生环境下可以更好地进行资源调度。

介绍

RocketMQ5.0把部分功能下沉到Proxy,Proxy承接了之前客户端的计算能里,客户端变得更加轻量级

NameServer

从上面的架构图可以看到,Producter/Consumer不再需要注册到NameServer,这一部分下移到了Proxy,由Proxy跟NameServer进行交互,比如查询TopicRouteData

public class CompletableFuture {
    public CompletableFuture<QueryRouteResponse>
    queryRoute(ProxyContext ctx, QueryRouteRequest request) {
        CompletableFuture<QueryRouteResponse> future = new CompletableFuture<>();
        try {//省略部分代码
            ProxyTopicRouteData proxyTopicRouteData = this.messagingProcessor.getTopicRouteDataForProxy(ctx, addressList, topicName);
            List<MessageQueue> messageQueueList = new ArrayList<>();
            Map<String, Map<Long, Broker>> brokerMap = buildBrokerMap(proxyTopicRouteData.getBrokerDatas());
            TopicMessageType topicMessageType = messagingProcessor.getMetadataService().getTopicMessageType(topicName);
            for (QueueData queueData : proxyTopicRouteData.getQueueDatas()) {
                String brokerName = queueData.getBrokerName();
                Map<Long, Broker> brokerIdMap = brokerMap.get(brokerName);
                if (brokerIdMap == null) {
                    break;
                }
                for (Broker broker : brokerIdMap.values()) {
                    messageQueueList.addAll(this.genMessageQueueFromQueueData(queueData, request.getTopic(), topicMessageType, broker));
                }
            }
            QueryRouteResponse response = QueryRouteResponse.newBuilder().setStatus(ResponseBuilder.getInstance().buildStatus(Code.OK, Code.OK.name())).addAllMessageQueues(messageQueueList).build();
            future.complete(response);
        } catch (Throwable t) {
            future.completeExceptionally(t);
        }
        return future;
    }
}

Proxy 适配多种协议,比如 HTTP、gRPC、remoting 等,不同协议的客户端跟 Proxy 建立连接后,Proxy 统一使用 remoting 协议跟 Broker、NameServer 进行通信。

流量控制

客户端所有的请求都要经过 Proxy,Proxy 将流量分发到 Broker。这样在 Proxy 可以进行流量控制和流量治理。

POP模式

我们知道,PUSH消费模式下,Borker中的每个MessageQueue只能被同一个ConsumerGroup中的一个消费者消费如图

Push模式存在下面几个问题

  1. 消费者最大数量只能等于MessgaeQueue数量,消费者数量等于MessageQueue的数量后,在增加消费者,也不能提高消费能力了;
  2. 客户端处理的逻辑比较多,比如负载均衡,offset管理、消费失败后的处理(比如发送失败消息会Broker);
  3. 如果一个消费者机器故障,比如上图中Consumer0这个消费者hang住了,Topic1下的俩个MessageQueue就不能被消费了,导致日志消息积压,最终只能是重启或线下Consumer0,Consumer做重平衡;
  4. 客户端很重,如果要用其他的语言编写工作量很大。

基于PUSH消费模式的不足,RocketMQ5.0引入了POP消费模式,如下图:

跟PUSH模式相比,POP模式客户端有如下优势:

  1. POP模式消费者可以拉取所有的MessageQueue,这样即使某一个消费者hang住,也不影响某一个MessageQueue的消费;
  2. POP模式消费者不会再重平衡,因为每个消费者默认会去所有的MessageQueue拉取消息
  3. 因为消费者可以拉取所有的MessageQueue消息,所以增加消费者数量,是可以提高消费能力的;
  4. 减少了很多逻辑,变得客户端轻量化了,可以方便多语言实现
  5. 消费者不再维护offset(offset由Broker维护),变成了无状态组件

proxy基于gRPC的标准性,兼容性和多语言传输代码生成能力,可以轻易构建多语言的轻量级客户端。

部署方式

根据不同的场景,PRoxy有俩种部署方式LOCAL方式和CLUSTER模式

LOCAL模式

RocketMQ4.x版本Client和Broker直接通讯,RocketMQ5.0引入Proxy后,Client和Broker之间的通讯多了一层,也增加了一次序列化和反序列化的过程,这势必增加了延迟,对于延迟敏感的场景可能不能接受。RocketMQ5.0引入了LOCAL模式部署Proxy:如下图

Proxy仍然可以适配多种语言的客户端,而且Proxy和Borker部署在一起,通信方式使用进程内通信,这样可以减少因为多一道网络带来的延迟,提高吞吐量。同时运维也变得简单,运维成本降低。

LOCAL模式有一个缺点,因为Proxy部署在Borker端,受网络环境的限制,对于多网络接入的情况并不友好,成本高。

CLUSTER模式

CLUSTER模式主要用于对延迟不敏感的场景,Proxy独立部署,在Proxy层适配网络的接入,同时Proxy和Broker可以独立扩容,互不影响。

LOCAL 模式更适合对延迟敏感、期望运维成本低、网络接入类型单一的场景。

CLUSTER 模式更适合对延迟要求低、网络接入类型多样的场景。

Last modification:November 30, 2023
如果觉得我的文章对你有用,请随意赞赏