【Linux运维】Redis集群的三种方式

为什么要有集群


单个Redis存在不稳定性,当Redis服务宕机或硬盘故障,系统崩溃之后,就没有可用的服务了,还会造成数据的丢失

单个Redis的读写能力也是有限的

还由于互联网的三高架构,高并发,高性能,高可用

| 概念

通过添加服务器的数量,提供相同的服务,从而让服务器达到一个稳定,高效的状态

| 三种模式

  • 主从模式

  • 哨兵模式

  • Cluster模式

主从模式介绍


【Linux运维】Redis集群的三种方式

| 为什么要用主从复制

单机redis的风险与问题

问题一:机器故障

  • 硬盘故障,系统崩溃

  • 本质:数据丢失,可能对业务造成灾难性打击

问题二:容量瓶颈

  • 内存不足,一台redis内存是有限的

解决方案:

为了避免单点Redis服务器故障,准备多台服务器,互相联通,将数据复制多个副本保存在不同服务器上,连接在一起,并保证数据是同步的,即使有其中一台服务器宕机,其他服务器依然可以继续提供服务,实现Redis的高可用,同时实现数据冗余备份

| 工作原理

【Linux运维】Redis集群的三种方式

如上图所示,是Redis的工作原理,其对应的文字说明如下:

  1. replica库通过”SLAVEOF 172.200.1.201 6379″命令连接master库并发送”SYNC”给master

  2. master库收到”SYNC”,会立即触发”BGSAVE”后台保存RDB快照,并将RDB快照发送给replica库;

  3. replica库接收后会应用RDB快照;

  4. master库会陆续将中间产生的新的操作保存并发送给replica库;

  5. 到此我们master复制集就正常工作了;

  6. 再此以后,master库只要发生新的操作,都会以命令传播的形式自动发送给replica库;

  7. 所有复制相关信息,从info信息中都可以查到,即使重启任何节点,他的master-replica关系依然都在;

  8. 如果发生master-replica关系断开时,replica库数据没有任何损坏,在下次重连之后,replica库发送”PSYNC

  9. master库只会将replica库缺失部分的数据同步给replica库应用,达到快速恢复master-replica架构的目的;

温馨提示:(可能面试时会问到):

  1. 在Redis 2.8版本之前,如果master-replica架构中replica库宕机将会再次触发”SYNC”,从而全量拷贝主库的数据,这样效率明显很低

    于是在Redis 2.8版本之后就新增了”PSYNC”指令以实现可以理解为”断点续传”的功能

  2. 生产环境中,建议开启master库持久化功能,避免因主库意外重启而导致缓存丢失

    举个例子,master库右1w条数据,replica库在某一时刻仅有9k数据,还有1k数据未来得及同步master库时,master库意外重启导致KEY为0,此时replica库同步master库数据会将replica库已有的9k缓存数据删除以达到和master库同步的目的

哨兵模式介绍

【Linux运维】Redis集群的三种方式

哨兵(sentinel)是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master

| 为什么有哨兵

主机宕机,如何能够高可用的恢复正常,不用人为进行干预

作用:

  • 监控:不断地检查master和slave是否正常运行;master存活检测,master与slave运行清空检测

  • 通知(提醒):当被监控地服务器出现问题时,向其他(哨兵、客户端)发送通知

  • 自动故障转移:断开master与slave连接,选取一个slave作为master,将其他slave连接到新地master,并告知客户端新地服务器地址

注意:

  • 哨兵也是一台redis服务器,只是不提供数据服务

  • 通常哨兵配置数量为单数

Cluster模式介绍

redis最开始使用主从模式做集群,若master宕机需要手动配置slave转为master

后来为了高可用提出来哨兵模式,该模式下有一个哨兵监视master和slave,若master宕机可自动将slave转为master,但它也有一个问题,就是不能动态扩充,哨兵模式还是只有一个master

所以在3.x提出cluster集群模式

| 工作原理

【Linux运维】Redis集群的三种方式

Redis数据分片原理说明:

  1. redis会有多组分片构成,本篇笔记实验环境是3组;

  2. redis cluster使用固定个数的slot存储数据,一共16384是slot;

  3. 每组分片分得1/3 slot个数,分别为”0-5460,5461-10921,10922-16383″

  4. 基于CRC16(key) % 16384公式会得到一个”槽位号”(该编号范围在”0-16383″之间);

  5. 根据计算得出的槽位值,找到相对应的分片节点的主节点,存储到相应槽位上;

  6. 如果客户端当时连接的节点不是将来要存储的分片节点,分片集群会将客户端连接切换至真正存储节点进行数据存储;

综上所述,我们来举个例子,在Redis分布式集群中,假设我们执行了”SET name oldboy”这一条指令,那么数据该如何存储的呢?其大致的存储逻辑如下:

  1. 使用”CRC16(KEY_NAME)”进行校验计算一个值,假设”CRC16(name)”的计算结果为”1234567890″;

  2. 再使用”CRC16(KEY_NAME)%16384″进行计算,从而得出该KEY_NAME被分配到哪个slot上,假设结果为”1234567890%16384=722″,就表示数据被落到slot=722的分片上;

  3. 而slot=722恰好在第一个分片上,因为我们第一个分片的slot范围就是”0-5460″

主从模式部署


| 为各个Redis实例创建数据和配置文件目

【Linux运维】Redis集群的三种方式


# 创建Redis的数据存储目录

mkdir -pv /oldboyedu/data/redis1637{0..2}


# 创建Redis的配置文件存储目录

mkdir -pv /oldboyedu/softwares/redis1637{0..2}

| 为各个Redis实例创建配置文件

1)为”redis16370″实例创建配置文件

【Linux运维】Redis集群的三种方式


# cat > /oldboyedu/softwares/redis16370/redis.conf <<EOF

port 16370

daemonize yes

pidfile /oldboyedu/data/redis16370/redis.pid

loglevel notice

logfile /oldboyedu/data/redis16370/redis.log

dbfilename dump.rdb

dir /oldboyedu/data/redis16370

bind 172.200.1.201 127.0.0.1

requirepass oldboyedu

masterauth oldboyedu

# 配置RDB持久化策略

save 900 1

save 300 10

save 60 10000

# 配置AOF持久化

appendonly yes

appendfsync everysec

EOF

2)为”redis16371″实例创建配置文件

【Linux运维】Redis集群的三种方式


# cat > /oldboyedu/softwares/redis16371/redis.conf <<EOF

port 16371

daemonize yes

pidfile /oldboyedu/data/redis16371/redis.pid

loglevel notice

logfile /oldboyedu/data/redis16371/redis.log

dbfilename dump.rdb

dir /oldboyedu/data/redis16371

bind 10.0.0.112 127.0.0.1

requirepass oldboyedu

masterauth oldboyedu

# 配置RDB持久化策略

save 900 1

save 300 10

save 60 10000

# 配置AOF持久化

appendonly yes

appendfsync everysec

EOF

3)为”redis16372″实例创建配置文件

【Linux运维】Redis集群的三种方式


cat > /oldboyedu/softwares/redis16372/redis.conf <<EOF

port 16372

daemonize yes

pidfile /oldboyedu/data/redis16372/redis.pid

loglevel notice

logfile /oldboyedu/data/redis16372/redis.log

dbfilename dump.rdb

dir /oldboyedu/data/redis16372

bind 10.0.0.112 127.0.0.1

requirepass oldboyedu

masterauth oldboyedu

# 配置RDB持久化策略

save 900 1

save 300 10

save 60 10000

# 配置AOF持久化

appendonly yes

appendfsync everysec

EOF

温馨提示:

  1. 所有的requirepass建议设置为一致,这为我们”哨兵模式”做准备,很明显,”requirepass”表示配置当前实例的验证口令;

  2. 注意”masterauth”表示的含义是主库的验证口令;下面的模板你可以根据自己业务的实际需求进行改动即可

【Linux运维】Redis集群的三种方式


 cat > /oldboyedu/softwares/redis16372/redis.conf <<EOF

port 16372

daemonize yes

pidfile /oldboyedu/data/redis16372/redis.pid

loglevel notice

logfile /oldboyedu/data/redis16372/redis.log

dbfilename dump.rdb

dir /oldboyedu/data/redis16372

bind 10.0.0.112 127.0.0.1

requirepass oldboyedu

masterauth oldboyedu

# 配置RDB持久化策略

save 900 1

save 300 10

save 60 10000

# 配置AOF持久化

appendonly yes

appendfsync everysec

EOF

| 启动各个Redis实例

1)启动各个Redis实例

【Linux运维】Redis集群的三种方式


redis-server /oldboyedu/softwares/redis16370/redis.conf

redis-server /oldboyedu/softwares/redis16371/redis.conf

redis-server /oldboyedu/softwares/redis16372/redis.conf

# 执行 ss -lnt 查看是否启动

ss -lnt

【Linux运维】Redis集群的三种方式

2)停止各个Redis实例运行,此步骤可跳过,因为我们暂时还不需要停止Redis,我们要用Redis实例做主从架构

【Linux运维】Redis集群的三种方式


redis-cli -p 16370 -a oldboyedu shutdown

redis-cli -p 16371 -a oldboyedu shutdown

redis-cli -p 16372 -a oldboyedu shutdown

| 开启Redis的主从

实验说明:redis16370 实例为主库,以 redis16371 实例和 redis16372 实例为从库

【Linux运维】Redis集群的三种方式


# 从库实例开启主从

redis-cli -p 16371 -a oldboyedu SLAVEOF 10.0.0.112 16370

redis-cli -p 16372 -a oldboyedu SLAVEOF 10.0.0.112 16370

| 主库实例查看主从信息

【Linux运维】Redis集群的三种方式


redis-cli -p 16370 -a oldboyedu INFO REPLICATION

| 从库实例查看主从信息

在 “redis16371” 实例中查看主从状态信息:

信息

【Linux运维】Redis集群的三种方式


redis-cli -p 16371 -a oldboyedu INFO REPLICATION

【Linux运维】Redis集群的三种方式

在 “redis16372” 实例中查看主从状态信息:

【Linux运维】Redis集群的三种方式


redis-cli -p 16372 -a oldboyedu INFO REPLICATION

【Linux运维】Redis集群的三种方式

| 验证主从配置是否生效

在”redis16370″实例主库的11号数据库创建测试数据

【Linux运维】Redis集群的三种方式


# redis-cli -p 16370 -a oldboyedu -n 11

16370[11]> KEYS *

(empty array)

16370[11]> SET name oldboyedu

OK

16370[11]> SET age 18

OK

16370[11]> KEYS *

1) "name"

2) "age"

16370[11]> QUIT

在”redis16371″实例从库的11号数据库查看是否同步主库的11号数据库

【Linux运维】Redis集群的三种方式


# redis-cli -p 16371 -a oldboyedu -n 11

16371[11]> KEYS *

1) "name"

2) "age"

16371[11]> GET name

"oldboyedu"

16371[11]> GET age

"18"

16371[11]> QUIT

在”redis16372″实例从库的11号数据库查看是否同步主库的11号数据库

【Linux运维】Redis集群的三种方式


# redis-cli -p 16372 -a oldboyedu -n 11

16371[11]> KEYS *

1) "name"

2) "age"

16371[11]> GET name

"oldboyedu"

16371[11]> GET age

"18"

16371[11]> QUIT

| 解除”redis16372″实例的主从关系

【Linux运维】Redis集群的三种方式


redis-cli -p 16372 -a oldboyedu SLAVEOF NO ONE

【Linux运维】Redis集群的三种方式

| 跨节点部署Redis集群实战案例

【Linux运维】Redis集群的三种方式


(0)主从架构设计

主库为:

10.0.0.108

从库:

10.0.0.106

10.0.0.107

(1)10.0.0.106的配置

install -d /oldboyedu/data/redis

cat > /oldboyedu/softwares/redis/conf/redis.conf <<EOF

port 8106

daemonize yes

pidfile /oldboyedu/data/redis/redis.pid

loglevel notice

logfile /oldboyedu/data/redis/redis.log

dbfilename oldboyedu_linux.rdb

dir /oldboyedu/data/redis/

bind 10.0.0.106

requirepass oldboyedu

masterauth oldboyedu

# 配置RDB持久化策略

save 900 1

save 300 10

save 60 10000

EOF


redis-server /oldboyedu/softwares/redis/conf/redis.conf


(2)10.0.0.107的配置

install -d /oldboyedu/data/redis

cat > /oldboyedu/softwares/redis/conf/redis.conf <<EOF

port 8107

daemonize yes

pidfile /oldboyedu/data/redis/redis.pid

loglevel notice

logfile /oldboyedu/data/redis/redis.log

dbfilename oldboyedu_linux.rdb

dir /oldboyedu/data/redis/

bind 10.0.0.107

requirepass oldboyedu

masterauth oldboyedu

# 配置RDB持久化策略

save 900 1

save 300 10

save 60 10000

EOF


redis-server /oldboyedu/softwares/redis/conf/redis.conf



(3)10.0.0.108的配置

install -d /oldboyedu/data/redis

cat > /oldboyedu/softwares/redis/conf/redis.conf <<EOF

port 8108

daemonize yes

pidfile /oldboyedu/data/redis/redis.pid

loglevel notice

logfile /oldboyedu/data/redis/redis.log

dbfilename oldboyedu_linux.rdb

dir /oldboyedu/data/redis/

bind 10.0.0.108

requirepass oldboyedu

masterauth oldboyedu

# 配置RDB持久化策略

save 900 1

save 300 10

save 60 10000

EOF


redis-server /oldboyedu/softwares/redis/conf/redis.conf



(4)从库开启主从

# 10.0.0.106:

redis-cli -p 8106 -h 10.0.0.106 -a oldboyedu SLAVEOF 10.0.0.108 8108

# 10.0.0.107:

redis-cli -p 8107 -h 10.0.0.107 -a oldboyedu SLAVEOF 10.0.0.108 8108

哨兵模式部署

| 准备3个Redis实例

(可以直接基于上一节搭建的主从复制环境即可)

【Linux运维】Redis集群的三种方式

| 创建sentinel的配置文件,数据,日志存储目录

【Linux运维】Redis集群的三种方式


mkdir /oldboyedu/softwares/redis16379


cat > /oldboyedu/softwares/redis16379/sentinel.conf <<EOF

bind 10.0.0.112 127.0.0.1

port 16379

dir /oldboyedu/data/redis16379

sentinel monitor oldboyedu_master 10.0.0.112 16370 1

sentinel down-after-milliseconds oldboyedu_master 5000

sentinel auth-pass oldboyedu_master oldboyedu

EOF

配置文件参数说明如下:

【Linux运维】Redis集群的三种方式

| 启动sentinel进程并查看日志

启动sentinel进程

【Linux运维】Redis集群的三种方式


redis-sentinel /oldboyedu/softwares/redis16379/sentinel.conf &> /oldboyedu/logs/redis16379/sentinel.log &

查看日志信息

【Linux运维】Redis集群的三种方式


tail -100f /oldboyedu/logs/redis16379/sentinel.log

【Linux运维】Redis集群的三种方式

| 手动停止主库运行,模拟主库宕机

手动将主库宕机,观察从库被sentinel实现了自动切换

【Linux运维】Redis集群的三种方式


redis-cli -p 16370 -a oldboyedu SHUTDOWN

# 由于我们将主库服务停了,因此无法再次连接主库了

redis-cli -p 16370 -a oldboyedu INFO REPLICATION  


redis-cli -p 16371 -a oldboyedu INFO REPLICATION

【Linux运维】Redis集群的三种方式

查看sentinel的日志信息,不难发现有记录切换的过程,如下所示

【Linux运维】Redis集群的三种方式


tail -100f /oldboyedu/logs/redis16379/sentinel.log

【Linux运维】Redis集群的三种方式

| 手动修复宕机的主库,sentinel会自动发现

修复之后,之前宕机的主库会自动加入集群并成为slaves节点

【Linux运维】Redis集群的三种方式


redis-server /oldboyedu/softwares/redis16370/redis.conf 

redis-cli -p 16370 -a oldboyedu INFO REPLICATION

【Linux运维】Redis集群的三种方式

温馨提示:除了提到上面的变化外,还有一点变化就是配置文件也修改了,每个”slave”节点都新增了以下两行:

(本案例只有”redis16370″和”redis16372″这两个实例的配置文件是slave节点,因此这两个节点中是存在的!)

【Linux运维】Redis集群的三种方式


# tail -3 /oldboyedu/softwares/redis16370/redis.conf

# Generated by CONFIG REWRITE

user default on #4208512d2963b999e0ef7a467cb42671c30be4d629ec75536a0eb6db15ee0d90 ~* &* +@all

replicaof 10.0.0.112 16371


# tail -3 /oldboyedu/softwares/redis16372/redis.conf

# Generated by CONFIG REWRITE

user default on #4208512d2963b999e0ef7a467cb42671c30be4d629ec75536a0eb6db15ee0d90 ~* &* +@all

replicaof 10.0.0.112 16371

Cluster模式部署

| 实验环境说明

在生产环境中,如果部署6个redis实例,一般会放到3台硬件服务器

在企业规划中,一个分片被分配到两个不同的物理机,防止硬件主机宕机造成的整个分片数据丢失

温馨提示:

本案例为了实验方便,就直接将6个Redis实例放在同一个节点上,对应的端口号为:63790-63795

| 创建多Redis实例使用的配置文件,数据,日志目录

【Linux运维】Redis集群的三种方式


mkdir -pv /oldboyedu/softwares/redis1000{0..5}

mkdir -pv /oldboyedu/data/redis1000{0..5}

mkdir -pv /oldboyedu/logs/redis1000{0..5}

|  为各个Redis实例创建配置文件

【Linux运维】Redis集群的三种方式


# 创建"redis10000"实例的配置文件

cat > /oldboyedu/softwares/redis10000/redis.conf <<EOF

port 10000

daemonize yes

pidfile /oldboyedu/data/redis10000/redis.pid

loglevel notice

logfile /oldboyedu/logs/redis10000/redis.log

dbfilename dump.rdb

dir /oldboyedu/data/redis10000

bind 10.0.0.112 127.0.0.1

# 配置RDB持久化策略

save 900 1

save 300 10

save 60 10000

# 配置AOF持久化

appendonly yes

appendfsync everysec

# 配置Redis Cluster模式

protected-mode no

cluster-enabled yes

cluster-config-file nodes.conf

cluster-node-timeout 5000

EOF

# 创建"redis10001"实例的配置文件

cat > /oldboyedu/softwares/redis10001/redis.conf <<EOF

port 10001

daemonize yes

pidfile /oldboyedu/data/redis10001/redis.pid

loglevel notice

logfile /oldboyedu/logs/redis10001/redis.log

dbfilename dump.rdb

dir /oldboyedu/data/redis10001

bind 10.0.0.112 127.0.0.1

# 配置RDB持久化策略

save 900 1

save 300 10

save 60 10000

# 配置AOF持久化

appendonly yes

appendfsync everysec

# 配置Redis Cluster模式

protected-mode no

cluster-enabled yes

cluster-config-file nodes.conf

cluster-node-timeout 5000

EOF

# 创建"redis10002"实例的配置文件

cat > /oldboyedu/softwares/redis10002/redis.conf <<EOF

port 10002

daemonize yes

pidfile /oldboyedu/data/redis10002/redis.pid

loglevel notice

logfile /oldboyedu/logs/redis10002/redis.log

dbfilename dump.rdb

dir /oldboyedu/data/redis10002

bind 10.0.0.112 127.0.0.1

# 配置RDB持久化策略

save 900 1

save 300 10

save 60 10000

# 配置AOF持久化

appendonly yes

appendfsync everysec

# 配置Redis Cluster模式

protected-mode no

cluster-enabled yes

cluster-config-file nodes.conf

cluster-node-timeout 5000

EOF

# 创建"redis10003"实例的配置文件

cat > /oldboyedu/softwares/redis10003/redis.conf <<EOF

port 10003

daemonize yes

pidfile /oldboyedu/data/redis10003/redis.pid

loglevel notice

logfile /oldboyedu/logs/redis10003/redis.log

dbfilename dump.rdb

dir /oldboyedu/data/redis10003

bind 10.0.0.112 127.0.0.1

# 配置RDB持久化策略

save 900 1

save 300 10

save 60 10000

# 配置AOF持久化

appendonly yes

appendfsync everysec

# 配置Redis Cluster模式

protected-mode no

cluster-enabled yes

cluster-config-file nodes.conf

cluster-node-timeout 5000

EOF

# 创建"redis10004"实例的配置文件

cat > /oldboyedu/softwares/redis10004/redis.conf <<EOF

port 10004

daemonize yes

pidfile /oldboyedu/data/redis10004/redis.pid

loglevel notice

logfile /oldboyedu/logs/redis10004/redis.log

dbfilename dump.rdb

dir /oldboyedu/data/redis10004

bind 10.0.0.112 127.0.0.1

# 配置RDB持久化策略

save 900 1

save 300 10

save 60 10000

# 配置AOF持久化

appendonly yes

appendfsync everysec

# 配置Redis Cluster模式

protected-mode no

cluster-enabled yes

cluster-config-file nodes.conf

cluster-node-timeout 5000

EOF

# 创建"redis10005"实例的配置文件

cat > /oldboyedu/softwares/redis10005/redis.conf <<EOF

port 10005

daemonize yes

pidfile /oldboyedu/data/redis10005/redis.pid

loglevel notice

logfile /oldboyedu/logs/redis10005/redis.log

dbfilename dump.rdb

dir /oldboyedu/data/redis10005

bind 10.0.0.112 127.0.0.1

# 配置RDB持久化策略

save 900 1

save 300 10

save 60 10000

# 配置AOF持久化

appendonly yes

appendfsync everysec

# 配置Redis Cluster模式

protected-mode no

cluster-enabled yes

cluster-config-file nodes.conf

cluster-node-timeout 5000

EOF

配置参数说明:

【Linux运维】Redis集群的三种方式

温馨提示:

  1. Redis的端口号范围在10000~55535之间。这是因为当我们启动各个redis实例后,会监听在对应redis实例的端口上加10000;

  2. 由于上面我使用了”protected-mode no”指令,表示不开启安全模式,如果设置为”YES”,尽管配置了”requirepass”和”masterauth”这两个配置项,我们在将redis节点加入到集群时依旧会报错哟!

  3. Redis cluster模式无需像redis sentinel模式那样有一个专门的监控模式;

| 启动各个Redis实例

【Linux运维】Redis集群的三种方式


redis-server /oldboyedu/softwares/redis10000/redis.conf

redis-server /oldboyedu/softwares/redis10001/redis.conf

redis-server /oldboyedu/softwares/redis10002/redis.conf

redis-server /oldboyedu/softwares/redis10003/redis.conf

redis-server /oldboyedu/softwares/redis10004/redis.conf

redis-server /oldboyedu/softwares/redis10005/redis.conf

| 将节点加入集群管理

【Linux运维】Redis集群的三种方式


redis-cli --cluster create 10.0.0.112:10000 10.0.0.112:10001 10.0.0.112:10002 10.0.0.112:10003 10.0.0.112:10004 10.0.0.112:10005 --cluster-replicas 1

注意:这里需要自己手动输入yes,如果超时了还没有输入,则会自动退出,需要再次创建!

看到以下信息说明创建redis缓存库成功:

【Linux运维】Redis集群的三种方式

| 查看集群的状态

【Linux运维】Redis集群的三种方式


# 查看集群的状态

redis-cli -p 10005 CLUSTER NODES


# 查看master节点的状态

redis-cli -p 10005 CLUSTER NODES | grep "master"


# 查看slave节点的状态

redis-cli -p 10005 CLUSTER NODES | grep "slave"

【Linux运维】Redis集群的三种方式

每项的含义如下:

【Linux运维】Redis集群的三种方式

各flags的含义 (上面所说数据项3):

【Linux运维】Redis集群的三种方式

温馨提示:

  1. 如果有”myself”字样,说明你正在连接的是哪个节点;

  2. 注意观察槽位编号信息,比如”10.0.0.112:10000 master”被分配到的槽位号是”0-5460″,而”10.0.0.112:10001″被分配到的槽位号是”5461-10922″,”10.0.0.112:10002 master”被分配到的槽位号是”10923-16383″;

  3. 每个redis实例都有其独有的id编号,比如”10.0.0.112:10000 master”的编号为”eefe41adf0a9dc823ef56183881bfa14f10571b9″,这有点类似于MySQL的”server_uuid”;

总结

在本教程中,我们通过一些示例学习了如何去部署Redis的三种集群

感谢您的阅读,由于文档整理较费时费力,难免会有疏漏和不妥当地方,敬请谅解

原文始发于微信公众号(运维基地):【Linux运维】Redis集群的三种方式

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/23534.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!