RocketMQ 客户端原理解析:从发送到消费

RocketMQ消费流程深度解析:从原理到实践 启动阶段完成路由发现和队列分配,拉取阶段通过长轮询机制高效获取消息,处理阶段利用线程池并发消费,位点管理确保消息不丢失。其中ProcessQueue作为本地缓存,起到了承上启下的关键作用。配置优化要根据业务特点调整线程池、批量拉取等参数。性能调优需要平衡吞吐量和延迟,合理设置缓存阈值防止内存溢出。问题排查要关注消息堆积、重复消费、消费延迟等典型问题,建立完善的监控和告警体系。 阅读详情

在分布式消息中间件领域,RocketMQ 以其高性能、低延迟和高可靠性著称。作为开发者,我们往往只需要几行代码就能完成消息的生产和消费,但在简洁的 API 背后,客户端(Client)内部运行着一套精密复杂的机制。

本文将深入剖析 RocketMQ 客户端的两大核心领域:消息发送的高可用策略与消息消费的推拉机制及重平衡(Rebalance),带你领略其源码级的设计哲学。

全文思维导图

RocketMQ Client 核心机制

消息发送 Producer

路由选择

默认轮询

故障延迟机制 LatencyFaultTolerance

发送重试

同步/异步重试

规避故障 Broker

消息消费 Consumer

Push 模式本质

长轮询 Long Polling

PullMessageService

流量控制 Flow Control

消息堆积阈值

内存阈值

重平衡 Rebalance

触发时机

分配策略

平均分配/机房优先

Offset 管理

广播模式: 本地存储

集群模式: Broker 存储


第一部分:消息发送(Producer)—— 寻找最佳路径

生产者发送消息的核心诉求是:快且不丢。为了实现这一目标,RocketMQ 设计了一套完善的路由选择和故障规避机制。

1. 路由选择策略

当生产者发送消息时,Topic 通常分布在多个 Broker 的多个 MessageQueue 上。Producer 需要从 TopicPublishInfo 中选择一个队列进行发送。

A. 默认策略:轮询(Round Robin)

在无故障情况下,RocketMQ 默认采用轮询机制。Producer 内部维护一个线程安全的 index,每次发送时递增,对队列总数取模。这保证了消息能够均匀分布在所有队列上,从而实现负载均衡。

// 源码逻辑简述 (TopicPublishInfo.java)
public MessageQueue selectOneMessageQueue(final String lastBrokerName) {
    // 简单的轮询算法
    int index = this.sendWhichQueue.incrementAndGet();
    int pos = Math.abs(index) % this.messageQueueList.size();
    return this.messageQueueList.get(pos);
}
B. 故障延迟机制(Fault Latency Mechanism)

这是生产环境高可用的关键。 如果开启了 sendLatencyFaultEnable = true(默认关闭,建议开启),客户端会启用故障规避。

当向某个 Broker 发送消息失败或耗时过长时,Producer 会将该 Broker 标记为“不可用”一段时间。

  • 原理:根据本次请求的耗时(currentLatency),计算该 Broker 的隔离时长(notAvailableDuration)。
本次发送耗时Broker 隔离时长
>= 550ms30000ms (30s)
>= 1000ms60000ms (60s)
……

发送流程图:

No

Yes

Yes

No

Yes

No

开始发送消息

开启故障延迟机制?

默认轮询选择队列

存在可用 Broker?

在可用 Broker 中轮询

退化为随机选择一个 Broker

执行发送

发送成功?

更新延迟统计

标记该 Broker 暂时不可用

触发重试

2. 如何处理发送失败?

RocketMQ 提供了自动重试机制来保证消息投递的可靠性:

  • 重试次数:默认同步发送重试 2 次(共 3 次),异步发送重试 2 次。
  • 规避策略:
    • 在重试时,如果上一次发送失败是指向 Broker-A,那么在选择下一个队列时,Producer 会尽量避开 Broker-A,选择 Broker-B 或其他节点的队列。
    • 这一机制大大提升了在 Broker 宕机或网络抖动时的发送成功率。

第二部分:消息消费(Consumer)—— 复杂的推拉博弈

消费端是 RocketMQ 机制最复杂、也是面试中最常问的部分。

1. Push 模式的本质:披着 Push 外衣的 Pull

在代码中我们使用 DefaultMQPushConsumer,感觉像是 Broker 主动把消息推给客户端。但实际上,RocketMQ 的 Push 模式是基于 Long Polling(长轮询)的 Pull 模式实现的。

为什么不用纯 Push?

纯 Push 模式下,Broker 难以掌控消费者的处理能力。如果 Broker 推送速度远超消费者处理速度,会造成消费者内存溢出(OOM)。

为什么不用纯 Pull?

纯 Pull 需要消费者不断轮询,间隔时间难定。间隔短了浪费带宽和 CPU,间隔长了消息延迟大。

RocketMQ 的长轮询(Long Polling)
  1. 发起请求:Consumer 发送拉取请求(PullRequest)给 Broker。
  2. Broker 挂起:Broker 收到请求,检查是否有新消息。
    • 有消息:立即返回。
    • 无消息:不立即返回空,而是将请求挂起(Suspend)。
  3. 等待与唤醒:
    • Broker 会持有该请求最多 15秒(默认)。
    • 在此期间,如果 CommitLog 有新消息写入,ReputMessageService 会通知唤醒请求,立即返回消息。
    • 如果超时(15s)仍无消息,返回空,Consumer 收到后立马再次发起请求。

Push 模式时序图:

Store (CommitLog) Broker Consumer (PullMessageService) Store (CommitLog) Broker Consumer (PullMessageService) 封装 PullRequest loop [监听是否有新消息] opt [15s 超时] alt [有消息] [无消息] 发送拉取请求 (Suspend=true) 查询是否有新消息? 返回消息 返回消息列表 回调 Listener 消费 挂起请求 (Hold 15s) 新消息到达! (Notify) 立即返回消息 返回 Empty 立即发起下一次 Pull

2. Flow Control(流控):保护消费者的最后一道防线

既然本质是 Pull,RocketMQ 就可以在客户端做流控。在 PullMessageService 准备去 Broker 拉取消息之前,会先检查 ProcessQueue(本地处理队列)的状态。

如果出现以下情况,消费者会暂停拉取(延迟 50ms 后再试):

  1. 数量堆积:本地缓存的消息数 > 1000 条(默认)。
  2. 内存占用:本地缓存的消息总大小 > 100MB(默认)。
  3. 跨度过大:本地缓存的消息最小 Offset 和最大 Offset 差值 > 2000(防止大量未被消费的消息堆积,通常发生在顺序消费场景)。
// 源码逻辑简述 (DefaultMQPushConsumerImpl.java)
if (cachedMessageCount > this.defaultMQPushConsumer.getPullThresholdForQueue()) {
    // 触发流控,延迟 50ms 再放入拉取任务
    this.executePullRequestLater(pullRequest, 50);
    return;
}

3. Rebalance(重平衡)机制

Rebalance 是 RocketMQ 协调消费者扩缩容的核心机制。它的目标是将 Topic 下的所有 MessageQueue 尽可能均匀地分配给 Consumer Group 下的所有 Consumer。

A. 什么时候触发?
  1. 消费者变动:新的 Consumer 上线或旧的 Consumer 下线(宕机)。
  2. 订阅关系变动:订阅的 Topic 发生变化。
  3. 队列扩容:Topic 的 MessageQueue 数量发生变化。
B. 分配策略

RocketMQ 提供了多种策略,最常用的是:

  • 平均分配(AllocateMessageQueueAveragely):
    • 类似于分页算法。
    • 例如:8个队列,3个消费者。C1 分 3 个,C2 分 3 个,C3 分 2 个。
  • 环形分配(AllocateMessageQueueAveragelyByCircle):
    • 轮流分发。
    • 例如:8个队列,3个消费者。
      • Q0 -> C1, Q1 -> C2, Q2 -> C3
      • Q3 -> C1, Q4 -> C2, Q5 -> C3 …

Rebalance 视觉演示:

Consumer_Group

Topic_Queues

Q1

Q2

Q3

Q4

Consumer A

Consumer B

平均分配策略:
2个消费者分4个队列,每人2个

C. Rebalance 的局限性

在 Rebalance 过程中(通常约 20s),可能会导致短暂的消息重复消费。因为在旧的消费者停止拉取、新的消费者开始拉取之间,Offset 的提交可能存在时差。因此,消费业务逻辑必须保证幂等性。

4. Offset 管理:从哪里跌倒,从哪里爬起

消费者消费完消息后,需要记录进度(Offset),以便宕机重启后继续消费。

  • 广播模式(Broadcasting):
    • 存储位置:消费者本地文件。
    • 原因:广播模式下,每条消息都要被每个消费者消费,各个消费者进度互不影响,没必要上传 Broker。
  • 集群模式(Clustering):
    • 存储位置:Broker 端。
    • 原因:同一个 Group 下的消费者共同消费一个 Topic,某个消费者宕机,其他消费者接手队列时,需要知道从哪里开始继续消费。Offset 存储在 Broker 的 config/delayOffset.json 和内存中。

总结

RocketMQ 客户端的设计极其精妙:

  1. 发送端通过故障延迟机制实现了高可用,聪明地避开不稳定的 Broker。
  2. 消费端利用长轮询机制,兼顾了 Push 的实时性和 Pull 的可控性。
  3. 流控机制在客户端筑起了一道防洪堤,防止下游服务被压垮。
  4. Rebalance 动态调整队列映射,实现了分布式系统的弹性伸缩。

理解这些底层机制,不仅能帮助我们写出更健壮的代码,也能在遇到消息堆积、重复消费等生产问题时,快速定位并解决问题。

【RocketMQ】架构原理、消息丢失、重复消费、顺序消费、事务消息 首先在生产者端,消息的发送分为同步、异步两种和单向发送(单向发送不保证成功,不建议使用),在同步发送消息的情况下,消息的发送会同步阻塞等待Broker返回结果,在Broker确认收到消息之后,生产者才会拿到SendResult。那就可能会导致没有人知道这个消息失败了,就导致不会重试了。不管是因为什么情况了,是真的消费失败了,还是出现了异常了,还是明明消费成功了,但是你错误的返回了失败等等情况,只要你给RocketMQ返回的是 RECONSUME_LATER ,那么消息就会重投,有重投就会有重复消费。 阅读详情

相关推荐

RocketMQ案例实战

假如是在同一个comsume组(consumeGroup1)里面,且topic定义的和生产者一样,之前订阅过生产者的消息,即已经注册到broker上面了,如上述有两个之前订阅过但是不在线,所以只有当消费者重启后会被消费:如果2个同时上线,则属于集群消费还是会被两个消费者均分,如果上线1个,则未消费消息将会被这一个全部消费掉。假如消费者是在同一个comsume组(consumeGroup1)里面,但是topic和生产者定义不一样,则意味着没订阅,此时即使生产者生产了消息,也不会被消费。

Romantic丶的博客 2131

云原生中间件RocketMQ-消费者核心参数、消费模式之集群模式

消费者采用负载均衡(集群模式)方式消费消息,一个分组(Group)下的多个消费者共同消费队列消息,每个消费者处理的消息不同。一个Consumer Group中的各个Consumer实例分摊去消费消息,即一条消息只会投递到一个Consumer Group下面的一个实例。: 单队列并行消费允许的最大跨度,默认值2000, 队列级别的流量控制阈值,拉消息本地队列缓存消息最大数,默认1000。: 消息拉取时间间隔,由于是长轮询,所以为0,但是如果应用为了流控,也可以设置大于0的值,单位毫秒,默认0。...

write less , do more 2万+

RocketMQ的底层是怎么实现发送消息,消费者感知消息进行消费

RocketMQ的底层实现发送消息以及消费者感知消息进行消费的过程,涉及多个组件和复杂的交互流程。

猪猪 1113

解析RocketMQ的client客户端

1.解析RocketMQ的client客户端 参考:http://www.cnblogs.com/atliwen/p/5216849.html 1.2个核心接口,3个默认实现 interface MQProducer --- DefaultMQProducer interface MQConsumer --- DefaultMQ

逍遥飞鹤的专栏 8204

RocketMQ

漏消费问题:通过消费进度管理、重试机制和消息持久化,RocketMQ 能有效防止漏消费。重复消费问题:虽然 RocketMQ 保证至少一次投递,但消费者需要通过幂等性设计来避免重复消费对业务造成的影响。事务消息机制:RocketMQ 提供的事务消息机制可以帮助在分布式事务场景中,确保消息消费的准确性和一致性。通过以上机制,RocketMQ 在高可靠性消息系统中提供了强大的保障,防止消息漏消费和重复消费问题。管理 事务消息。

qq_41893505的博客 2940

新手必看:电路设计里的‘地’到底怎么接?单点、多点、混合接地保姆级图解

本文深入解析电路设计中的接地技术,涵盖单点、多点和混合接地的原理与应用。通过详细的图解和实战案例,帮助新手理解接地在提供参考电位、构成电流回路及抑制电磁干扰中的关键作用,避免常见设计误区,提升电路稳定性与抗干扰能力。

weixin_28455801的博客 297

rocketMq原理_客户端与namesrv及broker的交互

对于rocketMq客户端来说,无非就两个任务,消息生产与消息消费 对于生产者来说,需要知道消息可以往哪个或者哪些目的地发送 对于消费者来说,需要知道可以从哪个或者哪些目的地拉取到消息 本文会从客户端与namesrv、broker的交互的角度讲述消息生产与消息消费大致原理,当然,重点还是围绕上文中提出的queue进行讲述: 客户端包括生产者与消费者,它们有共同的...

wb_snail的博客 4082

AutoLisp文件(.lsp)从编写到加载运行:一份给CAD新手的避坑指南

本文为CAD新手提供了一份AutoLisp文件(.lsp)从编写到加载运行的完整指南,涵盖编写环境配置、文件加载流程、调试技巧及工程化实践建议。通过详细的步骤和常见错误解决方案,帮助用户避免常见陷阱,提升AutoLisp脚本的开发效率和应用效果。

weixin_29228203的博客 894

RocketMQ Pull消费深度解析:原理、模式与最佳实践

RocketMQ Pull消费深度解析:原理、模式与最佳实践

专注后端开发 997

TI毫米波雷达人体生命体征(呼吸、心跳)信号提取算法(IWR6843ISK+DCA1000EVM)

本篇内容介绍的算法主要为传统的提取算法,但可实现在1m内呈现良好的检测效果,能够提取出非常可观的呼吸和心跳波形,但在超出1m的距离后便不适用这个算法了。远距离检测呼吸和心跳始终是这个领域一个很大的挑战。要实现远距离检测可在提取相位信号、分离呼吸和心跳信号上进行改进处理,例如线性解调提取相位、改进的VMD分离呼吸和心跳算法。

m0_61934621的博客 3万+

【面试系列】软件架构师 高频面试题及详细解答

本文介绍了软件架构师岗位的初级、中级和高级面试题及详细解答,涵盖了系统设计、面向对象设计、分布式系统、云计算技术、性能优化等关键领域。通过这些问题和解答,候选人可以深入理解软件架构师的核心职责和技能要求。此外,文章总结了在面试中需要掌握的常考知识点,包括架构模式、分布式系统管理、安全性、数据管理和DevOps实践。通过掌握这些知识点,软件架构师能够有效设计和指导复杂系统的结构,确保系统的可扩展性、性能和维护性。

在路上的专栏 850

RocketMQ原理及源码解析之Consumer消息消费

本文源码分析是基于RocketMQ 4.6.0;文中分析,省略了部分代码 1. Consumer消费设计 RocektMQ消费设计图(并发消费和顺序消费基本流程相似,顺序消费存在加锁解锁过程)。按照图中从上到下标注的序号顺序分析此图: ​ 虚线框都是consumer客户端,也就是我们的实际业务系统;Broker是MQ的消息存储服务器 RebalanceImpl(最上面Rebalance):此类主要是做了消息的负载均衡,就是把所有从nameSrv获取到的MessageQueue的信息按照一定的策略分.

Java动物园的专栏 910

虚拟网络的“隐形盾”:C#如何用代码把DDoS踢出门?

本文分享了如何用C#防御DDoS攻击的实战经验。通过三种核心防御策略:1)基于IP的速率限制中间件,使用滑动时间窗口算法控制请求流量;2)自动IP封禁服务,通过特征检测识别恶意IP并加入黑名单;3)优化数据库连接池和HTTP客户端连接池,合理配置资源参数。文中提供了详细的C#代码实现,包括限流中间件、IP封禁服务和连接池优化方案,并分享了生产环境中的调优经验,如配合Redis实现分布式限流、使用fail2ban自动封禁等技巧,帮助开发者构建更健壮的防御体系。

墨夶的博客 329

RocketMQ 核心原理 - 消息的消费

消费方式:Push(推) 或者 Pull(拉)消费模式:广播模式和集群模式消息消费反馈流量控制(包括消费并发线程数设置)消息的过滤(Tag, Key),过滤标签 TagA||TagB||TagC。

smart_an的专栏 858

CATIA二次开发——遍历几何图形集下的轴系及控制轴系生成位置

CATIA二次开发——轴系相关问题1.遍历几何图形集下的轴系2.解决轴系生成位置的问题 1.遍历几何图形集下的轴系 近期遇到的问题:遍历轴系的时候发现,AxisSystem属性的轴系只有放在轴系集合下,才能被找到,放在几何图形集或其他位置均不能被找到。本来想遍历几何图形集下的轴系元素,但是发现没有对应的属性,只能查找 几何图形集、几何元素、草图等元素,无法直接遍历轴系。 解决办法:(加入选择集,在选择集中筛选轴系类型) HybridBody axisHB = activePart.HybridBodies

WorkMan的博客 2730

YOLOv11 改进 - 注意力机制 | EMA (Efficient Multi-Scale Attention) 高效多尺度注意力:跨空间学习与多分支协同增强特征表征,优化多尺度目标检测

本文介绍了高效多尺度注意力(EMA)模块及其在YOLOv11中的结合应用。现有注意力机制在通道维度缩减时可能影响深度视觉表示,EMA模块通过结合通道和空间信息、采用多尺度并行子网络结构等创新点,实现了高效的多尺度注意力机制。其基本原理包括通道和空间注意力结合、多尺度并行子网络设计等。在多个流行基准数据集上的实验表明,该模块能有效提高特征表示能力。我们将EMA集成进YOLOv11,实验证明改进后的模型在目标检测任务中性能优越,具有广泛应用前景

魔改工程师的博客 1292

RocketMQ之原理深入解析

RocketMQ的消息模型中,这些就是比较关键的概念了。

weixin_45653328的博客111 1126

通过一个具体的例子,讲解 SAP Cloud Platform Integration(CPI) 的使用方法

最近笔者由于项目需要,又得学习一个新工具:SAP Cloud Platform Integration,简称 CPI,以前又叫做HCI - HANA Cloud Platform Integration Service.笔者想通过本文,把使用这个工具的第一手体会,分享给曾经用过SAP CRM中间件,但尚未有机会接触到CPI的朋友们。之所以本文标题里把SAP CRM中间件和SAP CPI关联在一起,是因为二者同广义上说,扮演的都是系统集成里中间件的角色。SAP ERP和CRM通过qRFC进行业务数据同步,而中

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 2274

RocketMQ消息轨迹全解析:从发送到消费的完整追踪链路

本文全面解析了RocketMQ消息轨迹功能,从发送到消费的完整追踪链路。详细介绍了消息轨迹的核心价值、配置方法、数据结构及存储原理,并提供了生产环境优化建议。通过消息轨迹,开发者可以高效排查消息丢失、消费失败等问题,提升分布式系统的可靠性。

weixin_27134495的博客 405

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

RocketMQ:拉模式消费者 DefaultLitePullConsumer

RocketMQ:拉模式消费者 DefaultLitePullConsumer

weixin_46619605的博客 1827

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

RocketMQ客户端配置详解

RocketMQ的客户端和服务端采取完全不一样的配置机制——客户端没有配置文件,所有的配置选项需要开发者使用对应的配置的setter进行设置。由于现在网上基本找不到一套较为完善的配置文档,今天来讲讲截止至RocketMQ 4.0,客户端已有的所有配置的含义。本文尽可能不深入讨论配置相关的原理和源码(除非笔者认为有必要交代对应的背景),尽可能以最简单的描述说清楚作为一个开发者从使用的角度需要明白的那...

菜鸟腾飞 1万+

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

RocketMQ之消费重试以及原理

对于顺序消息,当消费者消费消息失败后,消费者会在本地自动不断进行消息重试,每次间隔时间为 1 秒,重试最大值是 Integer.MAX_VALUE。对于无序消息(普通、定时、延时、事务消息)当消费者消费消息失败时可以通过设置返回状态达到重试的目的。广播模式下消息队列RocketMQ保证每条消息至少被每台客户端消费一次,但是并不会对消费失败的消息进行失败重投,因此业务方需要关注消费失败的情况。当消费者消费消息失败后,消费者会在本地自动不断进行消息重试,每次间隔时间为 1 秒,重试最大值是 Integer.MA

daobuxinzi的博客 2231

RocketMQ消费者之消息消费过程分析

消息拉取-PullMessageService PUSH模式 PullRequest 流量控制 回调处理-PullCallback 消息消费-ConsumeMessageService 并发消费-ConsumeMessageConcurrentlyService 重试队列 提交offset 关于offset的值 心跳机制 在Consumer启动后,它就会通过定时任务不断地向RocketMQ集群中的所有Broker实例发...

qq_36654870的博客 1987

RocketMQ - 源码解读:核心模块(生产者/消费者/Broker)工作原理

本文深入解析了RocketMQ核心模块的工作原理,包括生产者(Producer)、消费者(Consumer)和Broker三大组件。从源码层面详细剖析了Producer的初始化流程、消息发送机制,Consumer的消息拉取处理过程,以及Broker的消息存储架构。通过分析DefaultMQProducer、DefaultMQPushConsumer等核心类实现,揭示了RocketMQ高性能、高可用的设计原理,包括消息路由、负载均衡、存储优化等关键技术点,为开发者深入理解和使用RocketMQ提供了有价值的参

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

RocketMQ 消息消费:PushConsumer vs PullConsumer

RocketMQ的消息消费机制解析:PushConsumer和PullConsumer本质上都是基于Pull模式实现的。PushConsumer通过长轮询(Pull+回调)模拟推送效果,提供更简单的编程模型和近实时性,是官方推荐方式。PullConsumer则需手动控制拉取逻辑,灵活性更高但复杂度也更高。文章详细对比了两种模式的实现原理、适用场景和配置参数,指出RocketMQ采用"伪推送"设计是为了降低Broker压力、提高性能和兼容性,并提供了最佳实践建议。核心结论是:RocketM

csdn_tom_168的博客 1064

SpringBoot整合RocketMQ监听器实战:从注解配置到消息消费的完整流程解析

本文深入解析了SpringBoot整合RocketMQ监听器的完整流程,从基础配置到高级应用,涵盖注解配置、消息消费、性能优化及源码实现。重点介绍了@RocketMQMessageListener注解的使用和onMessage方法的实现,帮助开发者高效构建分布式消息系统。

s4t5u6v的博客 901
上一篇: Redis 大 Key问题解析与治理
下一篇: RocketMQ高可用架构与高级特性
TracyCoder123
博客等级 码龄8年 1万+粉丝 226原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TracyCoder123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值