Redis的高可用:哨兵和集群

在Redis中,缓存的高可用分两种,一种是哨兵,另外一种是集群,下面我们会用两节分别讨论它们。不过在讨论它们之前,需要引入对Redis的依赖,如代码清单16-1所示。

代码清单16-1 引入spring-boot-redis依赖(chapter16模块)

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
   <exclusions>
      <!--不依赖Redis的异步客户端lettuce-->
      <exclusion>
         <groupId>io.lettuce</groupId>
         <artifactId>lettuce-core</artifactId>
      </exclusion>
   </exclusions>
</dependency>
<!--引入Redis的客户端驱动jedis-->
<dependency>
   <groupId>redis.clients</groupId>
   <artifactId>jedis</artifactId>
</dependency>

这里引入了Redis的依赖,并且选用Jedis作为客户端,没有使用Lettuce。这里解释一下不使用Lettuce的原因。Lettuce是一个可伸缩的线程安全的Redis客户端,多个线程可以共享同一个Redis连接,因为线程安全,所以会牺牲一部分的性能。但是一般来说,使用缓存并不需要很高的线程安全,更注重的是性能。Jedis是一种多线程非安全的客户端,具备更高的性能,所以企业选择的时候往往还是以使用它为主。

16.1.1 哨兵模式

在Redis的服务中,可以有多台服务器,还可以配置主从服务器,通过配置使得从机能够从主机同步数据。在这种配置下,当主Redis服务器出现故障时,只需要执行故障切换(failover)即可,也就是作废当前出故障的主Redis服务器,将从Redis服务器切换为主Redis服务器即可。这个过程可以由人工完成,也可以由程序完成,如果由人工完成,则需要增加人力成本,且容易产生人工错误,还会造成一段时间的程序不可用,所以一般来说,我们会选择使用程序完成。这个程序就是我们所说的哨兵(sentinel),哨兵是一个程序进程,它运行于系统中,通过发送命令去检测各个Redis服务器(包括主从Redis服务器),如图16-1所示。

 

图16-1 单个哨兵模式

图16-1中有2个Redis从服务器,它们会通过复制Redis主服务器的数据来完成同步。此外还有一个哨兵进程,它会通过发送命令来监测各个Redis主从服务器是否可用。当主服务器出现故障不可用时,哨兵监测到这个故障后,就会启动故障切换机制,作废当前故障的主Redis服务器,将其中的一台Redis从服务器修改为主服务器,然后将这个消息发给各个从服务器,使得它们也能做出对应的修改,这样就可以保证系统继续正常工作了。通过这段论述大家可以看出,哨兵进程实际就是代替人工,保证Redis的高可用,使得系统更加健壮。

然而有时候单个哨兵也可能不太可靠,因为哨兵本身也可能出现故障,所以Redis还提供了多哨兵模式。多哨兵模式可以有效地防止单哨兵不可用的情况,如图16-2所示。

 

图16-2 多哨兵模式

在图16-2中,多个哨兵会相互监控,使得哨兵模式更为健壮,在这个机制中,即使某个哨兵出现故障不可用,其他哨兵也会监测整个Redis主从服务器,使得服务依旧可用。不过,故障切换方式和单哨兵模式的完全不同,这里我们通过假设举例进行说明。假设Redis主服务器不可用,哨兵1首先监测到了这个情况,这个时候哨兵1不会立即进行故障切换,而是仅仅自己认为主服务器不可用而已,这个过程被称为主观下线。因为Redis主服务器不可用,跟着后续的哨兵(如哨兵2和3)也会监测到这个情况,所以它们也会做主观下线的操作。如果哨兵的主观下线达到了一定的数量,各个哨兵就会发起一次投票,选举出新的Redis主服务器,然后将原来故障的主服务器作废,将新的主服务器的信息发送给各个从Redis服务器做调整,这个时候就能顺利地切换到可用的Redis服务器,保证系统持续可用了,这个过程被称为客观下线

为了演示这个过程,我先给出自己的哨兵和Redis服务器的情况,如表16-1所示。

表16-1 服务分配情况

服务进程类型

是否Redis主服务器

IP地址

服务端口

Redis

192.168.224.131

6397

Redis

192.168.224.133

6397

Redis

192.168.224.134

6397

Sentinel

192.168.224.131

26379

Sentinel

192.168.224.133

26379

Sentinel

192.168.224.134

26379

这样设计的架构,就如同图16-2一样,下面我们需要对各个服务进行配置。首先修改Redis主服务器配置(192.168.224.131)的内容,在Redis安装目录中找到redis.config文件,打开它,可以发现有很多配置项和注释。只需要对某些配置项进行修改即可,需要修改的配置项代码如下:

# 禁用保护模式
protected-mode no
# 修改可以访问的IP,0.0.0.0代表可以跨域访问
bind 0.0.0.0
# 设置Redis服务密码
requirepass 123456

然后再修改两台从服务器的配置,请注意,它们俩的配置是相同的。在Redis安装目录中找到redis.config文件,然后也是对相关的配置项进行修改,代码如下:

# 禁用保护模式
protected-mode no
# 修改可以访问的IP,0.0.0.0代表可以跨域访问
bind 0.0.0.0
# 设置Redis服务密码
requirepass 123456
# 配置从哪里复制数据(也就是配置主Redis服务器) 
replicaof 192.168.224.131 6379
# 配置主Redis服务器密码 
masterauth 123456

以上的配置都有清晰的注释,请自行参考。从服务器的配置只是比主服务器多了replicaof和masterauth两个配置项。

上述的两个配置只是在配置Redis的服务器,此外我们还需要配置哨兵。同样,在Redis安装目录下,找到sentinel.conf文件,然后把3个哨兵服务的配置都改成以下配置。

# 禁止保护模式
protected-mode no

# 配置监听的主服务器,这里sentinel monitor 代表监控,
# mymaster 代表服务器名称,可以自定义
# 192.168.224.131 代表监控的主服务器
# 6379代表端口
# 2 代表只有在2个或者2个以上的哨兵认为主服务器不可用的时候,才进行客观下线
sentinel monitor mymaster 192.168.224.131 6379 2

# sentinel auth-pass定义服务的密码
# mymaster 服务名称
# 123456 Redis服务器密码
sentinel auth-pass mymaster 123456

上述的配置只是在原有的其他配置项上按需进行修改。代码中已经给出了清晰的注释,请读者自行参考。

有了这些配置,我们就可以进入Redis的安装目录,使用下面的命令启动服务了。

# 启动Redis服务
./src/redis-server ./redis.conf

# 启动哨兵进程服务
./src/redis-sentinel ./sentinel.conf

需要注意的是启动的顺序,首先是主Redis服务器,然后是从Redis服务器,最后才是3个哨兵。启动之后,观察最后一个启动的哨兵,可以看到图16-3所示的信息。

 

 

图16-3 哨兵进程输出信息

从图16-3中可以看到主从服务器和哨兵的相关信息,说明我们的多哨兵模式已经搭建好了。

上述的哨兵模式配置好后,就可以在Spring Boot环境中使用了。首先需要配置YAML文件,如代码清单16-2所示。

代码清单16-2 在Spring Boot中配置哨兵(chapter16模块)

spring:
  redis:
    # 配置哨兵
    sentinel:
      # 主服务器名称
      master: mymaster
      # 哨兵节点
      nodes: 192.168.224.131:26379,192.168.224.133:26379,192.168.224.134:26379
    # 登录密码
    password: 123456
    # Jedis配置
    jedis:
      # 连接池配置
      pool:
        # 最大等待1秒
        max-wait: 1s
        # 最大空闲连接数
        max-idle: 10
        # 最大活动连接数
        max-active: 20
        # 最小空闲连接数
        min-idle: 5

这样就配置好了哨兵模式下的Redis,为了测试它,可以修改Spring Boot的启动类,如代码清单16-3所示。

代码清单16-3 测试哨兵(chapter16模块)

package com.spring.cloud.chapter16.main;
/**** imports ****/
@SpringBootApplication
@RestController
@RequestMapping("/redis")
public class Chapter16Application {

   public static void main(String[] args) {
       SpringApplication.run(Chapter16Application.class, args);
   }

   // 注入StringRedisTemplate对象,该对象操作字符串,由Spring Boot机制自动装配
   @Autowired
   private StringRedisTemplate stringRedisTemplate = null;

   // 测试Redis写入
   @GetMapping("/write")
   public Map<String, String> testWrite() {
      Map<String, String> result = new HashMap<>();
      result.put("key1", "value1");
      stringRedisTemplate.opsForValue().multiSet(result);
      return result;
   }

   // 测试Redis读出
   @GetMapping("/read")
   public Map<String, String> testRead() {
      Map<String, String> result = new HashMap<>();
      result.put("key1", stringRedisTemplate.opsForValue().get("key1"));
      return result;
   }    
}

这里的testWrite方法是写入一个键值对,testRead方法是读出键值对。我们先在浏览器请求http://localhost:8080/redis/write,然后到各个Redis主从服务器中查看,都可以看到键值对(key1->value1)。当某个哨兵、Redis服务器或者主Redis服务器出现故障时,哨兵都会进行监测,并且通过主观下线或者客观下线进行修复,使得Redis服务能够具备高可用的特性。只是,在进行客观下线的时候,也需要一个时间间隔进行修复,这是我们需要注意的。默认是30秒,可以通过Redis的sentinel.conf文件的sentinel down-after-milliseconds进行修改,例如修改为60秒:

sentinel down-after-milliseconds mymaster 60000

16.1.2 Redis集群

除了可以使用哨兵模式外,还可以使用Redis集群(cluster)技术来实现高可用,不过Redis集群是3.0版本之后才提供的,所以在使用集群前,请注意你的Redis版本。不过在学习Redis集群前,我们需要了解哈希槽(slot)的概念,为此先看一下图16-4。

 

图16-4 哈希槽概念

图16-4中有整数1~6的图形为一个哈希槽,哈希槽中的数字决定了数据将发送到哪台主Redis服务器进行存储。每台主服务器会配置1台到多台从Redis服务器,从服务器会同步主服务器的数据。那么它的工作机制是什么样的呢?下面我们来进行解释。

我们知道Redis是一个key-value缓存,假如计算key的哈希值,得到一个整数,记为hashcode。如果此时执行:

n = hashcode % 6 + 1

得到的n就是一个1到6之间的整数,然后通过哈希槽就能找到对应的服务器。例如,n=4时就会找到主服务器1的Redis服务器,而从服务器1就是其从服务器,会对数据进行同步。

在Redis集群中,大体也是通过相同的机制定位服务器的,只是Redis集群的哈希槽大小为(214=16 384),也就是取值范围为区间[0, 16383],最多能够支持16 384个节点,Redis设计师认为这个节点数已经足够了。对于key,Redis集群会采用CRC16算法计算key的哈希值,关于CRC16算法,本书就不论述了,感兴趣的读者可以自行查阅其他资料进行了解。当计算出key的哈希值(记为hashcode)后,通过对16 384求余就可以得到结果(记为n),根据它来寻找哈希槽,就可以找到对应的Redis服务器进行存储了。它们的计算公式为:

# key为Redis的键,通过CRC16算法求哈希值
hashcode = CRC16(key);
# 求余得到哈希槽中的数字,从而找到对应的Redis服务器 
n = hashcode % 16384

这样n就会落入Redis集群哈希槽的区间[0, 16383]内,从而进一步找到数据。下面举例进行说明,如图16-5所示。

 

图16-5 Redis集群工作原理

这里假设有3个Redis主服务器(或者称为节点),用来存储缓存的数据,每一个主服务器都有一个从服务器,用来复制主服务器的数据,保证高可用。其中哈希槽分配如下。

  • Redis主服务器1:分配哈希槽区间为[0, 5460]。
  • Redis主服务器2:分配哈希槽区间为[5461, 10922]。
  • Redis主服务器3:分配哈希槽区间为[10923, 16383]。

这样通过CRC16算法求出key的哈希值,再对16 384求余数,就知道n会落入哪个哈希槽里,进而决定数据存储在哪个Redis主服务器上。

注意,集群中各个Redis服务器不是隔绝的,而是相互连通的,采用的是PING-PONG机制,内部使用了二进制协议优化传输速度和带宽,如图16-6所示。

从图16-6中可以看出,客户端与Redis节点是直连的,不需要中间代理层,并且不需要连接集群所有节点,只需连接集群中任何一个可用节点即可。在Redis集群中,要判定某个主节点不可用,需要各个主节点进行投票,如果半数以上主节点认为该节点不可用,该节点就会从集群中被剔除,然后由其从节点代替,这样就可以容错了。因为这个投票机制需要半数以上,所以一般来说,要求节点数大于3,且为单数。因为如果是双数,如4,投票结果可能会为2:2,就会陷入僵局,不利于这个机制的执行。

 

图16-6 Redis集群中各个节点是联通的

在某些情况下,Redis集群会不可用,当集群不可用时,所有对集群的操作做都不可用。那么什么时候集群不可用呢?一般来说,分为两种情况。

  • 如果某个主节点被认为不可用,并且没有从节点可以代替它,那么就构建不成哈希槽区间[0, 16383],此时集群将不可用。
  • 如果原有半数以上的主节点发生故障,那么无论是否存在可代替的从节点,都认为该集群不可用。

Redis集群是不保证数据一致性的,这也就意味着,它可能存在一定概率的数据丢失现象,所以更多地使用它作为缓存,会更加合理。

有了上述的理论知识,下面让我们来搭建Redis集群环境。我使用的是Ubuntu来搭建Redis环境,首先进入root用户,然后执行以下命令:

cd /usr
# 创建Redis目录,并进入目录
mkdir redis
cd ./redis 
# 下载Redis
wget http://download.redis.io/releases/redis-5.0.5.tar.gz
# 解压缩安装包
tar xzf redis-5.0.5.tar.gz
# 进入安装目录
cd redis-5.0.5
# 编译安装Redis
make

执行上述命令就安装好了Redis,然后在/usr/redis/redis-5.0.5下创建文件夹cluster,并在其下面创建目录7001、7002、7003、7004、7005和7006,接着将/usr/redis/redis-5.0.5/redis.conf文件复制到目录7001、7002、7003、7004、7005下,最后执行如下命令。

# 进入安装目录
cd /usr/redis/redis-5.0.5
# 创建文件夹cluster和其子目录
mkdir cluster
cd ./cluster 
mkdir 7001 7002 7003 7004 7005 7006
# 复制文件
cp ../redis.conf ./7001
cp ../redis.conf ./7002
cp ../redis.conf ./7003
cp ../redis.conf ./7004
cp ../redis.conf ./7005
cp ../redis.conf ./7006
# 赋予目录下所有文件全部权限
chmod -R 777 ./

这样从7001到7006的目录下就都有一份Redis的启动配置文件了,之所以让目录起名为这些数字,是因为我将会使用这些数字作为端口来分别启动Redis服务。下面,我们首先来修改7001下的redis.conf文件,只修改文件的部分配置,修改的内容如下:

# 关闭保护模式
protected-mode no
# 允许跨域访问
bind 0.0.0.0
# 主机密码
masterauth 123456
# 登录密码
requirepass 123456
# 端口7001
port 7001
# 启用集群模式
cluster-enabled yes
# 集群配置文件
cluster-config-file nodes-7001.conf
# 和集群节点通信的超时时间
cluster-node-timeout 5000
# 采用添加写命令的模式备份
appendonly yes
# 备份文件名称
appendfilename "appendonly-7001.aof"
# 采用后台运行Redis服务
daemonize yes
# PID命令文件
pidfile /var/run/redis_7001.pid

然后再修改7002到7006目录下的redis.conf文件,修改时将所有配置项中的“7001”替换为对应的数字即可,这样我们就可以得到6个启动Redis服务的配置文件了。

接下来就是配置和创建集群了,这里Redis 5也为此提供了工具,并且放在Redis安装目录的子文件夹/utils/create-cluster(我使用的系统全路径为/usr/redis/redis-5.0.5/utils/create-cluster)中。打开这个目录,就可以发现一个create-cluster文件,我们修改它的权限(命令chmod 777 eate-cluster),然后打开它,修改它的内容,代码如下:

#!/bin/bash

# Settings
# 端口,从7000开始,SHELL会自动加1后,找到7001到7006的Redis服务实例
PORT=7000
# 创建超时时间
TIMEOUT=2000
# Redis节点数
NODES=6
# 每台主机的从机数
REPLICAS=1 # ①
# 密码,和我们配置的一致
PASSWORD=123456

......
#### 以下给redis-cli 命令添加配置的密码 #### 
if [ "$1" == "create" ]
then
   HOSTS=""
   while [ $((PORT < ENDPORT)) != "0" ]; do
      PORT=$((PORT+1))
      HOSTS="$HOSTS 192.168.224.135:$PORT"
   done
   ../../src/redis-cli --cluster create $HOSTS -a $PASSWORD --cluster-replicas $REPLICAS
   exit 0
fi

if [ "$1" == "stop" ]
then
   while [ $((PORT < ENDPORT)) != "0" ]; do
      PORT=$((PORT+1))
      echo "Stopping $PORT"
      ../../src/redis-cli -p $PORT -a $PASSWORD shutdown nosave
   done
   exit 0
fi

if [ "$1" == "watch" ]
then
   PORT=$((PORT+1))
   while [ 1 ]; do
      clear
      date
      ../../src/redis-cli -p $PORT -a $PASSWORD cluster nodes | head -30
      sleep 1
   done
   exit 0
fi
......
if [ "$1" == "call" ]
then
   while [ $((PORT < ENDPORT)) != "0" ]; do
      PORT=$((PORT+1))
      ../../src/redis-cli -p $PORT -a $PASSWORD $2 $3 $4 $5 $6 $7 $8 $9
   done
   exit 0
fi
...... 

这段配置看起来挺复杂,实际是很简单的,我修改的是代码中加粗的部分,其余的并未改动。首先修改了端口,例如,端口从7000开始遍历,这样循环加1,就可以找到7001到7006的服务实例。其次给redis-cli命令,加入配置的密码,修改IP。这里尽量不要使用localhost和127.0.01指向本机IP,应该使用该服务器在网络中的IP,否则不在本机客户端登录时,就会出现一些没有必要的错误。至此,所有的配置就都完成了。

跟着我们需要编写脚本,使得我们能够创建、停止和启动集群。为此,在Linux中以root用户登录,然后执行以下命令:

# 进入集群目录
cd /usr/redis/redis-5.0.5/cluster
# 创建3个脚本文件
touch create.sh start.sh shutdown.sh
# 赋予脚本文件全部权限
chmod 777 *.sh

从命令中可以看出,我们创建了3个Shell脚本文件。

  • create.sh:用来启动Redis服务,然后创建集群。
  • start.sh:用来在集群关闭后,启动集群的各个节点。
  • shutdown.sh:关闭运行中的集群的各个节点。

跟着来编写start.sh,代码如下:

# 进入集群工具目录
cd /usr/redis/redis-5.0.5/utils/create-cluster
# 启动集群各个Redis实例,参数为start
./create-cluster start

这个脚本是运行集群的各个节点,只是此时集群还没有被创建,所以还不能运行这个脚本。跟着是shutdown.sh的编写,代码如下:

# 进入集群工具目录
cd /usr/redis/redis-5.0.5/utils/create-cluster
# 停止集群各个Redis实例,参数为stop
./create-cluster stop

这个脚本是停止集群中的各个实例,当然集群现在没有创建和运行,所以它暂时也不能运行。

为了让start.sh和shutdown.sh能够运行,我们需要创建Redis集群,下面编写create.sh,内容如下:

# 在不同端口启动各个Redis服务 ①
/usr/redis/redis-5.0.5/src/redis-server /usr/redis/redis-5.0.5/cluster/7001/redis.conf

/usr/redis/redis-5.0.5/src/redis-server /usr/redis/redis-5.0.5/cluster/7002/redis.conf

/usr/redis/redis-5.0.5/src/redis-server /usr/redis/redis-5.0.5/cluster/7003/redis.conf

/usr/redis/redis-5.0.5/src/redis-server /usr/redis/redis-5.0.5/cluster/7004/redis.conf

/usr/redis/redis-5.0.5/src/redis-server /usr/redis/redis-5.0.5/cluster/7005/redis.conf

/usr/redis/redis-5.0.5/src/redis-server /usr/redis/redis-5.0.5/cluster/7006/redis.conf

# 创建集群,使用参数create  ②
cd /usr/redis/redis-5.0.5/utils/create-cluster
./create-cluster create 

这里分为两段,其中第①段是让Redis在各个端口下启动实例,第②段是创建集群。然后我们运行create.sh脚本,就可以看到图16-7所示的提示。

 

图16-7 创建Redis集群的提示信息

注意图16-7中框中的信息,信息类型大致分为两种。第一种是哈希槽的分配情况,这里提示了分为3个主节点,然后第一个的哈希槽区间为[0, 5460],第二个的为[5461, 10922],第三个的为[10923, 16383]。第二种是从节点的情况,7005端口为7001端口的从节点,7006端口为7002端口的从节点,7004端口为7003端口的从节点。然后它询问我们是否接受该配置,只要输入“yes”回车后,稍等一会儿,它就会创建Redis集群了。

创建好了Redis集群,可以通过命令来验证它,我们先通过redis-cli登录集群,在Linux中执行如下命令。

# 进入目录
cd /usr/redis/redis-5.0.5
# 登录Redis集群:
# -c代表以集群方式登录 
# -p 选定登录的端口 
# -a 登录集群的密码
./src/redis-cli -c -p 7001 -a 123456

这样就能够登录Redis集群了,然后我们可以执行几个Redis的命令来观察执行的情况,执行的命令如下:

set key1 value1
Set key2 value2
set key3 value3
Set key4 value4
set key5 value5

我执行的结果如图16-8所示。

 

图16-8 验证集群

在图16-8中可以看到,在执行命令的时候,Redis会打印出一个哈希槽的数字,然后重新定位到具体的Redis服务器。这些都是Redis集群机制完成的,对于客户端来说,一切都是透明的。

至此,Redis集群我们就搭建成功了。当我们想停止集群的时候,可以执行之前创建好的shutdown.sh。当我们需要启动已经停止的集群的时候,只需要执行start.sh即可。

上述我们搭建了Redis的集群,跟着就要在Spring Boot中使用它了。在Spring Boot中使用它并不麻烦,只需要先注释掉代码清单16-3中的配置,然后在application.yml文件中加入代码清单16-4所示的代码即可。

代码清单16-4 Spring Boot配置Redis集群(chapter16模块)

spring:
  redis:
    # 登录密码
    # Jedis配置
    jedis:
      # 连接池配置
      pool:
        # 最大等待1秒
        max-wait: 1s
        # 最大空闲连接数
        max-idle: 10     
        # 最大活动连接数
        max-active: 20
        # 最小空闲连接数
        min-idle: 5
    # 配置Redis集群信息
    cluster:
       # 集群节点信息
      nodes: 192.168.224.135:7001,192.168.224.135:7002,192.168.224.135:7003,192.168.224.135:7004,192.168.224.135:7005,192.168.224.135:7006
      # 最大重定向数,一般设置为5,
      # 不建议设置过大,过大容易引发重定向过多的异常
      max-redirects: 5
    password: 123456

这样就在Spring Boot中配置好了,可以像往常一样通过RedisTemplate或者StringRedisTemplate来操作Redis集群了。

本文摘自《Spring Cloud微服务和分布式系统实践》

Spring Cloudå¾®æå¡ååå¸å¼ç³»ç»å®è·µ

  • 结合实践讲解Spring Cloud 微服务系统基础组件的原理和应用 
  • 结合微服务讲解分布式系统的相关知识 
  • 结合企业真实需求讲解微服务(分布式)系统的开发 
  • 基于Spring Boot 2.x和Greenwich.RELEASE进行讲解。 


本书是讲述Spring Cloud微服务及其组件的专业技术书籍。微服务系统作为分布式系统的一种形式,必然会带有分布式系统的各种弊病,因此本书也会介绍分布式系统的一些常见知识,以更好满足企业构建系统的需求。

本书首先介绍分布式系统和微服务的概念以及技术基础;然后介绍Spring Cloud的主要组件,包含服务治理和服务发现、服务调用、断路器、API网关、服务配置和服务监控等,这部分是本书的主要内容;接着介绍企业实践中经常用到的分布式技术,包括分布式数据库事务、分布式Redis缓存等;最后介绍远程过程调用(RPC)以及微服务设计和高并发实践。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值