zookeeper集群+kafka集群

zookeeper集群+kafka集群:
kafka3.0之前依赖于zookeeper。
zookeeper开源,分布式的架构。提供协调服务(Apache项目)
基于观察者模式涉及的分布式服务管理架构。
存储和管理数据。分布式节点上的服务接受观察者的注册。一旦分布式节点上的数据发生变化,由zookeeper负责通知分布式节点上的服务。

zookeeper:

分为领导者和追随者 
leader follower组成的集群只要有一半以上的集群存活,zookeeper集群就可以正常工作。适用于安装奇数台的服务集群。
全局数据一致,每个zookeeper每个节点都保存相同的数据。维护监控服务的数据一致。数据更新的原子性。要么都成功,要么都失败。
实时性,只要有变化,立刻同步。

zookeeper的应用场景:

1、统一命名服务,在分布式的环境下,对所有的应用和服务进行统一命名。
2、统一配置管理,配置文件同步,kafka的配置文件被修改,可以快速同步到其他节点。3、统一集群管理,实时掌握所有节点的状态。
4、服务器动态上下线。
5、负载均衡,把访问的服务器的数据,发送到访问最少的服务器处理客户端的请求。

领导者和追随者:zookeeper的选举机制。

三台服务器:A B C
A先启动,发起第一次选举,投票投给自己,只有1票,不满半数,A的状态是looking
B启动,再发起一次选举,A和B分别投自己一票,交换选票信息,myid,
A发现B的myid比A大,A的这一票转而投给B
A 0 B 2 没有半数以上结果,A B会进入looking.B有可能成为leader。
C启动 MYID c的myid最大 A和B都会把票投给C 
A 0 B 0 C3  C的状态变为leader,A和B变成follower。
D myid 4 只要leader确定,后续的服务器都是追随者。

只有两种情况会开启选举机制:

1、初始化的情况会产生选举
2、服务器之间和leader丢失了连接状态。
leader已经存在,建立连接即可

不存在时
1、服务器ID大的胜出。
2、EPOCH大,直接胜出
3、EPOCH相同,事务ID大的胜出。
EPOCH是每个leader任期的代号,没有leader,大家的逻辑地址相同,每投完一次之后,数据是递增。
事务id,表示服务器的每一次变更,每变更一次事务iD变化一次。
服务器ID,zookeeper集群当中的机器都有一个ID,每台机器不重复,和myid保持一致。

zookeeper+kafka(2.7.0)实现过程:

1、zookeeper集群
 20.0.0.70  zookeeper+kafka

20.0.0.71  20.0.0.72

1.安装前准备
//关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
setenforce 0

//安装 JDK
yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel
java -version

//下载安装包
官方下载地址:https://archive.apache.org/dist/zookeeper/

cd /opt
wget https://archive.apache.org/dist/zookeeper/zookeeper-3.5.7/apache-zookeeper-3.5.7-bin.tar.gz
2.安装 Zookeeper
cd /opt
tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz
mv apache-zookeeper-3.5.7-bin /opt/zookeeper

//修改配置文件
cd /opt/zookeeper/conf/
cp zoo_sample.cfg zoo.cfg

vim zoo.cfg
tickTime=2000   #通信心跳时间,Zookeeper服务器与客户端心跳时间,单位毫秒
initLimit=10    #Leader和Follower初始连接时能容忍的最多心跳数(tickTime的数量),这里表示为10*2s
syncLimit=5     #Leader和Follower之间同步通信的超时时间,这里表示如果超过5*2s,Leader认为Follwer死掉,并从服务器列表中删除Follwer
dataDir=/opt/zookeeper/data      ●修改,指定保存Zookeeper中的数据的目录,目录需要单独创建
dataLogDir=/opt/zookeeper/logs   ●添加,指定存放日志的目录,目录需要单独创建
clientPort=2181   #客户端连接端口
#添加集群信息

server.1=20.0.0.70:3188:3288
server.2=20.0.0.71:3188:3288
server.3=20.0.0.72:3188:3288
 

-------------------------------------------------------------------------------------
server.A=B:C:D
●A是一个数字,表示这个是第几号服务器。集群模式下需要在zoo.cfg中dataDir指定的目录下创建一个文件myid,
这个文件里面有一个数据就是A的值,Zookeeper启动时读取此文件,
拿到里面的数据与zoo.cfg里面的配置信息比较从而判断到底是哪个server。

●B是这个服务器的地址。

●C是这个服务器Follower与集群中的Leader服务器交换信息的端口。

●D是万一集群中的Leader服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,
而这个端口就是用来执行选举时服务器相互通信的端口。
-------------------------------------------------------------------------------------

//拷贝配置好的 Zookeeper 配置文件到其他机器上
scp /opt/zookeeper/conf/zoo.cfg 192.168.233.20:/opt/zookeeper/conf/
scp /opt/zookeeper/conf/zoo.cfg 192.168.233.30:/opt/zookeeper/conf/

//在每个节点上创建数据目录和日志目录
mkdir /opt/zookeeper/data
mkdir /opt/zookeeper/logs

//在每个节点的dataDir指定的目录下创建一个 myid 的文件,不同节点分配1、2、3
70主机 echo 1 > /opt/zookeeper/data/myid
71        echo 2 > /opt/zookeeper/data/myid
72        echo 3 > /opt/zookeeper/data/myid

//配置 Zookeeper 启动脚本
vim /etc/init.d/zookeeper
!/bin/bash
#chkconfig:2345 20 90
#description:Zookeeper Service Control Script
ZK_HOME='/opt/zookeeper'
case $1 in
start)
    echo "---------- zookeeper 启动 ------------"
    $ZK_HOME/bin/zkServer.sh start
;;
stop)
    echo "---------- zookeeper 停止 ------------"
    $ZK_HOME/bin/zkServer.sh stop
;;
restart)
    echo "---------- zookeeper 重启 ------------"
    $ZK_HOME/bin/zkServer.sh restart
;;
status)
    echo "---------- zookeeper 状态 ------------"
    $ZK_HOME/bin/zkServer.sh status
;;
*)
    echo "Usage: $0 {start|stop|restart|status}"
esac

//    设置开机自启
chmod +x /etc/init.d/zookeeper
chkconfig --add zookeeper

//分别启动 Zookeeper
service zookeeper start

//查看当前状态
service zookeeper status

---------------- Kafka ----------------
Kafka 概述

为什么需要消息队列(MQ)

主要原因是由于在高并发环境下,同步请求来不及处理,请求往往会发生阻塞。比如大量的请求并发访问数据库,
导致行锁表锁,最后请求线程会堆积过多,从而触发 too many connection 错误,引发雪崩效应。

我们使用消息队列,通过异步处理请求,从而缓解系统的压力。消息队列常应用于异步处理,流量削峰,应用解耦,
消息通讯等场景。

当前比较常见的 MQ 中间件有 ActiveMQ、RabbitMQ、RocketMQ、Kafka 等。


使用消息队列的好处

(1)解耦
允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。

在软件系统中,解耦的概念与上述比喻类似。高度耦合的系统中,各个组件之间的关系非常紧密,
修改其中一个组件可能会影响其他组件。而低耦合的系统中,各个组件之间的关系更加独立,
修改一个组件不会波及其他组件。

解耦使得系统更容易维护、扩展和修改,因为它减少了组件之间的依赖性。

(2)可恢复性
系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,
加入队列中的消息仍然可以在系统恢复后被处理。

(3)缓冲
有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。

(4)灵活性 & 峰值处理能力
在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。
如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。
使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。

(5)异步通信
很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,
但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。


消息队列的两种模式

(1)点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)
消息生产者生产消息发送到消息队列中,然后消息消费者从消息队列中取出并且消费消息。消息被消费以后,
消息队列中不再有存储,所以消息消费者不可能消费到已经被消费的消息。消息队列支持存在多个消费者,
但是对一个消息而言,只会有一个消费者可以消费。

(2)发布/订阅模式(一对多,又叫观察者模式,消费者消费数据之后不会清除消息)
消息生产者(发布)将消息发布到 topic 中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,
发布到 topic 的消息会被所有订阅者消费。
发布/订阅模式是定义对象间一种一对多的依赖关系,使得每当一个对象(目标对象)的状态发生改变,
则所有依赖于它的对象(观察者对象)都会得到通知并自动更新。

RabbitMQ:
RabbitMQ支持点对点模型,其中消息生产者将消息发送到队列,而消息消费者从队列中接收和处理消息。

Apache Kafka:
Kafka的消息发布/订阅模型也可以被视为一种点对点通信模型,特别是在具有多个消费者时。
每个消费者组中的消费者将收到相同的消息,实现了一对多的发布/订阅和一对一的点对点模型。尺寸bnm/你00 

//Kafka 定义
Kafka 是一个分布式的基于发布/订阅模式的消息队列(MQ,Message Queue),主要应用于大数据实时处理领域。


Kafka 简介

Kafka 是最初由 Linkedin 公司开发,是一个分布式、支持分区的(partition)、多副本的(replica),基于 Zookeeper 协调的分布式消息中间件系统,它的最大的特性就是可以实时的处理大量数据以满足各种需求场景,比如基于 hadoop 的批处理系统、低延迟的实时系统,Spark/Flink 流式处理引擎,nginx 访问日志,消息服务等等,用 scala 语言编写,Linkedin 于 2010 年贡献给了 Apache 基金会并成为顶级开源项目。

Kafka 的特性

●高吞吐量、低延迟
Kafka 每秒可以处理几十万条消息,它的延迟最低只有几毫秒。
每个 topic 可以分多个 Partition,Consumer Group 对 Partition 进行消费操作,
提高负载均衡能力和消费能力。

●可扩展性
kafka 集群支持热扩展

●持久性、可靠性
消息被持久化到本地磁盘,并且支持数据备份防止数据丢失

●容错性
允许集群中节点失败(多副本情况下,若副本数量为 n,则允许 n-1 个节点失败)

●高并发
支持数千个客户端同时读写

名词

主题(Topic):Kafka数据流的基本单元是主题,它类似于一个数据流管道。生产者将数据发布到主题,
而消费者从主题中订阅数据。主题可以被分区,每个分区可以看作是主题的子数据流,具有自己的偏移量(Offset)。

生产者(Producer):生产者是将数据发布到Kafka主题的组件。它负责将消息写入主题,
生产者可以将消息发送到指定的主题和分区。

消费者(Consumer):消费者是从Kafka主题中读取数据的组件。它可以订阅一个或多个主题,
然后从主题的分区中读取消息。每个消费者都有一个唯一的消费者组ID,Kafka通过消费者组来实现负载均衡和容错性。

分区(Partition):每个主题可以分成多个分区,每个分区是数据的有序子集。分区允许Kafka进行水平扩展,
以处理大量数据。消息在分区中按照偏移量有序存储,消费者可以独立读取每个分区的数据。

偏移量(Offset):偏移量是每个消息在分区中的唯一标识,消费者使用偏移量来跟踪已读取的消息位置。
偏移量存储在Kafka中,消费者可以通过提交偏移量来记录已处理的消息。

经纪人(Broker):Kafka集群由多个经纪人组成,每个经纪人是一台运行Kafka服务的服务器。
经纪人存储主题分区中的数据,处理生产者和消费者的请求,以及维护元数据。

Kafka 通过 Zookeeper 来存储集群的meta信息。

由于consumer 在消费过程中可能会出现断电宕机等故障,consumer 恢复后,需要从故障前的位置的继续消费,
所以 consumer 需要实时记录自己消费到了哪个 offset,以便故障恢复后继续消费。

Kafka 0.9 版本之前,consumer 默认将 offset 保存在 Zookeeper 中;
从 0.9 版本开始,consumer 默认将 offset 保存在 Kafka 一个内置的 topic 中,
该 topic 为 __consumer_offsets。

也就是说,zookeeper的作用就是,生产者push数据到kafka集群,
就必须要找到kafka集群的节点在哪里,这些都是通过zookeeper去寻找的。
消费者消费哪一条数据,也需要zookeeper的支持,从zookeeper获得offset,
offset记录上一次消费的数据消费到哪里,这样就可以接着下一条数据进行消费。

在较早的Kafka版本中,ZooKeeper用于协调Kafka集群的配置和领导者选举。
但在较新的Kafka版本中,ZooKeeper的依赖性已经减少,未来版本可能会不再需要。


-----------------------------------------------------------------------------------------------------------

Kafka的工作流程:

生产者将消息发送到指定的主题,每个消息都附带一个可选的键(Key)和值(Value)。

主题可以有多个分区,生产者将消息写入一个分区。

经纪人(Broker)接收到消息后,将其存储在分区中,并分配一个唯一的偏移量。

在 Kafka 中,偏移量(Offset)是一个用于标识消费者在一个特定分区中已经读取到的消息位置的标记。
每个分区的每个消费者都会有一个偏移量,它表示下一条将要读取的消息的位置。
偏移量是一个64位的整数,表示在分区中消息的唯一位置。

Kafka 的消息是持久化的,一旦消息被写入分区,它就会保留一段时间,供消费者在需要时读取。
通过使用偏移量,消费者可以准确地指定它想要从哪个位置开始消费消息。

偏移量的重要性体现在以下几个方面:
消息的唯一标识: 每个消息都有一个在分区内的唯一偏移量,它可以被用作消息的全局唯一标识。

精确的消费位置: 消费者可以通过指定偏移量,精确地确定从哪个位置开始消费消息。
这是在重新启动消费者或者切换到不同的消费者时非常有用的功能。

消费者组的偏移量管理: 在消费者组中,Kafka 会追踪每个消费者的偏移量。
当新的消费者加入或离开消费者组时,Kafka 负责协调和管理这些消费者的偏移量。

至少一次语义: 消费者可以选择手动提交偏移量,确保消息至少被消费一次。
这是通过将偏移量与成功处理消息的应用程序逻辑关联来实现的。

 Kafka 中两种类型的偏移量:

消费者组偏移量(Group Offsets): 由 Kafka 内部管理,用于追踪消费者组中每个消费者的偏移量。

自定义偏移量(Commit Offsets): 由应用程序管理,消费者可以选择手动提交偏移量,以实现更细粒度的偏移量控制。

消费者订阅一个或多个主题,并从指定分区中读取消息。消费者使用偏移量来跟踪已读取的消息。

消费者可以以不同的速率读取消息,Kafka支持多个消费者组,每个组可以独立消费消息,从而实现负载均衡和水平扩展。

Kafka保留消息一定的时间,这个时间可以配置,一般情况下,已读取的消息会被保留一段时间,以便允许消费者重新读取。

Kafka的分布式特性、数据持久性、高吞吐量和水平扩展性使其成为处理大规模数据流的强大工具,可用于多种用途,
包括日志收集、实时数据分析、事件驱动架构等。

部署 kafka 集群

1.下载安装包
官方下载地址:http://kafka.apache.org/downloads.html

cd /opt
wget https://mirrors.tuna.tsinghua.edu.cn/apache/kafka/2.7.0/kafka_2.13-2.7.0.tgz

2.安装 Kafka
cd /opt/
tar zxvf kafka_2.13-2.7.0.tgz
mv kafka_2.13-2.7.0 kafka

3修改环境变量日志段是主题分区日志文件的一部分。

vim  /etc/profile

末尾添加

export KAFKA_HOME=/opt/kafka
export PATH=$PATH:$KAFKA_HOME/bin

退出

source /etc/profile

cd kafka/config 

cp server.properties server.properties.bak

vim server.properties

修改为broker.id=1    ●21行,broker的全局唯一编号,每个broker不能重复,因此要在其他机器上配置 71 broker.id=2、72  broker.id=3

listeners=PLAINTEXT://20.0.0.70:9092    ●31行,指定监听的IP和端口,如果修改每个broker的IP需区分开来,也可保持默认配置不用修改

num.network.threads=3    #42行,broker 处理网络请求的线程数量,一般情况下不需要去修改

num.io.threads=8         #45行,用来处理磁盘IO的线程数量,数值应该大于硬盘数

socket.send.buffer.bytes=102400       #48行,发送套接字的缓冲区大小

socket.receive.buffer.bytes=102400    #51行,接收套接字的缓冲区大小

socket.request.max.bytes=104857600    #54行,请求套接字的缓冲区大小

log.dirs=/var/log/kafka     修改为/var/log/kafka   #60行,kafka运行日志存放的路径,也是数据存放的路径

num.partitions=1    #65行,topic在当前broker上的默认分区个数,会被topic创建时的指定参数覆盖

num.recovery.threads.per.data.dir=1    #69行,用来恢复和清理data下数据的线程数量

log.retention.hours=168    #103行,segment文件(数据文件)保留的最长时间,单位为小时,默认为7天,超时将被删除

log.segment.bytes=1073741824    #110行,一个segment文件最大的大小,默认为 1G,超出将新建一个新的segment文件
#Kafka 以日志文件的形式维护其数据,而这些日志文件被分割成多个日志段。当一个日志段达到指定的大小时,就会创建一个新的日志段。

zookeeper.connect=20.0.0.70:2181,20.0.0.71:2181,20.0.0.72:2181
   ●123行,添加配置连接Zookeeper集群地址

退出

//配置 Zookeeper 启动脚本
vim /etc/init.d/kafka
#!/bin/bash
#chkconfig:2345 22 88
#description:Kafka Service Control Script
KAFKA_HOME='/opt/kafka'
case $1 in
start)
        echo "---------- Kafka 启动 ------------"
        ${KAFKA_HOME}/bin/kafka-server-start.sh -daemon ${KAFKA_HOME}/config/server.properties
;;
stop)
        echo "---------- Kafka 停止 ------------"
        ${KAFKA_HOME}/bin/kafka-server-stop.sh
;;
restart)
        $0 stop
        $0 start
;;
status)
        echo "---------- Kafka 状态 ------------"
        count=$(ps -ef | grep kafka | egrep -cv "grep|$$")
        if [ "$count" -eq 0 ];then
        echo "kafka is not running"
    else
        echo "kafka is running"
    fi
;;
*)
    echo "Usage: $0 {start|stop|restart|status}"
esac
 

//设置开机自启
chmod +x /etc/init.d/kafka
chkconfig --add kafka

//分别启动 Kafka
service kafka start


3.Kafka 命令行操作
//创建topic
kafka-topics.sh --create --zookeeper 20.0.0.70:2181,20.0.0.71:2181,20.0.0.72:2181 --replication-factor 2 --partitions 3 --topic test1

-------------------------------------------------------------------------------------
--bootstrap-server:定义 bootstrap-server 集群服务器地址,如果有多个 IP 地址使用逗号分割,一般使用一个 IP 即可
--replication-factor:定义分区副本数,1 代表单副本,建议为 2 
--partitions:定义分区数 
--topic:定义 topic 名称
-------------------------------------------------------------------------------------

//查看当前服务器中的所有 topic
kafka-topics.sh --list --zookeeper 20.0.0.70:2181,20.0.0.71:2181,20.0.0.72:2181

//查看某个 topic 的详情
[root@test1 efak]# kafka-topics.sh  --describe --zookeeper 20.0.0.70:2181,20.0.0.71:2181,20.0.0.72:2181
Topic: test3    PartitionCount: 3    ReplicationFactor: 2    Configs: 
    Topic: test3    Partition: 0    Leader: 3    Replicas: 3,1    Isr: 3,1
    Topic: test3    Partition: 1    Leader: 1    Replicas: 1,3    Isr: 1,3
    Topic: test3    Partition: 2    Leader: 3    Replicas: 3,1    Isr: 3,1
    
Partition:分区编号    

Leader:每个分区都有一个领导者(Leader),领导者负责处理分区的读写操作。
在上述输出中,领导者的编号分别为 3、1、3。

Replicas:每个分区可以有多个副本(Replicas),用于提供冗余和容错性。
在上述输出中,Replica 3、1、2 分别对应不同的 Kafka broker。

Isr:ISR(In-Sync Replicas)表示当前与领导者保持同步的副本。
ISR 3、1分别表示与领导者同步的副本。

先做地址映射:
vim /etc/hosts
20.0.0.70 test1

20.0.0.71 test2

20.0.0.72 test3
//发布消息
kafka-console-producer.sh --broker-list 20.0.0.70:9092,20.0.0.71:9092,20.0.0.72:9092  --topic test1

//消费消息
kafka-console-consumer.sh --bootstrap-server 20.0.0.70:9092,20.0.0.71:9092,20.0.0.72:9092 --topic test1 --from-beginning

-------------------------------------------------------------------------------------
--from-beginning:会把主题中以往所有的数据都读取出来
-------------------------------------------------------------------------------------

[root@test1 config]# kafka-topics.sh  --describe --zookeeper 192.168.233.10:2181,192.168.233.20:2181,192.168.233.30:2181
Topic: __consumer_offsets    PartitionCount: 50    ReplicationFactor: 1    Configs: compression.type=producer,cleanup.policy=compact,segment.bytes=104857600

Topic: test1    PartitionCount: 3    ReplicationFactor: 2    Configs: 
    Topic: test1    Partition: 0    Leader: 3    Replicas: 3,1    Isr: 3,1
    Topic: test1    Partition: 1    Leader: 1    Replicas: 1,2    Isr: 1,2
    Topic: test1    Partition: 2    Leader: 2    Replicas: 2,3    Isr: 2,3
Topic: test2    PartitionCount: 3    ReplicationFactor: 2    Configs: 
    Topic: test2    Partition: 0    Leader: 1    Replicas: 1,2    Isr: 1,2
    Topic: test2    Partition: 1    Leader: 2    Replicas: 2,3    Isr: 2,3
    Topic: test2    Partition: 2    Leader: 3    Replicas: 3,1    Isr: 3,1

__consumer_offsets 主题的作用是记录每个消费者组中每个消费者在每个分区上的偏移量。
这样,当消费者组中的消费者重新加入或者新的消费者加入时,它们可以从上次提交的偏移量处继续消费消息,
而不会重复消费或错过消息。

请注意,对于这个主题,配置为 Replication Factor 为 1 可能会对高可用性造成一些影响。
在生产环境中,通常会将 __consumer_offsets 主题的 Replication Factor 设置得更高,
以确保偏移量信息的可靠性。

//修改分区数
kafka-topics.sh --zookeeper 192.168.233.10:2181,192.168.233.20:2181,192.168.233.30:2181 --alter --topic test1 --partitions 6

//删除 topic
kafka-topics.sh --delete --zookeeper 20.0.0.70:2181,20.0.0.71:2181,20.0.0.72:2181 --topic test1

你提到的 "Note: This will have no impact if delete.topic.enable is not set to true." 
是关于删除 Kafka 主题的一个重要提示。默认情况下,Kafka 集群禁用了主题删除操作,为了确保不会意外删除数据。

在 Kafka 中,要执行主题删除操作,需要确保 delete.topic.enable 配置项被设置为 true。
这个配置项决定了是否允许删除主题。如果没有设置或设置为 false,即使你执行了删除主题的命令,
实际上也不会删除主题,而只是标记主题为 "marked for deletion"。

在生产环境中,特别谨慎地处理主题删除操作

#在配置文件中添加,将彻底删除topic.
delete.topic.enable=true

在zookeeper中查看topic信息:
/zkCli.sh -server 20.0.0.72:2181

ls /brokers/topics

  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值