zookeeper概念

ZooKeeper是什么?提供了什么

  • 简单的说,zookeeper=文件系统+通知机制
  • ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务
  • 它是一个为分布式应用提供一致性服务的软件
  • 提供的功能包括:配置维护、域名服务、分布式同步、组服务等

Zookeeper文件系统

  • ZooKeeper 的命名空间就是 ZooKeeper 应用的文件系统
  • 树状结构
  • 对于命名空间的操作必须都是绝对路径操作
  • 无文件与文件夹区分,统称为znode

zookeeper 的通知机制

  • 通知机制是观察者(watch)的模式,异步回调的触发机制
  • 客户端注册监听它关心的目录节点,当节点发生变化时(数据增删改、子目录节点增删改)时,zookeeper就会通知客户端
  • zookeeper所有读操作:getData(),getChildren()和exists()都有设置watch的选项,watch只会通知一次

ZAB协议?

  • ZAB协议是专门为zookeeper实现分布式协调功能而设计
  • zookeeper主要是根据ZAB协议是实现分布式系统数据一致性
  • zookeeper根据ZAB协议建立了主备模型完成zookeeper集群中数据的同步
  • ZAB的协议核心是在整个zookeeper集群中只有一个节点即Leader将客户端的写操作转化为事物(或提议proposal)
  • Leader节点再数据写完之后,将向所有的follower节点发送数据广播请求(或数据复制),等待所有的follower节点反馈
  • 在ZAB协议中,只要超过半数follower节点反馈OK,Leader节点就会向所有的follower服务器发送commit消息(即将leader节点上的数据同步到follower节点之上)
  • ZAB协议要求每个leader都要经历三个阶段,即发现,同步,广播
  1. 发现:即要求zookeeper集群必须选择出一个leader进程,同时leader会维护一个follower可用列表。将来客户端可以这follower中的节点进行通信
  2. 同步:leader要负责将本身的数据与follower完成同步,做到多副本存储。这样也是体现了CAP中高可用和分区容错。follower将队列中未处理完的请求消费完成后,写入本地事物日志中
  3. 广播:leader可以接受客户端新的proposal请求,将新的proposal请求广播给所有的follower
  • ZAB协议中主要有两种模式,第一是消息广播模式;第二是崩溃恢复模式

消息广播模式

  1. 在zookeeper集群中数据副本的传递策略就是采用消息广播模式。zookeeper中数据副本的同步方式与二阶段提交相似但是却又不同。二阶段提交的要求协调者必须等到所有的参与者全部反馈ACK确认消息后,再发送commit消息。要求所有的参与者要么全部成功要么全部失败。二阶段提交会产生严重阻塞问题。
  2. ZAB协议中Leader等待follower的ACK反馈是指”只要半数以上的follower成功反馈即可,不需要收到全部follower反馈”

崩溃恢复

  1. zookeeper集群中为保证任何所有进程能够有序的顺序执行,只能是leader服务器接受写请求,即使是follower服务器接受到客户端的请求,也会转发到leader服务器进行处理
  2. 如果leader服务器发生崩溃,则zab协议要求zookeeper集群进行崩溃恢复和leader服务器选举
  3. ZAB协议崩溃恢复要求满足如下2个要求
  • 确保已经被leader提交的proposal必须最终被所有的follower服务器提交
  • 确保丢弃已经被leader出的但是没有被提交的proposal

四种类型的数据节点 Znode

  • PERSISTENT-持久节点
    除非手动删除,否则节点一直存在于 Zookeeper 上
  • EPHEMERAL-临时节点
    临时节点的生命周期与客户端会话绑定,一旦客户端会话失效(客户端与zookeeper 连接断开不一定会话失效),那么这个客户端创建的所有临时节点都会被移除。
  • PERSISTENT_SEQUENTIAL-持久顺序节点
    基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。
  • EPHEMERAL_SEQUENTIAL-临时顺序节点
    基本特性同临时节点,增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。

ACL权限控制机制

  • 权限模式(Scheme)
  1. IP:从 IP 地址粒度进行权限控制
  2. Digest:最常用,用类似于 username:password 的权限标识来进行权限配 置,便于区分不同应用来进行权限控制
  3. World:最开放的权限控制方式,是一种特殊的 digest 模式,只有一个权限标 识“world:anyone”
  4. Super:超级用户
  • 授权对象
    授权对象指的是权限赋予的用户或一个指定实体,例如 IP 地址或是机器灯。
  • 权限 Permission
  1. CREATE:数据节点创建权限,允许授权对象在该 Znode 下创建子节点
  2. DELETE:子节点删除权限,允许授权对象删除该数据节点的子节点
  3. READ:数据节点的读取权限,允许授权对象访问该数据节点并读取其数据内 容或子节点列表等
  4. WRITE:数据节点更新权限,允许授权对象对该数据节点进行更新操作
  5. ADMIN:数据节点管理权限,允许授权对象对该数据节点进行 ACL 相关设置 操作

Chroot特性

  • 3.2.0 版本后,添加了 Chroot 特性
  • 该特性允许每个客户端为自己设置一个命名空间
  • 如果一个客户端设置了 Chroot,那么该客户端对服务器的任何操作,都将会被限制在其自己的命名空间下
  • 通过设置 Chroot,能够将一个客户端应用于 Zookeeper 服务端的一颗子树相对应,在那些多个应用公用一个 Zookeeper 进群的场景下,对实现不同应用间的相互隔离非常有帮助

会话管理

  • 会话的创建
  • 会话过期检查
  • 会话激活
  1. 会话的状态变化主要由client维护,server端保存的状态较少,一定程度上减少了server端的压力
  2. 分桶策略在这种大量client会话场景下显得非常有用,显著提升了会话超时的清理效率

服务器角色

  • Leader角色
  1. 事务请求的唯一调度者和处理者,保证集群事务处理的顺序性
  2. 集群内部各个服务器的调度者
  • Follower角色
  1. 处理客户端非事务性请求,转发事务请求给Leader服务器(事务请求都由Leader处理)
  2. 参与事务请求Proposal的投票
  3. 参与Leader选举投票
  • Observer角色
  1. Observer充当观察者角色,观察zk集群的最新状态变化并将这些状态同步过来
  2. 对于非事务请求可以进行独立的处理,对于事务请求,则会转发给Leader服务器进行处理
  3. Observer不会参与任何形式的投票,包括事务请求Proposal的投票和Leader选举的投票

Zookeeper 下 Server工作状态

  • LOOKING
  1. 寻找leader状态
  2. 当前服务器处于该状态时,它会认为当前集群中没有leader,因此需要进入leader选举状态。
  • FOLLOWING
  1. 跟随者状态
  2. 表示当前服务器的角色是Follower角色
  • LEADING
  1. 领导者状态
  2. 表示当前服务器是Leader
  • OBSERVING
  1. 观察者状态
  2. 表示当前服务器角色是Observer

Leader 选举

选择机制中的概念
serverId(服务器ID 既 myid)

  • 编号越大在选择算法中的权重越大

zxid(最新的事物ID 既 LastLoggedZxid)

  • 服务器中存放的最大数据ID
  • ID值越大说明数据越新,在选举算法中数据越新权重越大

epoch (逻辑时钟 既 PeerEpoch)

  • 每个服务器都会给自己投票,或者叫投票次数,同一轮投票过程中的逻辑时钟值是相同的
  • 每投完一次票这个数据就会增加,然后与接收到的其它服务器返回的投票信息中的数值相比
  • 如果收到低于当前轮次的投票结果,该投票无效,需更新到当前轮次和当前的投票结果

选举算法
electionAlg 属性指定(1-3)

  • 1 对应:LeaderElection 算法
  • 2 对应:AuthFastLeaderElection 算法
  • 3 对应:FastLeaderElection 默认的算法

投票内容

  • 选举人ID
  • 选举人数据ID
  • 选举人选举轮数
  • 选举人选举状态
  • 推举人ID
  • 推举人选举轮数

Leader选举

  • 服务器初始化启动的Leader选举
  • 服务器运行时期的Leader选举(服务器运行期间无法和Leader保持连接)

Leader选举的前提条件

  • 只有服务器状态在LOOKING(竞选状态)状态才会去执行选举算法
  • Zookeeper 的集群规模至少是2台机器,才可以选举Leader
  • 这里以3台机器集群为例,当一台服务器启动是不能选举的,等第二台服务器启动后,两台机器之间可以互相通信,才可以进行Leader选举
  • 服务器运行期间无法和Leader保持连接的时候

服务器启动时期的 Leader 选举

在集群初始化阶段,当有一台服务器Server1启动时,其单独无法进行和完成Leader选举,当第二台服务器Server2启动后,此时两台机器可以相互通信,每台机器都试图找到Leader,于是进入Leader选举过程。选举过程如下

  • 每个Server发出一个投票投给自己。由于是初始情况,Server1和Server2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举的服务器的myid和ZXID,使用(myid, ZXID)来表示,此时Server1的投票为(1, 0),Server2的投票为(2, 0),然后各自将这个投票发给集群中其他机器
  • 接受来自各个服务器的投票。集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自LOOKING状态的服务器
  • 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下
  1. 优先检查ZXID,ZXID比较大的服务器优先作为Leader
  2. 如果ZXID相同,那么就比较myid。myid较大的服务器作为Leader服务器
  3. 统计投票
  4. 改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING

服务器运行时期的 Leader 选举
在Zookeeper运行期间,即便当有非Leader服务器宕机或新加入,此时也不会影响Leader,但是一旦Leader服务器挂了,那么整个集群将暂停对外服务,进入新一轮Leader选举,其过程和启动时期的Leader选举过程基本一致

  • 变更状态。Leader挂后,余下的非Observer服务器都会将自己的服务器状态变更为LOOKING,然后开始进入Leader选举流程

以三台服务器为例

  • 每个Server会发出一个投票,在这个过程中,需要生成投票信息(myid,ZXID)每个服务器上的ZXID可能不同,我们假定Server1的ZXID为123,而Server3的ZXID为122;在第一轮投票中,Server1和Server3都会投自己,产生投票(1, 123),(3, 122),然后各自将投票发送给集群中所有机器
  • 接收来自各个服务器的投票。与启动时过程相同
  • 处理投票。与启动时过程相同,此时,Server1将会成为Leader
  • 统计投票。与启动时过程相同
  • 改变服务器的状态。与启动时过程相同

Leader选举核心算法FastLeaderElection

  • 自增选举轮次。Zookeeper规定所有有效的投票都必须在同一轮次中,在开始新一轮投票时,会首先对logicalclock进行自增操作
  • 初始化选票。在开始进行新一轮投票之前,每个服务器都会初始化自身的选票,并且在初始化阶段,每台服务器都会将自己推举为Leader
  • 发送初始化选票。完成选票的初始化后,服务器就会发起第一次投票。Zookeeper会将刚刚初始化好的选票放入sendqueue中,由发送器WorkerSender负责发送出去
  • 接收外部投票。每台服务器会不断地从recvqueue队列中获取外部选票。如果服务器发现无法获取到任何外部投票,那么就会立即确认自己是否和集群中其他服务器保持着有效的连接,如果没有连接,则马上建立连接,如果已经建立了连接,则再次发送自己当前的内部投票
  • 判断选举轮次。在发送完初始化选票之后,接着开始处理外部投票。在处理外部投票时,会根据选举轮次来进行不同的处理
  1. 外部投票的选举轮次大于内部投票。若服务器自身的选举轮次落后于该外部投票对应服务器的选举轮次,那么就会立即更新自己的选举轮次(logicalclock),并且清空所有已经收到的投票,然后使用初始化的投票来进行PK以确定是否变更内部投票。最终再将内部投票发送出去
  2. 外部投票的选举轮次小于内部投票。若服务器接收的外选票的选举轮次落后于自身的选举轮次,那么Zookeeper就会直接忽略该外部投票,不做任何处理,并返回步骤4
  3. 外部投票的选举轮次等于内部投票。此时可以开始进行选票PK
  • 选票PK。在进行选票PK时,符合任意一个条件就需要变更投票
  1. 若外部投票中推举的Leader服务器的选举轮次大于内部投票,那么需要变更投票
  2. 若选举轮次一致,那么就对比两者的ZXID,若外部投票的ZXID大,那么需要变更投票
  3. 若两者的ZXID一致,那么就对比两者的SID,若外部投票的SID大,那么就需要变更投票
  • 变更投票。经过PK后,若确定了外部投票优于内部投票,那么就变更投票,即使用外部投票的选票信息来覆盖内部投票,变更完成后,再次将这个变更后的内部投票发送出去
  • 选票归档。无论是否变更了投票,都会将刚刚收到的那份外部投票放入选票集合recvset中进行归档。recvset用于记录当前服务器在本轮次的Leader选举中收到的所有外部投票(按照服务队的SID区别,如{(1, vote1), (2, vote2)…})
  • 统计投票。完成选票归档后,就可以开始统计投票,统计投票是为了统计集群中是否已经有过半的服务器认可了当前的内部投票,如果确定已经有过半服务器认可了该投票,则终止投票。否则返回步骤4
  • 更新服务器状态。若已经确定可以终止投票,那么就开始更新服务器状态,服务器首选判断当前被过半服务器认可的投票所对应的Leader服务器是否是自己,若是自己,则将自己的服务器状态更新为LEADING,若不是,则根据具体情况来确定自己是FOLLOWING或是OBSERVING

步骤4-9会经过几轮循环,直到有Leader选举产生

数据同步

zk中所有数据都是放在内存中的,当然也有snapshot、事务日志

  • snapshot
    用于记录zk服务器上某一时刻的全量内存数据内容。snapshot.2c021384ce,后缀也是一个zxid,表示本次数据快照开始时刻的服务器最新zxid。没有预分配机制,所有内容都是有效的
  • 事务日志
    每次事务操作都会有一条记录保存在事务日志文件中,文件大小都是64M(包含填充,也就是预分配,主要为了提高事务日志的写入效率,不需要重新分配磁盘空间),日志后缀是一条zxid,也就是写入该事务文件的第一条事务记录的zxid

数据同步

  • 数据的同步都是由leader发起
  • 简单来说,learner启动时都会向leader建立连接,由leader分别对followe和observer进行数据同步
  • 有全量同步、仅回滚同步、先回滚再差异化同步、直接差异化同步四种同步指令
  • leader发送给learner的差异化数据同步指令(proposal),如果learner同意就会返回ack,如果leader收到ack,就会同时进入过半策略的等待阶段—leader会和其他learner服务器进行上述同样的数据同步流程,知道集群中有过半的learner机器响应了leader的这个ack消息
  • 一旦满足过半策略后,leader服务器就会向所有已经完成数据同步的learner机器发送一个uptodate指令,用来通知learner已经完成了数据同步,同时集群中已经有过半机器完成了数据同步,集群已经具备了对外服务的能力了
  • learner在接收到这个uptodate指令后,会终止数据同步流程,然后向leader再反馈一个ack消息

zookeeper是如何保证事务的顺序一致性的?

  • zookeeper 采用了递增的事务 Id 来标识
  • 所有的 proposal(提议)都在被提出的时候加上了 zxid, zxid 实际上是一个 64 位的数字
  • 高 32 位是 epoch 用来标识 leader 是否发生改变
  • 如果有新的 leader 产生出来,epoch 会自增,低 32 位用来递增计数
  • 当新产生 proposal 的时候,会依据数据库的两阶段过程,首先会向其他的 server 发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会开始执行

分布式集群中为什么会有Master?

  • 在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机
    器可以共享这个结果
    这样可以大大减少重复计算,提高性能

zk节点宕机如何处理?

以三台服务器为例

  • 如果是一个 Follower 宕机,还有 2 台服务器提供访问,因为 Zookeeper 上的数
    据是有多个副本的,数据并不会丢失
  • 如果是一个 Leader 宕机,Zookeeper 会选举出新的 Leader。
  • ZK 集群的机制是只要超过半数的节点正常,集群就能正常提供服务。只有在 ZK
    节点挂得太多,只剩一半或不到一半节点能工作,集群才失效。
  • 所以
    3 个节点的 cluster 可以挂掉 1 个节点(leader 可以得到 2 票>1.5)
    2 个节点的 cluster 就不能挂掉任何 1 个节点了(leader 可以得到 1 票<=1)

zookeeper负载均衡和nginx负载均衡区别

  • Nginx是著名的反向代理服务器,也被广泛的作为负载均衡服务器
  • ZooKeeper是分布式协调服务框架,有时也被用来做负载均衡
  • zk 的负载均衡是可以调控,nginx 只是能调权重,其他需要可控的都需要自己写插件
  • nginx 的吞吐量比 zk 大很多,应该说按业务选择用哪种方式。

Zookeeper有哪几种几种部署模式?

  • 单机模式
  • 伪集群模式
  • 集群模式

集群最少要几台机器,集群规则是怎样的?

  • 集群规则为 2N+1 台,N>0,即 3 台
  • 半数机制:集群中半数以上机器存活,集群可用

集群支持动态添加机器吗?

  • 全部重启:关闭所有 Zookeeper 服务,修改配置之后启动。不影响之前客户端的会话
  • 逐个重启:在过半存活即可用的原则下,一台机器重启不影响整个集群对外提供服务。这是比较常用的方式
  • 3.5 版本开始支持动态扩容。动态读取配置文件而不用重启全部ZK Server

Zookeeper对节点的watch监听通知为什么不是永久的?

  • 一个 Watch 事件是一个一次性的触发器,当被设置了 Watch的数据发生了改变的时候,则服务器将这个改变发送给设置了 Watch 的客户端,以便通知它们。
  • 举个例子,如果服务端变动频繁,而监听的客户端很多情况 下,每次变动都要通知到所有的客户端,给网络和服务器造成很大压力。
  • 一般是客户端执行 getData(“/节点 A”,true),如果节点 A 发生了变更或删除,客户端会得到它的 watch 事件
  • 但是在之后节点 A 又发生了变更,而客户端又没 有设置 watch 事件,就不再给客户端发送
  • 在实际应用中,很多情况下,我们的客户端不需要知道服务端的每一次变动,只要最新的数据即可

Zookeeper的java客户端都有哪些?

  • zk 自带的 zkclient
  • Apache 开源的 Curator

chubby是什么,和zookeeper比你怎么看?

  • chubby 是 google 的,完全实现 paxos 算法,不开源
  • zookeeper 是 chubby的开源实现,使用 zab 协议,paxos 算法的变种
  • ZAB协议并不是Paxos算法的一个典型实现

说几个zookeeper常用的命令

  • ls
  • ls2
  • get
  • set
  • create
  • delete
  • quit

ZAB和Paxos算法的联系与区别?

  • 两者都存在一个类似于Leader进程的角色,由其负责协调多个Follow进程的运行
  • Leader进程都会等待超过半数的Follower做出正确的反馈后,才会将一个提案进行提交
  • 在ZAB协议中,每个Proposal中都包含了一个epoch值,用来代表当前Leader周期,在Paxos算法中,同样存在这样一个标识,只是名字变成了Ballot
  • ZAB协议主要用于构建一个高可用的分布式数据主备系统
  • Paxos算法则是用于构建一个分布式的一致性状态机系统

在Paxos算法中,一个新选举产生的主进程会进行两个阶段的工作。

  • 第一阶段被称为读阶段,在这个阶段中,这个新的主进程会通过和所有其他进程进行通信的方式来收集上一个主进程的提案,并将他们提交。
  • 第二阶段被称为写阶段,在这个阶段,当前主进程开始提出他自己的提案。在Paxos算法设计的基础上,ZAB协议额外添加了一个同步阶段。
  • 在同步阶段之前,ZAB协议也存在一个和Paxos算法中的读阶段非常类似的过程,称为发现(Discovery)阶段。
  • 在同步阶段中,新的Leader会确保存在过半的Follower已经提交了之前Leader周期中的所有事务Proposal。
  • 这一同步阶段的引入,能够有效地保证Leader在新的周期中提出事务Proposal之前,所有的进程都已经完成了对之前所有事务Proposal的提交。
  • 一旦完成同步阶段后,那么ZAB就会执行和Paxos算法类似的写阶段

Zookeeper的典型应用场景

  • Dubbo
  • Hadoop
  • Kafka

Zookeeper分布式锁

  • 有了zookeeper的一致性文件系统,锁的问题变得容易。
  • 锁服务可以分为两类,一个是保持独占,另一个是控制时序。
  • 对于第一类,我们将zookeeper上的一个znode看作是一把锁,通过createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的distribute_lock 节点就释放出锁。
  • 对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便

Zookeeper集群管理

  • 所谓集群管理无在乎两点:是否有机器退出和加入、选举master。
  • 对于第一点,所有机器约定在父目录下创建临时目录节点,然后监听父目录节点的子节点变化消息。一旦有机器挂掉,该机器与 zookeeper的连接断开,其所创建的临时目录节点被删除,所有其他机器都收到通知:某个兄弟目录被删除,于是,所有人都知道:它上船了。新机器加入也是类似,所有机器收到通知:新兄弟目录加入,highcount又有了,
  • 对于第二点,我们稍微改变一下,所有机器创建临时顺序编号目录节点,每次选取编号最小的机器作为master就好

Zookeeper队列管理

两种类型的队列:

  • 同步队列,当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。
  • 队列按照 FIFO 方式进行入队和出队操作。
  • 第一类,在约定目录下创建临时目录节点,监听节点数目是否是我们要求的数目。
  • 第二类,和分布式锁服务中的控制时序场景基本原理一致,入列有编号,出列按编号。在特定的目录下创建PERSISTENT_SEQUENTIAL节点,创建成功时Watcher通知等待的队列,队列删除序列号最小的节点用以消费。此场景下Zookeeper的znode用于消息存储,znode存储的数据就是消息队列中的消息内容,SEQUENTIAL序列号就是消息的编号,按序取出即可。由于创建的节点是持久化的,所以不必担心队列消息的丢失问题

Zookeeper数据复制

  • Zookeeper作为一个集群提供一致的数据服务,自然,它要在所有机器间做数据复制。数据复制的好处:
  1. 容错:一个节点出错,不致于让整个系统停止工作,别的节点可以接管它的工作;
  2. 提高系统的扩展能力 :把负载分布到多个节点上,或者增加节点来提高系统的负载能力;
  3. 提高性能:让客户端本地访问就近的节点,提高用户访问速度。

从客户端读写访问的透明度来看,数据复制集群系统分下面两种:

  • 写主(WriteMaster) :对数据的修改提交给指定的节点。读无此限制,可以读取任何一个节点。这种情况下客户端需要对读与写进行区别,俗称读写分离;
  • 写任意(Write Any):对数据的修改可提交给任意的节点,跟读一样。这种情况下,客户端对集群节点的角色与变化透明。

对zookeeper来说,它采用的方式是写任意。通过增加机器,它的读吞吐能力和响应能力扩展性非常好,而写,随着机器的增多吞吐能力肯定下降(这也是它建立observer的原因),而响应能力则取决于具体实现方式,是延迟复制保持最终一致性,还是立即复制快速响应

zookeeper 都有哪些功能

  • 统一命名服务(naming)
  • 配置管理
  • 集群管理
  • 队列管理

客户端注册Watcher实现

服务端处理Watcher实现

客户端回调Watcher

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值