Java八股文——分布式(下)

RPC的概念是什么?

面试官您好,RPC,全称是远程过程调用(Remote Procedure Call)。它的核心思想,就是让一个程序能够像调用本地方法一样,去调用另一台机器上另一个程序的方法,而完全不需要关心底层的网络通信细节。

RPC的目标,就是为开发者屏蔽掉所有复杂的网络编程,让我们能够以一种简单、透明的方式,来构建分布式系统。

1. RPC解决了什么问题?

在没有RPC的时代,如果服务A要调用服务B的功能,我们需要手动处理很多繁琐的事情:

  1. 需要知道服务B的IP地址和端口号。
  2. 需要和服务B约定好一个应用层协议(比如用JSON还是XML)。
  3. 需要自己处理网络连接、数据序列化、反序列化、网络异常等。

这个过程非常复杂、低效且容易出错。RPC框架的诞生,就是为了把所有这些脏活累活都封装起来,让分布式调用变得像本地调用一样简单。

2. 一个典型的RPC调用流程

假设我们有一个UserService,客户端想要调用它在远端的getUserById(123)方法。

  1. 客户端调用(像本地调用一样)

    • 开发者在客户端代码中,就像调用一个本地接口一样,直接写下:User user = userService.getUserById(123);
    • 这里的 userService 实际上不是真正的实现,而是一个由RPC框架生成的代理对象(Proxy),也叫存根(Stub)
  2. 代理层封装(客户端Stub)

    • 这个代理对象会“拦截”这次调用。它会将调用的接口名UserService)、方法名getUserById)、参数类型参数值123)等所有元信息,打包成一个标准的请求对象。
  3. 序列化与网络传输

    • 客户端的RPC框架会将这个请求对象,通过一个序列化协议(如Protobuf, JSON, Kryo)转换成二进制字节流
    • 然后,通过底层的网络库(如Netty),将这个字节流发送到服务端的指定地址。
  4. 服务端接收与反序列化(服务端Skeleton)

    • 服务端的RPC框架(通常称为骨架,Skeleton)接收到这个网络字节流。
    • 它使用相同的序列化协议,将字节流反序列化,还原成请求对象。
  5. 服务端执行与返回

    • 服务端框架根据请求对象中的接口名和方法名,通过反射或动态代理,找到真正的UserService实现类,并调用其getUserById(123)方法。
    • 得到业务方法的执行结果(比如一个User对象)后,服务端框架再将这个结果对象序列化成字节流,通过网络发送回客户端。
  6. 客户端接收与唤醒

    • 客户端的RPC框架接收到响应字节流,将其反序列化User对象。
    • 最后,唤醒之前被阻塞的业务线程,并将这个User对象作为返回值,赋给变量user

至此,一次完整的RPC调用结束。对于上层的业务开发者来说,他完全感觉不到这中间发生了序列化、网络传输、反序列化等一系列复杂的过程。

3. RPC框架的核心组成部分

一个成熟的RPC框架,通常包含以下几个核心模块:

  • 服务定义:用于定义跨网络调用的服务接口和数据结构(如gRPC的.proto文件)。
  • 客户端/服务端桩代码:自动生成的代理和骨架代码。
  • 序列化/反序列化层:负责对象和字节流之间的转换。
  • 网络传输层:基于TCP/HTTP等协议,负责高效的数据传输(Netty是Java领域的首选)。
  • 服务治理:这是一个高级RPC框架的标志,包括:
    • 服务注册与发现:服务提供者将自己注册到注册中心(如ZooKeeper, Nacos),消费者可以动态地发现服务地址。
    • 负载均衡:当一个服务有多个提供者时,客户端可以根据策略(如随机、轮询)选择一个进行调用。
    • 熔断、降级、限流:保证系统在极端压力下的稳定性。

4. 主流的RPC框架

  • gRPC:Google出品,基于HTTP/2,使用Protobuf作为默认序列化协议,性能极高,跨语言支持好,是云原生时代的首选。
  • Thrift:Facebook出品,历史悠久,支持多种协议和序列化格式,非常灵活。
  • Dubbo:阿里巴巴开源的、专为Java设计的高性能RPC框架,提供了非常完善的服务治理能力,在国内有极其广泛的应用。

总的来说,RPC是构建现代微服务架构的“神经系统”,它使得服务之间的通信变得高效、透明和可靠。


ZooKeeper拿来做什么?核心的原理是什么?

面试官您好,ZooKeeper是一个开源的、为分布式应用提供高性能协调服务的中间件。它的定位不是用来做数据存储,而是作为分布式系统中各个服务之间的一个 “协调员”“仲裁者”

它能解决的核心问题是,在复杂的分布式环境下,如何可靠地管理和同步那些共享的、关键的元数据(Metadata)

1. ZooKeeper能拿来做什么?(应用场景)

基于其强大的协调能力,ZooKeeper在分布式系统中有三大经典应用场景:

a. 分布式锁 (Distributed Lock)

  • 场景:在分布式环境下,多个服务实例需要竞争同一个共享资源。
  • ZK实现:利用ZooKeeper的有序临时节点特性。每个想获取锁的服务,都在一个约定的父节点下创建一个有序临时节点。然后,通过比较谁的节点序号最小来决定谁持有锁。其他服务则监视(Watch) 比自己序号小的前一个节点。当锁被释放(即节点被删除)时,下一个服务会被唤醒,从而实现一个公平、可靠的分布式锁。同时,因为是临时节点,如果持有锁的服务宕机,锁会自动释放,避免了死锁。

b. 服务注册与发现 (Service Registry & Discovery)

  • 场景:在微服务架构中,服务提供者的地址是动态变化的,消费者需要能动态地发现它们。
  • ZK实现
    • 注册:服务提供者在启动时,在ZK的一个约定目录下(如/services/order-service),创建一个代表自己服务实例的临时节点,节点中可以存储IP和端口信息。
    • 发现:服务消费者启动时,订阅这个目录,获取所有子节点列表,从而得到所有可用的服务提供者地址。
    • 健康检查:消费者可以监视这些子节点。如果某个服务提供者宕机,它对应的临时节点会自动消失,消费者会立即收到通知,并从其本地的服务列表中移除该实例。
    • Dubbo框架就曾广泛使用ZooKeeper作为其默认的注册中心。

c. 配置管理 (Configuration Management)

  • 场景:分布式系统中,大量的服务实例需要共享同一份配置信息,并希望在配置变更时能被实时通知。
  • ZK实现:将配置信息存储在ZK的某个数据节点(Znode)上。所有服务实例在启动时都去读取这个节点的数据作为自己的配置。同时,它们都监视这个节点。当运维人员修改了该节点的数据时,所有监视该节点的服务都会立即收到通知,然后重新拉取最新的配置,实现配置的动态更新。

2. ZooKeeper的核心原理是什么?

ZooKeeper之所以能提供如此可靠的协调服务,主要依赖于它独特的数据模型一致性协议

a. 数据模型:类似文件系统的树形结构

  • Znode:ZooKeeper的数据都存储在一个层次化的、类似文件系统的树形结构中。每个节点被称为一个Znode
  • Znode类型
    • 持久节点 (PERSISTENT):默认类型,节点一旦创建,除非被显式删除,否则一直存在。
    • 临时节点 (EPHEMERAL):节点的生命周期与创建它的客户端会话(Session)绑定。一旦客户端会话因超时或断开而失效,该节点会被自动删除。这是实现分布式锁和健康检查的关键。
    • 顺序节点 (SEQUENTIAL):在创建节点时,ZK会自动在其名称后追加一个单调递增的、全局唯一的序号。这是实现分布式锁和队列的关键。
  • Watch机制:客户端可以对Znode注册一个一次性的监视器(Watcher)。当该Znode的数据发生变化或其子节点列表发生变化时,客户端会收到一个通知。这个机制是实现服务发现、配置管理等实时通知功能的基础。

b. 一致性协议:ZAB协议 (ZooKeeper Atomic Broadcast)

这是ZooKeeper的灵魂,它保证了集群数据的一致性。ZAB协议是一种为ZooKeeper专门设计的、支持崩溃恢复原子广播协议

  • 角色:ZK集群采用主从(Leader-Follower) 架构。
    • Leader:负责处理所有写请求(事务)
    • Follower:可以处理读请求,同时作为Leader的候选者。
  • 原子广播 (写操作流程)
    1. 所有写请求必须先发给Leader。
    2. Leader将这个写操作,作为一个事务提议(Proposal),广播给所有Follower。
    3. Follower收到提议后,会先将其以日志形式写入本地磁盘,然后向Leader发送一个ACK
    4. 当Leader收到超过半数(Quorum)的Follower的ACK后,它就会向所有Follower发送一个COMMIT消息,并执行本地的写操作。
    5. Follower收到COMMIT后,也执行本地的写操作。
    • 这个过程类似于一个两阶段提交(2PC),但通过“过半数即提交”的机制,保证了数据最终的一致性和系统的可用性。
  • 崩溃恢复:当Leader宕机时,所有Follower会进入选举模式,通过内部算法(基于zxidmyid)选举出一个数据最新的Follower成为新的Leader。选举完成后,集群会进行数据同步,确保所有节点的数据与新Leader一致,然后才重新对外提供服务。

总结

  • 做什么:ZooKeeper主要用于实现分布式锁、服务注册与发现、配置管理等分布式协调任务。
  • 怎么做:它通过类似文件系统的树形数据模型(Znode)和强大的Watch机制,为上层应用提供了丰富的操作接口。
  • 如何保证可靠:其底层通过ZAB协议,以主从复制过半数提交的方式,保证了在分布式环境下数据的一致性和系统的容错能力。

总的来说,ZooKeeper是一个CP系统,它在任何时候都优先保证数据的一致性,是构建可靠分布式系统的关键基础设施。


常见的限流算法你知道哪些?

面试官您好,限流是构建高可用、高弹性系统的关键一环,它的核心目标是通过控制请求的速率,来保护下游服务不被瞬时的高并发流量冲垮。我了解到的常见限流算法主要有以下几种,它们各有优劣,适用于不同的场景。

1. 固定窗口/计数器算法 (Fixed Window Counter)

这是最简单、最直观的限流算法。

  • 核心思想:在一个固定的时间窗口内(比如1秒钟),维护一个计数器,记录这个窗口内接收到的请求数量。
  • 工作流程
    1. 当一个请求到来时,将计数器的值加一。
    2. 如果计数器的值小于或等于限流阈值(比如100 QPS),则允许请求通过。
    3. 如果计数器的值超过了阈值,则拒绝该请求。
    4. 当时间窗口结束时,将计数器清零
  • 优点:实现非常简单,容易理解。
  • 缺点:存在 “临界突发” 问题。
    • 举例:假设限流阈值是100 QPS。如果在第一个窗口的最后100毫秒,突然来了100个请求;然后在第二个窗口的最开始100毫秒,又来了100个请求。那么,在这短短的200毫秒内,系统实际承受了200个请求,这个瞬时流量是限流阈值的两倍,可能会对系统造成冲击。

2. 滑动窗口算法 (Sliding Window)

这是对固定窗口算法的改进,旨在解决“临界突发”问题。

  • 核心思想:将一个大的时间窗口(比如1分钟),再细分成多个更小的子窗口(比如分成60个1秒的子窗口)。然后,在一个滑动的时间范围内,统计请求总数。
  • 工作流程
    1. 假设我们将1分钟划分为60个子窗口,每个子窗口都有自己独立的计数器。
    2. 当一个请求到来时,当前子窗口的计数器加一。
    3. 然后,统计当前时间点向前推移的整个大窗口(过去1分钟)内,所有子窗口的请求总数。
    4. 如果总数超过了限流阈值,则拒绝请求。
    5. 随着时间的推移,这个大的统计窗口会平滑地向右滑动,旧的子窗口会被移出,新的子窗口被纳入。
  • 优点:通过更细粒度的控制,完美地解决了临界突发问题,限流更加平滑和精确。
  • 缺点:实现相对复杂,需要更多的内存来存储所有子窗口的计数。

3. 漏桶算法 (Leaky Bucket)

这个算法的思路发生了转变,它更关注于以一个恒定的速率处理请求

  • 核心思想:可以把系统想象成一个容量固定、底部有一个小洞的漏桶
  • 工作流程
    1. 所有的请求,无论来得多快多猛,都先被扔进这个“漏桶”里排队。
    2. 系统以一个恒定的、固定的速率,从桶的底部“漏出”(即处理)请求。
    3. 如果请求到来的速率,超过了漏出的速率,桶里的“水”(请求)就会越积越多。
    4. 如果桶被装满了,新来的请求就会被直接溢出(即拒绝)。
  • 优点
    • 流量整形(Traffic Shaping):它能强制性地将不规则的、突发的流量,“整形”成平滑的、匀速的流量,对下游系统的保护效果非常好。
  • 缺点
    • 无法应对突发流量:即使系统当前处于空闲状态,它也只能以那个固定的速率处理请求,无法利用空闲资源来快速处理突发的流量。对于那些需要快速响应的场景,这可能会增加请求的延迟。

4. 令牌桶算法 (Token Bucket)

这是目前应用最广泛、综合效果最好的限流算法。它既能限制平均速率,又能允许一定程度的突发流量。

  • 核心思想:可以把系统想象成一个容量固定的、装令牌的桶
  • 工作流程
    1. 系统以一个恒定的速率,不断地向这个桶里放入令牌(Token)
    2. 如果桶里的令牌已经满了,新生成的令牌就会被丢弃。
    3. 当一个请求到来时,它必须先尝试从桶里获取一个令牌
    4. 如果能拿到令牌,请求就被允许通过。
    5. 如果桶里没有令牌了,请求就会被拒绝或排队等待。
  • 优点
    • 兼顾平均速率和突发处理
      • 因为令牌是以恒定速率生成的,所以从长期来看,请求的平均速率被限制住了。
      • 当系统处于低谷时,桶里的令牌会逐渐积攒起来。当突发流量到来时,系统可以一次性地消耗掉这些积攒的令牌,来快速处理掉这波流量。这个“积攒的令牌数”,就代表了系统能应对的突发流量的上限
  • 应用:Google的Guava库中的RateLimiter就是基于令牌桶算法实现的。

总结对比

算法名称核心思想优点缺点
固定窗口固定时间窗口内计数实现简单有临界突发问题 (2倍流量)
滑动窗口细分窗口,平滑统计限流精确,解决了临界问题实现复杂,内存占用高
漏桶以固定速率流出流量整形,对下游保护效果好无法应对突发,增加延迟
令牌桶以固定速率生成令牌,请求消耗令牌能限制平均速率,又能应对一定突发实现相对复杂

在实际选型中,令牌桶算法因其出色的综合表现,成为了绝大多数场景下的首选方案。而滑动窗口算法,则在一些需要进行精确统计和分析的场景下(如API网关的用量统计)有很好的应用。


Raft协议和Paxos协议的原理?

面试官您好,Raft和Paxos都是分布式系统领域,用来解决一致性(Consensus) 问题的核心算法。它们的目标是相同的:确保在一个可能出现节点故障、网络延迟的分布式集群中,所有节点最终能对某个值或一系列操作的顺序达成完全一致的共识

尽管目标相同,但它们在设计哲学、可理解性和实现方式上存在巨大的差异。

1. Paxos 协议:共识算法的“鼻祖”

Paxos由计算机科学巨匠兰伯特提出,它是一种非常通用、理论性极强的共识算法。它的核心思想是 “少数服从多数”,并将一个复杂的共识问题,分解为一系列的提议(Proposal)和投票过程。

核心角色:

  • 提议者 (Proposer):提出一个值的提议。
  • 接受者 (Acceptor):对提议进行投票和决策。
  • 学习者 (Learner):被动地学习最终达成的共识结果。

Basic Paxos 的工作流程(两阶段提交):

  1. 准备阶段 (Prepare Phase)

    • 目标:提议者需要“抢占”一个提案的“所有权”,并了解历史情况。
    • 流程:提议者选择一个全局唯一的、单调递增的提案编号N,然后向所有接受者发送一个Prepare(N)请求。
    • 接受者收到请求后,会检查N是否比它之前响应过的所有Prepare请求的编号都大。如果是,它就向提议者承诺:“我以后不会再响应任何编号小于N的提议”,并把自己之前接受过的、编号最大的提案值返回给提议者。
  2. 接受阶段 (Accept Phase)

    • 目标:让大多数接受者就一个具体的值达成一致。
    • 流程:如果提议者收到了超过半数接受者的承诺响应,它就会从这些响应中,选择一个编号最大的提案值作为本次的提议值(如果响应中都没有值,就用自己的值)。然后,它向所有接受者发送一个Accept(N, Value)请求。
    • 接受者收到Accept请求后,只要这个请求的编号N不小于它之前承诺过的编号,它就会接受这个值。
  3. 达成共识:当一个提议值被超过半数的接受者接受后,这个值就达成了共识。

Paxos的特点

  • 优点:理论上非常强大和通用,是后续很多共识算法的基础。
  • 缺点极其难以理解和实现。兰伯特的论文非常晦涩,而且Basic Paxos只解决了对“单个值”达成共识的问题。要将它应用到实际系统中(如实现一个分布式日志),需要进行大量的扩展和工程实践(如Multi-Paxos),这个过程非常复杂且容易出错。

2. Raft 协议:为“可理解性”而生

Raft协议的诞生,就是为了解决Paxos“难以理解”的痛点。它的作者认为,一个算法如果不能被大多数工程师理解,就很难在工程上被正确地实现和应用。

因此,Raft的核心设计哲学就是 “可理解性(Understandability)”。它通过强Leader模型,将复杂的共识问题,分解为几个更简单、更独立的子问题。

核心子问题与机制:

a. Leader选举 (Leader Election)

  • 角色:Raft将节点明确划分为Leader、Follower、Candidate三种角色。
  • 机制:系统在任何时候,都必须有且仅有一个Leader。Leader负责处理所有客户端请求。其他节点都是Follower,被动地从Leader同步数据。
  • 选举流程
    • 如果Follower在一段时间内(选举超时)没有收到Leader的心跳,它就会转变为Candidate,并开启一个新的任期(Term)
    • 它会向所有其他节点发送请求投票的消息。
    • 如果一个Candidate获得了超过半数的选票,它就成为新一任的Leader。
    • 这个强Leader的设计,极大地简化了一致性管理的逻辑。

b. 日志复制 (Log Replication)

这是Raft实现状态机复制的核心。

  • 流程
    1. 所有客户端的写请求,都必须发给Leader
    2. Leader会将这个请求作为一个日志条目(Log Entry) 追加到自己的日志中。
    3. 然后,Leader会并行地将这个日志条目,通过AppendEntries RPC,复制给所有的Follower。
    4. 当Leader收到超过半数Follower的成功响应后,它就认为这条日志是 “已提交”(Committed) 的了。
    5. 一旦日志被提交,Leader就会将它应用到自己的状态机中,并可以向客户端返回成功。
    6. Leader也会在后续的心跳或AppendEntries RPC中,通知Follower哪些日志已经被提交,Follower收到通知后,也将这些日志应用到自己的状态机。

c. 安全性 (Safety)

Raft通过一系列的规则(如选举限制、提交规则等),严格地保证了系统的安全性,比如:

  • 一个任期最多只有一个Leader。
  • 拥有最新、最完整日志的节点,才有可能当选为Leader。
  • 一个已提交的日志条目,永远不会被覆盖。

对比总结

对比维度PaxosRaft
设计哲学理论通用性,解决最根本的共识问题工程可理解性,为实际系统构建而设计
核心模型对等节点,通过多轮提议和投票达成共识强Leader模型,Leader统一协调
问题分解分为Prepare和Accept两个阶段分为Leader选举、日志复制、安全性三个子问题
可理解性非常困难相对容易
实现复杂度极高中等
工业应用作为理论基础,但直接实现少非常广泛,如etcd, Consul, TiDB等

简单来说

  • Paxos 就像是在一群平等的、互相不信任的议员中,通过复杂的议会程序,艰难地就某项法案达成一致。
  • Raft 则是先选举出一个“总统”(Leader),然后由这位总统来主导所有决策,其他人(Follower)只需要听从和复制即可。这个过程显然更清晰、更有序。

正因为Raft的易于理解和实现,它已经成为当今构建新的分布式一致性系统的首选算法


有什么框架或技术用了Raft协议?

面试官您好,是的,Raft协议因为其出色的可理解性和相对容易实现的特性,在现代分布式系统中得到了极其广泛的应用。它已经成为构建需要强一致性的、高可用的分布式协调服务和存储系统的首选共识算法

以下是我了解到的、使用了Raft协议的一些著名框架和技术:

1. 分布式协调服务 & 配置中心

这是Raft协议最直接、最经典的应用场景,用于替代过去由ZooKeeper(基于ZAB协议)所主导的领域。

  • etcd

    • 简介:这是由CoreOS团队开发的一个高可用的、分布式的键值存储系统
    • Raft的应用etcd 的核心就是Raft协议。它通过Raft来保证集群中所有节点上存储的键值数据是强一致的。客户端的任何写操作,都必须通过Raft的日志复制和共识流程,被复制到大多数节点后,才能被确认。
    • 业界地位etcdKubernetes (K8s) 官方指定的、唯一的后端存储。K8s集群中所有的状态信息,如Pod的定义、Service的配置、节点的状态等等,都存储在etcd中。K8s的稳定性和一致性,完全依赖于etcd的可靠性,而etcd的可靠性,则源于Raft协议。
  • Consul

    • 简介:这是由HashiCorp公司推出的一个功能非常全面的服务网格(Service Mesh)解决方案,提供了服务发现、健康检查、配置管理和服务隔离等功能。
    • Raft的应用Consul 的Server节点之间,通过Raft协议组成一个高可用的集群。这个集群负责维护服务目录、配置信息、ACL策略等所有关键数据的一致性。当服务注册、健康状态变更或配置更新时,这些变更都会通过Raft协议在Server集群中达成共识。

2. 分布式数据库 & 存储系统

Raft协议也被广泛应用于新一代分布式数据库的底层,以实现数据的多副本一致性和自动故障转移。

  • TiDB

    • 简介:这是一个开源的、兼容MySQL协议的分布式HTAP(混合事务/分析处理)数据库
    • Raft的应用:TiDB的存储层是 TiKV。TiKV会将数据切分成许多个小的区域(Region),每个Region都是一个独立的Raft组(Raft Group),通常有3个副本。所有的写操作,都必须在一个Region的Raft组内部,通过Raft协议达成共识后才能成功。这保证了数据在多个副本间的强一致性。
  • CockroachDB

    • 简介:这是一款开源的、高可用的分布式SQL数据库,设计目标之一就是能在地理上分布,并能容忍数据中心级别的故障。
    • Raft的应用:与TiDB类似,CockroachDB也将数据划分为多个范围(Range),每个范围都是一个Raft组,通过Raft协议来保证数据的多副本一致性和自动故障转移。
  • InfluxDB Enterprise

    • 简介:这是一个领先的**开源时序数据库(TSDB)**的企业版。
    • Raft的应用:其集群版本的元数据管理,就是通过一个内嵌的Raft共识组来实现的,确保了集群拓扑、数据库和用户等元信息的一致性。

3. 分布式消息队列

  • RocketMQ (Dledger模式)
    • 简介:阿里巴巴开源的高性能消息队列。
    • Raft的应用:在4.5版本之后,RocketMQ引入了基于Raft协议的Dledger模式,作为一种可选的主从同步和Leader选举方案。当启用Dledger后,一个Broker组内的多个节点会形成一个Raft组。当Master节点宕机时,Dledger可以自动地、快速地从Slave中选举出新的Master,实现了自动故障转移,提升了MQ的可用性。

总结

领域框架/技术Raft的应用场景
分布式协调/配置etcd (K8s核心), Consul核心:保证集群元数据、配置、服务注册信息的一致性
分布式数据库TiDB, CockroachDB核心:保证数据分片(Region/Range)在多副本间的一致性
分布式消息队列RocketMQ (Dledger)高可用:实现Broker主从之间的自动Leader选举和故障转移

可以看到,Raft协议凭借其出色的可理解性和坚实的理论基础,已经成为构建新一代分布式系统的事实标准。从底层的键值存储,到上层的数据库和消息队列,都能看到它的身影。可以说,理解Raft协议,是理解现代分布式系统工作原理的一把钥匙。

参考小林 coding

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

xumistore

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

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

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

打赏作者

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

抵扣说明:

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

余额充值