浅谈Kafka消费者原理_kafka消费者组的作用,2024年最新大数据开发面试项目上的难点

import org.apache.kafka.clients.consumer.ConsumerRecord;

import java.util.Collections;
import java.util.Properties;

// 配置消费者属性
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, “localhost:9092”);
props.put(ConsumerConfig.GROUP_ID_CONFIG, “my-consumer-group”);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, “org.apache.kafka.common.serialization.StringDeserializer”);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, “org.apache.kafka.common.serialization.StringDeserializer”);
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, “earliest”);

// 创建 Kafka 消费者
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);

// 订阅主题
String topic = “test”;
consumer.subscribe(Collections.singletonList(topic));

// 消费消息
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
System.out.printf(“offset = %d, key = %s, value = %s%n”, record.offset(), record.key(), record.value());
}
// 异步提交偏移量
consumer.commitAsync();
}

// 关闭消费者
consumer.close();


在上述示例中,我们为消费者实例设置了相同的消费者组 ID(`my-consumer-group`),这样这些消费者实例就会组成一个消费者组。当您需要扩展消费能力时,只需增加具有相同消费者组 ID 的消费者实例即可。


### 2. 分区分配策略


#### 2.1 概述


在 Kafka 中,分区分配策略是指如何将主题的分区分配给消费者组中的消费者实例。分区分配策略的目标是实现负载均衡,从而提高 Kafka 集群的性能和可靠性。Kafka 提供了默认的分区分配策略,同时也允许用户自定义分区分配策略。


#### 2.2 分区分配策略的作用


分区分配策略的主要作用是实现负载均衡。通过将主题的分区分配给消费者组中的不同消费者实例,可以实现负载均衡。这样,每个消费者实例只需要处理一部分分区的消息,从而提高整体的消费性能。


#### 2.3 默认分区分配策略


Kafka 提供了两种默认的分区分配策略:Range 分配策略和 RoundRobin 分配策略。


Kafka消费者使用分区分配策略来确定将主题的哪些分区分配给组内的哪些消费者。Kafka提供了两种内置的分区分配策略:`RoundRobinAssignor`和`RangeAssignor`。此外,您还可以实现自定义的分区分配策略。


* `RoundRobinAssignor`:此策略将主题的分区依次分配给组内的消费者,实现负载均衡。例如,如果有3个消费者和9个分区,每个消费者将分配到3个分区。
* `RangeAssignor`:此策略将主题的连续分区分配给组内的消费者。例如,如果有3个消费者和9个分区,消费者1将分配到分区0-2,消费者2将分配到分区3-5,消费者3将分配到分区6-8。


#### 2.4 Range 分配策略


Range 分配策略是 Kafka 的默认分区分配策略。其原理如下:


1. 将消费者组中的消费者实例按照消费者 ID 排序。
2. 将订阅的主题按照主题名称排序。
3. 为每个消费者实例分配一个连续的分区范围,使得分区数量尽可能平均分配。


Range 分配策略适用于消费者组中的所有消费者实例订阅相同的主题列表。


#### 2.5 RoundRobin 分配策略


RoundRobin 分配策略是 Kafka 的另一种默认分区分配策略。其原理如下:


1. 将消费者组中的消费者实例按照消费者 ID 排序。
2. 将订阅的主题按照主题名称排序。
3. 按照 RoundRobin(轮询)方式为每个消费者实例分配一个分区,直到所有分区都被分配。


RoundRobin 分配策略适用于消费者组中的消费者实例订阅不同的主题列表。


#### 2.6 自定义分区分配策略


要自定义分区分配策略,您需要实现 Kafka 客户端提供的 `org.apache.kafka.clients.consumer.PartitionAssignor` 接口。以下是一个自定义分区分配策略的示例:



import org.apache.kafka.clients.consumer.PartitionAssignor;
import org.apache.kafka.common.Cluster;
import org.apache.kafka.common.TopicPartition;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class CustomPartitionAssignor implements PartitionAssignor {

@Override
public String name() {
    return "custom";
}

@Override
public Subscription subscription(Set<String> topics) {
    return new Subscription(topics);
}

@Override
public Map<String, Assignment> assign(Cluster metadata, GroupSubscription groupSubscription) {
    // 实现自定义的分区分配策略
}

@Override
public void onAssignment(Assignment assignment) {
    // 可选:处理分区分配结果
}

@Override
public List<RebalanceProtocol> supportedProtocols() {
    return Collections.singletonList(RebalanceProtocol.EAGER);
}

}


在上述示例中,我们实现了一个自定义的分区分配策略。要使用自定义分区分配策略,您需要在消费者配置中设置 `partition.assignment.strategy` 属性,如下所示:



import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.ConsumerRecord;

import java.util.Collections;
import java.util.Properties;

// 配置消费者属性
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, “localhost:9092”);
props.put(ConsumerConfig.GROUP_ID_CONFIG, “my-consumer-group”);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, “org.apache.kafka.common.serialization.StringDeserializer”);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, “org.apache.kafka.common.serialization.StringDeserializer”);
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, “earliest”);
props.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG, “com.example.CustomPartitionAssignor”);

// 创建 Kafka 消费者
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);

// 订阅主题
String topic = “test”;
consumer.subscribe(Collections.singletonList(topic));

// 消费消息
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
System.out.printf(“offset = %d, key = %s, value = %s%n”, record.offset(), record.key(), record.value());
}
// 异步提交偏移量
consumer.commitAsync();
}

// 关闭消费者
consumer.close();


### 3. 消费者配置参数


在 Kafka 中,消费者配置参数是指用于控制消费者行为的一组参数。这些参数包括连接设置、消费策略、性能调优等方面。通过设置不同的消费者配置参数,您可以根据实际需求调整消费者的行为,以实现更好的性能和可靠性。


Kafka消费者提供了许多配置参数,以便您根据实际需求调整消费者的行为。以下是一些重要的消费者配置参数:


#### 3.1 bootstrap.servers


`bootstrap.servers` 参数用于指定 Kafka 集群的地址。消费者会使用这个地址与 Kafka 集群建立连接。该参数的值可以是一个或多个以逗号分隔的 `host:port` 组合。


示例:



bootstrap.servers=localhost:9092


#### 3.2 group.id


`group.id` 参数用于指定消费者组的 ID。具有相同消费者组 ID 的消费者实例会组成一个消费者组,共享订阅的主题的分区。


示例:



group.id=my-consumer-group


#### 3.3 key.deserializer 和 value.deserializer


`key.deserializer` 和 `value.deserializer` 参数用于指定键和值的反序列化器。这些反序列化器用于将从 Kafka 集群接收到的字节数据转换为 Java 对象。


示例:



key.deserializer=org.apache.kafka.common.serialization.StringDeserializer
value.deserializer=org.apache.kafka.common.serialization.StringDeserializer


#### 3.4 auto.offset.reset


`auto.offset.reset` 参数用于指定消费者在没有初始偏移量或者当前偏移量不存在时的重置策略。可选的值有 `earliest`(从最早的偏移量开始消费)、`latest`(从最新的偏移量开始消费)和 `none`(抛出异常)。


示例:



auto.offset.reset=earliest


#### 3.5 enable.auto.commit


`enable.auto.commit` 参数用于指定是否自动提交消费者的偏移量。如果设置为 `true`,消费者会定期自动提交偏移量;如果设置为 `false`,您需要手动提交偏移量。默认值为 `true`。


示例:



enable.auto.commit=false


#### 3.6 auto.commit.interval.ms


`auto.commit.interval.ms` 参数用于指定自动提交偏移量的间隔(以毫秒为单位)。仅当 `enable.auto.commit` 参数设置为 `true` 时生效。默认值为 `5000`(5 秒)。


示例:



auto.commit.interval.ms=10000


#### 3.7 max.poll.records


`max.poll.records` 参数用于指定每次调用 `poll()` 方法时返回的最大记录数。通过调整该参数,您可以控制消费者的吞吐量和内存占用。默认值为 `500`。


示例:



max.poll.records=1000


#### 3.8 fetch.min.bytes


`fetch.min.bytes` 参数用于指定消费者从服务器获取数据的最小字节数。消费者会等待直到服务器返回的数据量达到该值。通过调整该参数,您可以控制消费者的吞吐量和延迟。默认值为 `1`。


示例:



fetch.min.bytes=1024


#### 3.9 fetch.max.wait.ms


`fetch.max.wait.ms` 参数用于指定消费者等待服务器返回数据的最长时间(以毫秒为单位)。如果在这个时间内服务器没有返回足够的数据(即达到 `fetch.min.bytes`),消费者将立即获取可用的数据。通过调整该参数,您可以控制消费者的吞吐量和延迟。默认值为 `500`。


示例:



fetch.max.wait.ms=1000


#### 3.10 session.timeout.ms


`session.timeout.ms` 参数用于指定消费者与 Kafka 集群之间的会话超时时间(以毫秒为单位)。如果在这个时间内消费者没有发送心跳到 Kafka 集群,那么 Kafka 集群会认为该消费者已经故障,然后触发分区再平衡。通过调整该参数,您可以控制消费者的故障检测敏感度。默认值为 `10000`(10 秒)。


示例:



session.timeout.ms=15000


#### 3.11 heartbeat.interval.ms


`heartbeat.interval.ms` 参数用于指定消费者发送心跳到 Kafka 集群的间隔时间(以毫秒为单位)。心跳用于维持消费者与 Kafka 集群之间的连接,并通知 Kafka 集群消费者的存活状态。通常情况下,该参数的值应该小于 `session.timeout.ms` 参数的值。默认值为 `3000`(3 秒)。


示例:



heartbeat.interval.ms=5000


#### 3.12 max.partition.fetch.bytes


`max.partition.fetch.bytes` 参数用于指定消费者每次从单个分区获取的最大字节数。通过调整该参数,您可以控制消费者的吞吐量和内存占用。默认值为 `1048576`(1 MB)。


示例:



max.partition.fetch.bytes=2097152


#### 3.13 如何在实际应用中设置消费者配置参数


要在实际应用中设置消费者配置参数,您需要创建一个 `Properties` 对象,并为其设置相应的参数。以下是一个使用 Java 客户端的消费者示例:



import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.ConsumerRecord;

import java.util.Collections;
import java.util.Properties;

// 配置消费者属性
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, “localhost:9092”);
props.put(ConsumerConfig.GROUP_ID_CONFIG, “my-consumer-group”);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, “org.apache.kafka.common.serialization.StringDeserializer”);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, “org.apache.kafka.common.serialization.StringDeserializer”);
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, “earliest”);
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, “false”);
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, “1000”);
props.put(ConsumerConfig.FETCH_MIN_BYTES_CONFIG, “1024”);
props.put(ConsumerConfig.FETCH_MAX_WAIT_MS_CONFIG, “1000”);
props.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, “15000”);
props.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, “5000”);
props.put(ConsumerConfig.MAX_PARTITION_FETCH_BYTES_CONFIG, “2097152”);

// 创建 Kafka 消费者
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);

// 订阅主题
String topic = “test”;
consumer.subscribe(Collections.singletonList(topic));

// 消费消息
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
System.out.printf(“offset = %d, key = %s, value = %s%n”, record.offset(), record.key(), record.value());
}
// 异步提交偏移量
consumer.commitAsync();
}

// 关闭消费者
consumer.close();


### 4. 消费者位移管理


#### 4.1 概述


Kafka消费者使用位移(offset)来跟踪每个分区中已经消费的消息。位移是一个递增的整数,表示消费者在分区中的位置。当消费者读取一个消息时,它会将位移更新为下一个消息的位置。消费者可以将位移存储在Kafka的内部主题`__consumer_offsets`中,或者在外部存储系统中。


#### 4.2 位移的概念


位移(Offset)是一个递增的整数,表示消费者在分区中已经消费的消息的位置。位移从 0 开始,每消费一个消息,位移加 1。消费者可以通过位移来控制消息的消费顺序和进度。


在 Kafka 中,位移以主题和分区为单位进行存储。每个消费者组都有一个独立的位移,用于记录消费者组中的消费者实例在各个分区的消费进度。


#### 4.3 位移提交方式


位移提交是指将消费者的位移更新到 Kafka 集群。位移提交有两种方式:自动提交和手动提交。


消费者可以使用以下三种位移提交策略来管理位移:


* 自动提交:消费者会定期自动提交位移。您可以通过设置`enable.auto.commit`和`auto.commit.interval.ms`配置选项来启用自动提交并指定提交间隔。
* 同步提交:消费者可以在处理完一批消息后显式提交位移。这可以通过调用`commitSync()`方法实现。同步提交会阻塞调用线程,直到位移提交成功或发生错误。
* 异步提交:消费者可以在处理完一批消息后异步提交位移。这可以通过调用`commitAsync()`方法实现。异步提交不会阻塞调用线程,但可能在提交失败时丢失位移。


##### 4.3.1 自动提交


自动提交是指消费者定期自动将位移提交到 Kafka 集群。要启用自动提交,您需要将消费者配置参数 `enable.auto.commit` 设置为 `true`(默认值),并设置 `auto.commit.interval.ms` 参数以指定自动提交的间隔。


示例:



enable.auto.commit=true
auto.commit.interval.ms=5000


自动提交的优点是简单易用,但缺点是可能导致消息的重复消费。因为在自动提交的间隔内,消费者可能已经消费了一些消息,但这些消息的位移尚未提交。如果此时消费者发生故障,那么在恢复后,消费者会从上次提交的位移开始消费,导致这些消息被重复消费。


##### 4.3.2 手动提交


手动提交是指消费者在消费消息后显式地将位移提交到 Kafka 集群。要启用手动提交,您需要将消费者配置参数 `enable.auto.commit` 设置为 `false`。


示例:



enable.auto.commit=false


手动提交可以通过 `KafkaConsumer` 类的 `commitSync()` 和 `commitAsync()` 方法实现。`commitSync()` 方法是同步提交,会阻塞当前线程直到位移提交成功或发生错误;`commitAsync()` 方法是异步提交,不会阻塞当前线程,但需要提供一个回调函数以处理提交结果。


手动提交的优点是可以准确地控制位移的提交时机,从而减少消息的重复消费。但缺点是需要编写更多的代码来处理位移提交的逻辑和错误。


#### 4.4 如何在实际应用中管理位移


以下是一个使用 Java 客户端的消费者示例,演示了如何在实际应用中管理位移:



import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.ConsumerRecord;

import java.util.Collections;
import java.util.Properties;

// 配置消费者属性
Properties props = new Properties();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, “localhost:9092”);
props.put(ConsumerConfig.GROUP_ID_CONFIG, “my-consumer-group”);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, “org.apache.kafka.common.serialization.StringDeserializer”);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, “org.apache.kafka.common.serialization.StringDeserializer”);
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, “earliest”);
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, “false”);

// 创建 Kafka 消费者
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);

// 订阅主题
String topic = “test”;
consumer.subscribe(Collections.singletonList(topic));

// 消费消息
while (true) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
for (ConsumerRecord<String, String> record : records) {
System.out.printf(“offset = %d, key = %s, value = %s%n”, record.offset(), record.key(), record.value());
}
// 异步提交偏移量
consumer.commitAsync((offsets, exception) -> {
if (exception != null) {
System.err.println("Commit failed for offsets: " + exception.getMessage());
} else {
System.out.println("Offsets committed: " + offsets);
}
});
}

// 关闭消费者
consumer.close();


在上述示例中,我们为消费者实例设置了手动提交位移的配置参数,并在消费消息后使用 `commitAsync()` 方法异步提交位移。您可以根据实际需求选择自动提交或手动提交的方式来管理消费者的位移。


### 5. 拉取和批处理


#### 5.1 概述


Kafka消费者使用拉取(pull)模型从分区中获取消息。拉取模式允许消费者根据自身的处理能力来控制消息的获取速度,从而实现负载均衡和流量控制。消费者会定期向分区的leader副本发送拉取请求,请求包含位移和最大拉取字节数。leader副本会将位移之后的消息发送给消费者,直到达到最大拉取字节数。


为了提高效率,Kafka消费者会将拉取到的消息存储在内部缓冲区中,并在需要时批量处理。批处理是指消费者一次拉取多个消息,以提高消费性能。通过调整拉取和批处理参数,您可以根据实际需求优化消费者的性能和资源占用。  
 您可以通过设置`fetch.min.bytes`和`fetch.max.wait.ms`配置选项来控制拉取操作的行为。`fetch.min.bytes`表示消费者在发送拉取请求之前需要等待的最小数据量,`fetch.max.wait.ms`表示消费者在发送拉取请求之前需要等待的最长时间。


#### 5.2 拉取模式


拉取模式是指消费者主动从 Kafka 集群获取消息。与推送(Push)模式相比,拉取模式具有以下优点:


* 负载均衡:消费者可以根据自身的处理能力来控制消息的获取速度,从而避免因为处理速度不匹配而导致的资源浪费或拥塞。
* 流量控制:消费者可以根据网络状况和资源占用来调整拉取速度,从而实现流量控制。
* 容错性:消费者可以在发生故障时重新拉取消息,从而实现容错。


在 Kafka 中,消费者通过 `KafkaConsumer` 类的 `poll()` 方法来拉取消息。`poll()` 方法会从订阅的主题的分区中获取一批消息,并返回给调用者。您可以通过调整拉取参数来控制拉取的行为,如拉取间隔、拉取数量等。


#### 5.3 批处理原理


批处理是指消费者一次拉取多个消息,以提高消费性能。批处理的原理是将多个消息组合成一个批次,然后一次性发送给消费者。这样可以减少网络传输的开销,提高消费者的吞吐量。


在 Kafka 中,批处理是通过以下两个参数来控制的:


* `fetch.min.bytes`:消费者从服务器获取数据的最小字节数。消费者会等待直到服务器返回的数据量达到该值。通过调整该参数,您可以控制消费者的吞吐量和延迟。
* `fetch.max.wait.ms`:消费者等待服务器返回数据的最长时间(以毫秒为单位)。如果在这个时间内服务器没有返回足够的数据(即达到 `fetch.min.bytes`),消费者将立即获取可用的数据。通过调整该参数,您可以控制消费者的吞吐量和延迟。


#### 5.4 如何在实际应用中调整拉取和批处理参数


要在实际应用中调整拉取和批处理参数,您需要创建一个 `Properties` 对象,并为其设置相应的参数。以下是一个使用 Java 客户端的消费者示例:



import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.ConsumerRecords;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数大数据工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上大数据开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注大数据获取)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

因此收集整理了一份《2024年大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
[外链图片转存中…(img-g8ZExW6K-1713036394745)]
[外链图片转存中…(img-4lzVoqn0-1713036394746)]
[外链图片转存中…(img-N5HeV9iK-1713036394746)]
[外链图片转存中…(img-EPG3PtXj-1713036394748)]
[外链图片转存中…(img-iuk6DiCd-1713036394750)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上大数据开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注大数据获取)
[外链图片转存中…(img-DdJ6qTgx-1713036394751)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值