【RocketMQ】消息的发送

Producer

发送方式

生产者发送消息有三种方式

  • 同步(Sync):发送方线程发送后同步堵塞等待SendResult,若failed则重试下一个broker
  • 异步(Async):发送方线程发送后无需等待返回结果(返回的是null),当Broker返回结果后,生产者使用回调函数异步处理,超时可抛出Timeout异常
  • 单向(OneWay):发送方不等待broker响应也没有回调函数触发,速度快但可靠性弱
生产者类图设计

我的理解是分为三层:

1、Producer实例层:该层用于创建Topic、消息请求的创建、执行消息发送时的前后置处理、发送不同方式的消息,有几个关键参数在DefaultMQProducer中。从类图中看出,DefaultMQProducerImpl是具体的实现者,DefaultMQProducer实际是提供一些参数的。

参数默认值解释
defaultTopicQueueNums4一个主题创建4个消息队列
sendMsgTimeout3000ms发送消息超时时间
compressMsgBodyOverHowmuch4K消息体(Msg Body)长度超过4K,需要压缩
retryTimesWhenSendFailed2同步模式下消息发送失败重试次数
retryTimesWhenSendAsyncFailed2异步模式下消息发送失败重试次数
maxMessageSize4M消息最大长度

2、实例管理&服务层:该层主要有MQClientInstance实现,生产者和消费者都会调用这个类,该类具体包含以下功能:

  • 注册&取消生产者(消费者)
  • 寻找&维护Broker路由
  • 发送&查询消息
  • 基础服务:包含向所有Broker发送心跳、定时任务执行(见表格)、拉取消息、负载均衡、启动生产者。
fetchNameServerAddr间隔2分钟利用Http获取NameServer的地址
updateTopicRouteInfoFromNameServer间隔30s请求NameServer,获取最新的Topic路由关系
cleanOfflineBroker间隔30s清理不在线的Broker
sendHeartbeatToAllBrokerWithLock间隔30s向所有Broker发送心跳
persistAllConsumerOffset间隔5s把每个队列消费到的位置保存到本地文件或者Broker
adjustThreadPool间隔1分钟调整线程池,只针对于PushConsumer,调整策略是如果一个消费者未消费消息总和超过100000,增加线程CoreSize,小于80000,减小线程CoreSize,实际RocketMQ未实现该功能

3、网络通信层

RocketMQ网络通信依赖netty,因此RemotingClient接口的实际实现是NettyRemotingClient,无论是消息的消费还是生产,最终都会由它执行,具体负责消息发送拉取、接受Broker回传结果并异步处理(执行回调)、消息处理器管理(不同的请求类型使用不同的处理器,思想很好,实现时作者还是使用了同一个Processor)。

【MQClientAPIImpl】
this.remotingClient.registerProcessor(RequestCode.CHECK_TRANSACTION_STATE, this.clientRemotingProcessor, null);

this.remotingClient.registerProcessor(RequestCode.NOTIFY_CONSUMER_IDS_CHANGED, this.clientRemotingProcessor, null);

this.remotingClient.registerProcessor(RequestCode.RESET_CONSUMER_CLIENT_OFFSET, this.clientRemotingProcessor, null);

this.remotingClient.registerProcessor(RequestCode.GET_CONSUMER_STATUS_FROM_CLIENT, this.clientRemotingProcessor, null);

this.remotingClient.registerProcessor(RequestCode.GET_CONSUMER_RUNNING_INFO, this.clientRemotingProcessor, null);

this.remotingClient.registerProcessor(RequestCode.CONSUME_MESSAGE_DIRECTLY, this.clientRemotingProcessor, null);

在这一层,NettyRemotingClient不会感知到上层是发送消息还是拉取消息,对它来说,看到的都是RemotingCommand(具体封装了请求类型),只有同步、异步、单向执行,所以这三个方式和生产者的那三个(同步发送、异步发送、单向发送)还是有所不同的。

发送流程

介绍完Producer发送消息所经历的层次,分析具体的发送流程。

1、查询本地缓存是否存储了TopicPublishInfo,否则从NameServer获取。
2、根据选择策略获取待发送队列。
3、获取消息队列对应的broker实际IP。
4、设置消息Unique ID,zip压缩消息。
5、检查信息合法性,调用NettyClient发送消息

TopicPublishInfo包含队列优先级、消息队列列表、路由信息以及一个线程安全的index坐标。

public class TopicPublishInfo {
    private boolean orderTopic = false;
    private boolean haveTopicRouterInfo = false;
    private List<MessageQueue> messageQueueList = new ArrayList<MessageQueue>();
    private volatile ThreadLocalIndex sendWhichQueue = new ThreadLocalIndex();
    private TopicRouteData topicRouteData;
}
队列选择

队列选择是通过MQFaultStrategy的selectOneMessageQueue方法完成。MQFaultStrategy重要的属性包含

 //延迟容错对象,维护延迟Brokers的信息
LatencyFaultTolerance<String /*brokerName*/> latencyFaultTolerance = new LatencyFaultToleranceImpl();
//延迟容错开关
boolean sendLatencyFaultEnable = false;
//延迟级别数组
long[] latencyMax = { 50L, 100L, 550L, 1000L, 2000L, 3000L, 15000L };
//不可用时长数组
long[] notAvailableDuration = {0L, 0L, 30000L, 60000L, 120000L, 180000L, 600000L};

MQFaultStrategy中最重要的属性是latencyFaultTolerance,它维护了那些消息发送延迟较高的brokers的信息,同时延迟的时间长短对应了延迟级别latencyMax 和时长notAvailableDuration ,sendLatencyFaultEnable 控制了是否开启发送消息延迟功能。LatencyFaultToleranceImpl负责判断队列是否可用、更新Broker的延迟时间。

public boolean isAvailable(final String name) {
     final FaultItem faultItem = this.faultItemTable.get(name);
     if (faultItem != null) {
         return faultItem.isAvailable();
     }
     //如果队列中没找到,说明没有延迟记录
     return true;
}

//计算Broker被禁用时间是否到了
public boolean isAvailable() {
    return (System.currentTimeMillis() - startTimestamp) >= 0;
}

 

public void updateFaultItem(final String brokerName, final long currentLatency, boolean isolation) {
        if (this.sendLatencyFaultEnable) {
            //计算不可用时间持续多久
            long duration = computeNotAvailableDuration(isolation ? 30000 : currentLatency);
            this.latencyFaultTolerance.updateFaultItem(brokerName, currentLatency, duration);
        }
}

private long computeNotAvailableDuration(final long currentLatency) {
        for (int i = latencyMax.length - 1; i >= 0; i--) {
            if (currentLatency >= latencyMax[i])
                return this.notAvailableDuration[i];
        }

        return 0;
}
public void updateFaultItem(final String name, final long currentLatency, final long notAvailableDuration) {
        FaultItem old = this.faultItemTable.get(name);
        if (null == old) {
            final FaultItem faultItem = new FaultItem(name);
            faultItem.setCurrentLatency(currentLatency);
            faultItem.setStartTimestamp(System.currentTimeMillis() + notAvailableDuration);

            old = this.faultItemTable.putIfAbsent(name, faultItem);
            if (old != null) {
                old.setCurrentLatency(currentLatency);
                old.setStartTimestamp(System.currentTimeMillis() + notAvailableDuration);
            }
        } else {
            old.setCurrentLatency(currentLatency);
            old.setStartTimestamp(System.currentTimeMillis() + notAvailableDuration);
        }
}

最后再来看看selectOneMessageQueue究竟做了什么?

1、获取上一次使用之后的队列,从这个队列开始判断该队列所在的Broker是否可用

2、如果该Broker可用,则返回该队列

3、如果发现都不符合要求,则至少需要选择一个相对好的broker,并返回对应的队列

消息的发送先写到这里。下一篇会提到如何消费消息。

转载于:https://my.oschina.net/u/2302503/blog/1915727

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值