RocketMQ一个实例能消费另一个就不能消费?

一文带你理解 RocketMQ 广播模式实现机制 本文主要讲解了RocketMQ广播消息的实现机制,理解广播消息,要把握下面几点1.偏移量保存在消费者本地内存和文件中;2.广播消息不支持重试;3.从源码上看,广播模式并不能支持顺序消息;4.广播模式消费者订阅了Topic下的所有MessageQueue,不会重平衡。httpshttpshttpshttps。... 阅读详情

前言

RocketMQ 有两种消费模式,集群模式和广播模式。
集群模式是指 RocketMQ 中的一条消息只能被同一个消费者组中的一个消费者消费。如下图,Producer 向 TopicTest 这个 Topic 并发写入 3 条新消息,分别被分配到了 MessageQueue1~MessageQueue3 这 3 个队列,然后 Group 中的三个 Consumer 分别消费了一条消息:
 

广播模式是 RocketMQ 中的消息会被消费组中的每个消费者都消费一次,如下图:
 

使用 RocketMQ 的广播模式时,需要在消费端进行定义,下面是一段官方示例:
public static void main(String[] args) throws InterruptedException, MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_1");

consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);

consumer.setMessageModel(MessageModel.BROADCASTING);

consumer.subscribe("TopicTest", "TagA || TagC || TagD");

consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});

consumer.start();
System.out.printf("Broadcast Consumer Started.%n");
}

从代码中可以看到,在定义 Consumer 时,通过 messageModel 这个属性指定消费模式,这里指定为 BROADCASTING,也就启动了广播模式的消费者。
1 消费者启动
以 RocketMQ 推模式为例,看一下消费者调用关系类图:
 

DefaultMQPushConsumer 作为启动入口类,它的 start 方法调用了 DefaultMQPushConsumerImpl 类的 start 方法,下面重点看一下这个方法。
1.1 拷贝订阅关系
start 方法中调用了 copySubscription 方法,代码如下:
private void copySubscription() throws MQClientException {
try {
//拷贝订阅关系
switch (this.defaultMQPushConsumer.getMessageModel()) {
case BROADCASTING:
break;
case CLUSTERING:
final String retryTopic = MixAll.getRetryTopic(this.defaultMQPushConsumer.getConsumerGroup());
SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(retryTopic, SubscriptionData.SUB_ALL);
this.rebalanceImpl.getSubscriptionInner().put(retryTopic, subscriptionData);
break;
default:
break;
}
} catch (Exception e) {
throw new MQClientException("subscription exception", e);
}
}

这里的代码有一点需要注意:集群模式会创建一个重试 Topic 的订阅关系,而广播模式是不会创建这个订阅关系的。也就是说广播模式不考虑重试。
1.2 初始化偏移量
下面是初始化 offset 的代码:
if (this.defaultMQPushConsumer.getOffsetStore() != null) {
this.offsetStore = this.defaultMQPushConsumer.getOffsetStore();
} else {
switch (this.defaultMQPushConsumer.getMessageModel()) {
case BROADCASTING:
this.offsetStore = new LocalFileOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
break;
case CLUSTERING:
this.offsetStore = new RemoteBrokerOffsetStore(this.mQClientFactory, this.defaultMQPushConsumer.getConsumerGroup());
break;
default:
break;
}
this.defaultMQPushConsumer.setOffsetStore(this.offsetStore);
}

从上面的代码可以看到,广播模式使用了 LocalFileOffsetStore,也就是说偏移量保存在客户端本地,除了在内存中会保存,在本地文件中也会保存。
2 消息拉取
ConsumeMessageService 是真正拉取消息的地方,消费者初始化时会初始化 ConsumeMessageService,并且这里会区分并发消息还是顺序消息。
2.1 顺序消息
在集群模式下,需要获取到 processQueue 的锁才会拉取消息,而在广播模式下,不用获取锁,直接就可以拉取消息。判断逻辑如下:
//ConsumeMessageOrderlyService.ConsumeRequest
final Object objLock = messageQueueLock.fetchLockObject(this.messageQueue);
synchronized (objLock) {
if (MessageModel.BROADCASTING.equals(ConsumeMessageOrderlyService.this.defaultMQPushConsumerImpl.messageModel())
|| (this.processQueue.isLocked() && !this.processQueue.isLockExpired())) {
}
}

这里有个疑问,对于顺序消息,获取锁是必须的,这样才能保证一个 processQueue 只能由一个线程进行处理,从而保证消费的顺序性。那对于广播模式,为什么不用获取 processQueue 的 锁呢?难道广播模式不支持顺序消息?
2.2 并发消息
对于并发消息,广播模式不同的是,对消费结果的处理。集群模式消费失败后需要把消息发送回 Broker 等待再次被拉取,而广播模式则不需要重试。代码如下:
//ConsumeMessageConcurrentlyService.rocessConsumeResult
switch (this.defaultMQPushConsumer.getMessageModel()) {
case BROADCASTING:
for (int i = ackIndex + 1; i < consumeRequest.getMsgs().size(); i++) {
MessageExt msg = consumeRequest.getMsgs().get(i);
log.warn("BROADCASTING, the message consume failed, drop it, {}", msg.toString());
}
break;
case CLUSTERING:
List<MessageExt> msgBackFailed = new ArrayList<MessageExt>(consumeRequest.getMsgs().size());
for (int i = ackIndex + 1; i < consumeRequest.getMsgs().size(); i++) {
MessageExt msg = consumeRequest.getMsgs().get(i);
boolean result = this.sendMessageBack(msg, context);
if (!result) {
msg.setReconsumeTimes(msg.getReconsumeTimes() + 1);
msgBackFailed.add(msg);
}
}

if (!msgBackFailed.isEmpty()) {
consumeRequest.getMsgs().removeAll(msgBackFailed);

this.submitConsumeRequestLater(msgBackFailed, consumeRequest.getProcessQueue(), consumeRequest.getMessageQueue());
}
break;
default:
break;
}

这再次说明,广播模式是不支持消息重试的。
3 重平衡
在消费者启动过程中,会调用 RebalanceService 的 start 方法,进行重平衡。从重平衡的代码中可以看到,广播模式消费者会消费所有 MessageQueue,而集群模式下会根据负载均衡策略选择其中几个 MessageQueue。代码如下:
private void rebalanceByTopic(final String topic, final boolean isOrder) {
switch (messageModel) {
case BROADCASTING: {
Set<MessageQueue> mqSet = this.topicSubscribeInfoTable.get(topic);
if (mqSet != null) {
boolean changed = this.updateProcessQueueTableInRebalance(topic, mqSet, isOrder);
//省略部分逻辑
} else {
}
break;
}
case CLUSTERING: {
Set<MessageQueue> mqSet = this.topicSubscribeInfoTable.get(topic);
List<String> cidAll = this.mQClientFactory.findConsumerIdList(topic, consumerGroup);
//省略部分逻辑
if (mqSet != null && cidAll != null) {
//省略部分逻辑
try {
allocateResult = strategy.allocate(
this.consumerGroup,
this.mQClientFactory.getClientId(),
mqAll,
cidAll);
} catch (Throwable e) {
return;
}

Set<MessageQueue> allocateResultSet = new HashSet<MessageQueue>();
if (allocateResult != null) {
allocateResultSet.addAll(allocateResult);
}

boolean changed = this.updateProcessQueueTableInRebalance(topic, allocateResultSet, isOrder);
//省略部分逻辑
}
break;
}
default:
break;
}
}

上面 updateProcessQueueTableInRebalance 这个方法调用前,要获取到需要消费的 MessageQueue 集合。广播模式下,直接取了订阅的 Topic 下的所有集合元素,而集群模式下,则需要通过负责均衡获取当前消费者自己要消费的 MessageQueue 集合。
4 总结
本文主要讲解了 RocketMQ 广播消息的实现机制,理解广播消息,要把握下面几点:
1.偏移量保存在消费者本地内存和文件中;
2.广播消息不支持重试;
3.从源码上看,广播模式并不能支持顺序消息;
4.广播模式消费者订阅了 Topic 下的所有 MessageQueue,不会重平衡。
·············· END ··············

发布于 2022-07-20 13:19・IP 属地湖南

​ 修改​赞同​添加评论

​分享

​收藏一键生成视频

​设置

收起​

更多回答

Jaskey Lam

​

腾讯 架构师

7 人赞同了该回答

从你现象上看,最可能的情况是:你的消费端A 和 B属于同一个消费组(ConsumerGroup)。但是两个实例(消费端)却订阅了不同的topic即不同的订阅关系(一个订阅T1,一个订阅T2)。这在RocketMQ中是明确声明过的——“同一个消费组应该拥有完全一样的订阅关系”。

拥有不同的订阅关系就有可能会导致你的现象。具体底层的原因来自于RocketMQ对于负载均衡分配queue的策略。简单的说就是每次分配queue的时候,相互的计算互相被干扰了,导致了漏分queue的现象。

更多负载均衡的解析请参看:

RocketMQ中关于消费者Rebalance过程? - Jaskey Lam 的回答 - 知乎RocketMQ--水平扩展及负载均衡详解 - 薛定谔的风口猪

编辑于 2017-02-04 10:29

​赞同 7​​1 条评论

​分享

​收藏​喜欢

​

后端进阶

1 人赞同了该回答

1.

假设有消费组 g1,有消费者 c1 和 c2,c1 订阅了 topicA,c2 订阅了 topicB,集群内有 broker1 和broker2,假设 topicA 有 8 个消息队列,broker_a(q0/q1/q2/q3) 和 broker_b(q0/q1/q2/q3),前面我们知道 findConsumerIdList 方法会获取消费组内所有消费者客户端 ID,topicA 经过平均分配算法进行分配之后的消费情况如下:

c1:broker_a(q0/q1/q2/q3)

c2:broker_b(q0/q1/q2/q3)

问题就出现在这里,c2 根本没有订阅 topicA,但根据分配算法,却要加上 c2 进行分配,这样就会导致这种情况有一半的消息被分配到 c2 进行消费,被分配到 c2 的消息队列会延迟十几秒甚至更久才会被消费,topicB 同理。

2.

假设有消费者组 g1,g1下有消费者 c1 和消费者 c2,c1 订阅了 topicA,c2 订阅了 topicB,此时c2 先启动,将 g1 的订阅信息更新为 topicB,c1 随后启动,将 g1 的订阅信息覆盖为 topicA,c1 的 Rebalance 负载将 topicA 的 pullRequest 添加到 pullRequestQueue 中,而恰好此时 c2 心跳包又将 g1 的订阅信息更新为 topicB,那么此时 c1 的 PullMessageService 线程拿到 pullRequestQueue 中 topicA 的 pullRequest 进行消息拉取,然而在 broker 端找不到消费者组 g1 下 topicA 的订阅信息(因为此时恰好被 c2 心跳包给覆盖了),就会报消费者订阅信息不存在的错误了。

RocketMQ 集群消费与广播消费 每个消费者分组只初始化唯一一个消费者,每个消费者可消费到消费者分组内所有的消息,各消费者分组都订阅相同的消息,以此实现单客户端级别的广播一对多推送效果。:,每个消费者分组下初始化了多个消费者,这些消费者共同分担消费者分组内的所有消息,实现消费者分组内流量的水平拆分和均衡负载。该方式一般可用于网关推送、配置推送等场景。该方式一般可用于微服务解耦场景。消费者启动时指定消费模式,通过。处理,根据分配的队列拉取消息。处理消息,区分并发/顺序消费。:消费完成后提交进度。 阅读详情

相关推荐

【消息队列】RocketMQ 生产和消费中的集群模式和广播模式

在消息队列系统中,生产者和消费者的模式通常包括集群模式和广播模式。这两种模式分别用于不同的场景,具有不同的特点和优势。

代码的魅力,根本走不出来 1789

RocketMQ使用广播消息

目录说明生产端消费端总结 说明 RocketMQ消息模式主要有两种: (1)、MessageModel.CLUSTERING:集群模式。同一消费者组内的每个消费者,只消费到Topic的一部分消息,所有消费者消费的消息加起来就是Topic的所有消息。 (2)、MessageModel.BROADCASTING:广播模式。同一消费者组内的每个消费者,都消费到Topic的所有消息。如Topic有100条消息,则同个消费者组下的所有消费者都能消费到100条消息。 消息广播,主要配置在于消费者通过配置消息模式Mess

LZJLZJLZJLZJLZJLZJ的专栏 7865

RocketMQ——广播消费模式与集群消费模式

如有疑问可通过微信公众号“假装正经的程序员”沟通

东天里的冬天 4万+

RocketMQ消息发送之广播模式

在RocketMQ中,广播消息是一种特殊的消息消费模式。在广播模式下,每条消息都会被推送到集群内所有注册过的客户端,保证消息至少被每台机器消费一次。与集群模式不同,集群模式下,同一个消费者组内的消息只会被消费一次,而广播模式下,每个消费者都会收到并消费相同的消息。

小威的博客 1万+

NewLife 的 RocketMQ 的生产者每次都是新实例将只由一个消费者消费

我写了一点逗比代码,让在每次使用 NewLife 的 RocketMQ 发送消息时,都创建一个新的 Producer 生产者。此时我发现了在我的消费者里面,无论开多少个消费者实例进程,每次都只有一个消费者进行消费 本文记录的问题,和 NewLife 的 RocketMQ 库的设计毫无相关,仅仅只是我的逗比问题。还请大家放心使用 NewLife 的 RocketMQ 库 我在对 NewLife...

lindexi 1858

【消息队列】RocketMQ 并发消费和顺序消费

在 RocketMQ 中,消息的消费模式包括并发消费和顺序消费,它们分别适用于不同的业务场景。

代码的魅力,根本走不出来 3064

RocketMQ-消息消费模式 顺序消费

微服务笔记

m0_62434717的博客 8380

RocketMQ消息消费

配置内容添加消息监听器@Component@Override// 执行本地事务逻辑,返回事务状态try {logger.info("消息消费开始:{}", bodyStr);logger.info("消息消费结束");logger.error("交易消息消费失败!", e);throw e;框架实现源码过滤的含义指的是将符合条件的消息投递给消费者,而不是将匹配到的消息过滤掉。

Mr_zly的博客 922

RocketMq 部分队列不能消费问题排查

欢迎移步到我个人博客网站阅读 RocketMq 部分队列不能消费问题排查

sharedCode源码交流 1万+

rocketmq-spring-boot-starter导致的多消费者实例重复消费问题

rocketM多消费者重复问题问题描述背景知识本地代码消息积压并重复消息原因分析 问题描述 在使用rocketMQ集群模式消费的时候(通过springboot整合的依赖操作),发现在一台服务器启动多个消费者实例后,消息进行了重复消费,但是在不同的服务器部署消费者实例后,消息能正常消费。 背景知识 rocketMQ的消费模式有两种: 负载均衡模式 消费者采用负载均衡方式消费消息,多个消费者(服务启动多个,本地多个springboot加载类启动)共同消费队列消息,每个消费者处理的消息不同 广播模式 消

qq_35008624的博客 4861

RocketMQ系列之顺序消费

前言 上节我们介绍了RMQ的两大亮点,重试和重复消费的问题,其实重试才能算亮点,重复消费最终还是要由我们自己来解决这个问题,RMQ自身并没有提供很好的机制,至少目前是没有,不知道将来会不会有,OK扯远了,今天呢,我们再来介绍RMQ一个不错的地方,那就是顺序消费,RMQ是可以保证同一个queue中的消息被顺序的消费。 RMQ实现如何实现顺序消费? 生产者Producer在生产消息时将需要顺序消费的消息发送到同一个queue下,每个topic默认是有4个queue所以Producer需要一个队列选择.

吭哧吭哧快乐的小牛码 2万+

RocketMQ一个新的消费组初次启动时从何处开始消费呢?

本文详细介绍一个新的消费组在启动时从何处开始消费,并信息介绍了CONSUME_FROM_MAX_OFFSET、CONSUME_FROM_FIRST_OFFSET、CONSUME_FROM_TIMESTAMP其计算逻辑,解答CONSUME_FROM_MAX_OFFSET在什么情况下不生效并如何解决。

中间件兴趣圈 1万+

【XR硬件介绍】PICO Space Pro(Project Swan)深度解析:字节 2026 旗舰 MR 头显延期背后

内部代号 Project Swan 的 PICO 年度旗舰 MR 头显正式定名PICO Space Pro,原本计划 2026 年 9 月对外发布,官方官宣推迟至 2026 年第四季度上市。硬件层面已经完成 FCC 认证,硬件成熟度较高,延期核心目标是打磨 PICO OS 6 空间操作系统,补齐空间计算软件能力。本文从产品背景、硬件架构、软件生态、延期逻辑、行业竞争格局几个维度拆解这款国产高端 MR 设备,分析它对国内 XR 产业的意义与待解的问题。

houdou112358的博客 1226

RocketMQ顺序消费

RocketMQ 支持两种类型的顺序消息消费:分区顺序消息(Partition Ordered Messages)和全局顺序消息(Global Ordered Messages)。这两种方式都能确保消息的顺序消费,但它们的工作机制有所不同。

Lin_Miao_09的博客 3403

卡尔曼滤波、互补滤波与Tsinghua滤波:平衡车姿态感知的三大算法对比

本文对比了平衡车姿态感知中的卡尔曼滤波、互补滤波与Tsinghua滤波三大核心算法。互补滤波计算简单,适合入门;卡尔曼滤波理论最优但实现复杂;Tsinghua滤波则是一种面向嵌入式优化的轻量级方案,通过动态权重调整和零偏估计,在资源受限的MCU上实现了性能与效率的平衡,是智能车竞赛等场景的实用选择。

weixin_29232121的博客 349

SWAT模型参数太多搞不定?一篇讲清如何用SWAT-CUP筛选关键敏感参数(含结果图解读)

本文详细解析了如何使用SWAT-CUP进行参数敏感性分析,帮助水文模型初学者从上百个参数中筛选出关键敏感参数。通过实际案例和结果图解读,介绍了敏感性分析的核心价值、SWAT-CUP结果界面解读方法以及参数筛选的四步实战法,显著提升模型调试效率。文章还提供了典型问题排查手册,解决敏感参数与文献不一致、率定后模型变差等常见问题。

weixin_30385925的博客 321

RocketMQ的顺序消费

上游系统通过消息队列,发送一个订单的状态操作信息,下游系统如何接受消息,并且需要严格保证订单状态的操作顺序性

maimai1993的博客 1万+

CMOS sensor 基础

NA

m0_72053187的博客 1658

rocketmq批量消费

rocketmq默认就是可以批量消费的,但需要设置多个参数一起配合。 我们只需要知道他是怎么消费的,就可以很精准的设置他的批量消费参数。 我们看看DefaultMQPushConsumer源码中的这几个参数: /** * 消费消息线程,最小数目 */ private int consumeThreadMin = 20; /** * 消费消息线程...

zhangsir的博客 1万+

三维重建笔记_光束平差法(Bundle Adjustment, BA)

目录 1 光束平差法(Bundle Adjustment, BA) 1.1 SFM (Structure‐from‐Motion) 定义 问题描述 1.2 重投影误差 (Reprojection Error) 目标函数 1.3 使用 Levenberg‐Marquardt Algorithm 进行优化 问题描述 简化目标函数 参考 Gauss‐Newton Method ...

惊鸿一博 2万+

手把手教你用SAM模型实现精准图像分割:从环境配置到实战应用

本文详细介绍了如何使用Meta推出的Segment Anything Model(SAM)实现精准图像分割,从环境配置到实战应用的全流程。特别针对电商场景中的商品分割需求,提供了硬件配置、模型下载、API使用及优化策略等实用指南,帮助开发者快速掌握这一强大的深度学习工具。

pepsi的博客 1062

面试官:RocketMQ同一个消费组内的消费者订阅了不同tag,会有问题吗?

面试官:同一个消费组内的消费者,如果订阅了相同的 topic,但是订阅的 tag 不一样,会有什么问题吗? 我:会出现丢消息的情况。 面试官:能详细说一说吗? 我:RocketMQ 要求同一个消费组内的消费者必须订阅关系一致,如果订阅关系不一致,会出现消息丢失的问题。 面试官:什么是订阅关系一致呢? 我:订阅关系一致是指同一个消费者组下所有消费者所订阅的 Topic、Tag 必须完全一致。如下图所示: 其中,消费组 1 中的消费组都订阅了 Topic1 中的 Tag1,消费组 2 中的消费组

zjj2006的专栏 9718

保姆级教程:用ROS2 Humble和Nav2从零搭建一个能自主导航的机器人小车(含SLAM建图与避障)

本文提供了一份详细的保姆级教程,指导如何使用ROS2 Humble和Nav2从零搭建一个具备自主导航功能的机器人小车,包括SLAM建图与避障。教程涵盖了硬件组装、ROS2环境配置、URDF模型创建、SLAM建图实战、Nav2导航栈配置以及系统集成调试等关键步骤,适合机器人开发初学者和进阶开发者。

weixin_30329623的博客 490

Kafka/RocketMQ 多线程消费时如何保证消费顺序?

之前在讨论顺序消息的一些知识,看到有个读者的留言如下: 这个问题问得非常棒,由于在之前的文章中并没有提及到,因此我在这篇文章中单独讲解,本文将从消费顺序性这个问题出发,深度剖析 Kafka/RocketMQ 消费线程模型。 Kafka kafka 的消费类 KafkaConsumer 是非线程安全的,因此用户无法在多线程中共享一个 KafkaConsumer 实例,且 KafkaConsumer 本身并没有实现多线程消费逻辑,如需多线程消费,还需要用户自行实现,在这里我会讲到 Kafka 两种多线程消费模

weixin_47082274的博客 4366

Vofa+使用笔记(1):用Vofa+通过串口连接显示波形

1. 介绍 Vofa+有三种协议设置 rawdata:你发什么就显示什么,跟普通的串口助手一样 firewater:最简单在VOFA+里绘制波形的协议,建议仅在通道数量不多、发送频率不高的时候使用。 justfloat:有一定的数据格式,此协议非常适合用在通道数量多、发送频率高的时候。 2. 使用步骤 设置号下面三个地方,使用的协议以及传输的方式 拉出控件 (最开始上位机没有识别到你的数据的时候,右边是没有数据显示的,如果你想要y轴显示什么数据,右边设置显示也可以,右键波形图然后Y轴设置也行) 把进

苏释州的博客 5万+

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

RocketMQ 消费者(1)概念和消费流程

1. 背景 RocketMQ 的消费可以算是 RocketMQ 的业务逻辑中最复杂的一块。这里面涉及到许多消费模式和特性。本想一篇文章写完,写到后面发现消费涉及到的内容太多,于是决定分多篇来写。本文作为消费系列的第一篇,主要讲述 RocketMQ 消费涉及到的模式和特性,也会概括性地讲一下消费流程。 我将 RocketMQ 的消费流程大致分成 4 个步骤 重平衡 消费者拉取消息 Broker 接收拉取请求后从存储中查询消息并返回 消费者消费消息 每个步骤都会用一篇文章来讲解。 先了解一下 RocketM

金甲虫的博客 7793

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

上一篇: 记一次曲折的拿靶标记录
下一篇: 大厂的门只进不出,来看我的Java面试资料大全!免费送
程序员职业指南
博客等级 码龄4年 2136粉丝 960原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值