文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

MongoDB中复制集集群的原理是什么

2024-04-02 19:55

关注

这篇文章给大家介绍MongoDB中复制集集群的原理是什么,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

复制集介绍

MongoDB中的复制集(也被称为副本)是一组维护相同数据集的mongod进程。副本集提供冗余性及和高可用,是所有生产部署的基础。简单来说,复制集有多台MongoDB组成的一个集群,集群中有一个主节点(Primary)和N个副本节点(Secondary)等,它们有相同的数据库,假如主MongoDB服务器或者MongoDB实例Down机之后,其它的副本服务器可以继续提供服务,实现数据的高可用及可靠性。

复制集群架构

术语介绍:

术语类型描述
Primary主节点负责整个集群的读写操作,包含了所有改变操作的日志
Secondary备节点同步主服务器所有的数据,负责集群的读取请求,主服务器宕机可以称为主节点
Arbiter仲裁者在主节点宕机后只进行投票,不参与选举,不同步主节点数据


MongoDB中复制集集群的原理是什么

此架构由一个Primary节点和两个Secondary节点组成

1)Primary节点为主节点,所有的写操作或者更改操作都只能从Primary节点中操作(复制集内的所有成员都可以接收读操作,但是,默认情况下,应用程序将其读操作指向主成员),主节点上所有的更改及写操作都会记录到oplog日志中。

2)两台Secondary节点复制Primary节点的oplog日志,通过异步的方式去执行oplog日志中的记录来和Primary节点达到数据一致性。

3)oplog作用主要是记录主节点的写入操作,充当复制源。

MongoDB中复制集集群的原理是什么

4)如果Primary节点无故Down机之后,复制集集群会通过投票机制在两台Secondary中选举一台升级为Primary节点。

投票选举机制

MongoDB节点之间维护心跳检查,主节点选举由心跳触发。

心跳检查 MongoDB复制集成员会向自己之外的所有成员发送心跳并处理响应信息,因此每个节点都维护着该节点看到的其它所有节点的状态信息,节点根据自己的集群状态判断是否需要更新新的Primary。在实现的时候主要由两个异步的过程分别处理心跳响应和超时,每个复制集成员都会在后台运行与复制集所有节点的心跳线程,在以下几种情况下会触发状态检测过程:

•Secondary节点权重(Priority)比Primary节点高时,发起替换选举;

•Secondary节点发现集群中没有Primary时,发起选举;

•Primary节点不能访问到大部分成员时主动降级,降级操作会断开连接,终止用户请求等;

•复制集成员心跳检测结果发生变化,比如某个节点挂了或者新增节点,发起重新投票选举规则;

•超过4s没有执行状态检测过程,发起替换选举;

选举发起 发起选举的节点首先需要做一些条件判断,维护主节点的有N个备用节点,备用节点中的所有节点都可能被选举成为主节点,成为主节点前每个备节点都会检测自身以及全局条件是否满足,检测条件如下:

1.是否看见复制集中是否有Majority在线

2.自身Priority是否大于0

3.自身不为arbiter

4.自身opTime不能落后于最新节点10s以上

5.自身存储的集群程序按信息为最新

如果所有条件满足,则将自身添加到主节点的备用列表中,否则,将自身从列表中移除

自身检测

•MongoDB选举需要获得大多数投票才能通过,如果没有节点投反对票,且获得成票数超过有权投票节点总数的1/2,则能成为Primary。否则进入下一轮选举。为避免陷入无限重复选举,MongoDB建议复制集的成员个数为奇数,当Secondary为双数时,可以增加一个Arbiter节点。

•选举过程中,复制集没有主节点,所有成员都是只读状态

•选举过程很复杂,一般情况下需要5s左右进行选主。

•如果新选择的主节点立刻挂掉,至少需要30s时间重新选主。

大多数的定义  假设复制集内投票成员数量为N,则大多数 = N/2 + 1 ,当复制集内存活成员数量不足大多数时,整个复制集将无法选举出Primary,复制集将无法提供写服务,处于只读状态。 我们按照上面的架构来举例,三台MongoDB,一台Primary,两台Secondary,主节点挂了之后,只有两台Secondary可以投票,根据公式我们来算 “2/2 + 1 = 2”,也就是算大多数等于2,但是当复制集内存活的成员数量不足大多数时,我们的大多数为2,集群成员也为2,所以这两台集群成员会发起选举投票机制,如果两台Secondary节点自身条件都满足的情况下,则先发起选举节点的成员成为Primary节点  

投票成员数大多数容忍失效数
110
220
321
431
532
642
743

复制集群成员说明

Secondary 正常情况下,复制集的Seconary会参与Primary选举(自身也可能会被选为Primary),并从Primary同步最新写入的数据,以保证与Primary存储相同的数据。Secondary可以提供读服务,增加Secondary节点可以提供复制集的读服务能力,同时提升复制集的可用性。另外,Mongodb支持对复制集的Secondary节点进行灵活的配置,以适应多种场景的需求。

Arbiter Arbiter节点只参与投票,不能被选为Primary,并且不从Primary同步数据。比如你部署了一个2个节点的复制集,1个Primary,1个Secondary,任意节点宕机,复制集将不能提供服务了(无法选出Primary),这时可以给复制集添加一个Arbiter节点,即使有节点宕机,仍能选出Primary。Arbiter本身不存储数据,是非常轻量级的服务,当复制集成员为偶数时,最好加入一个Arbiter节点,以提升复制集可用性。

Priority0 Priority0节点的选举优先级为0,不会被选举为Primary。比如你跨机房A、B部署了一个复制集,并且想指定Primary必须在A机房,这时可以将B机房的复制集成员Priority设置为0,这样Primary就一定会是A机房的成员。(注意:如果这样部署,最好将『大多数』节点部署在A机房,否则网络分区时可能无法选出Primary)

Vote0 Mongodb 3.0里,复制集成员最多50个,参与Primary选举投票的成员最多7个,其他成员(Vote0)的vote属性必须设置为0,即不参与投票。

Hidden Hidden节点不能被选为主(Priority为0),并且对Driver不可见。因Hidden节点不会接受Driver的请求,可使用Hidden节点做一些数据备份、离线计算的任务,不会影响复制集的服务。

Delayed Delayed节点必须是Hidden节点,并且其数据落后与Primary一段时间(可配置,比如1个小时)。因Delayed节点的数据比Primary落后一段时间,当错误或者无效的数据写入Primary时,可通过Delayed节点的数据来恢复到之前的时间点。

优先级为0复制集成员

MongoDB中复制集集群的原理是什么

此架构由一个Primary节点和两个Secondary节点组成

1)此架构由一台Primary主节点和两台Secondary备节点组成,其原理就是主从复制架构的原理,两台Secondary节点同样通过oplog日志来与Primary主节点达成数据一致

2)与其不同的是在Data Center2节点上的Secondary备用节点的实例优先级priority为0,则不参与选举,也不可能会成为Primary节点,将其优先级配置为0,主要是防止它成为主节点,这在多数据中心的部署特别有用。

3)优先级值范围为 0-100(0表示不参与选举),在复制集群中,优先级高的优先成为主节点,假如我们原来集群中有三台节点,主节点优先级为2,其它两台备节点优先级为1,当我们新加入到集群一台MongoDB实例,给它优先级设置为4,则该实例在加入集群后就会自动抢夺Primary到本机。

仲裁节点架构

MongoDB中复制集集群的原理是什么

上图中,三个成员组成复制集群

一个主库:负责整个集群的所有写、更改操作

一个从库:通过oplog日志来与主节点数据达成一致

一个Airbiter节点,在选举中,只进行投票,不能成为主库,而且不复制Primary的任何数据,因此这个架构中只能提供一个完成的副本Secondary,Arbiter只需要很少的资源,代价是有限的冗余和容错,当Primary节点故障后,Aribiter将票数头给Secondary,使其成为Primary节点,如果Primary节点再次故障后,集群将不可用,Arbiter节点也未存储任何数据。

MongoDB中复制集集群的原理是什么

集群中还有其它的节点成员,但是我们用的比较少,所以此文章中没有提到。可以自行查阅官方文档:https://docs.mongodb.com/manual/core/replica-set-members/

复制集集群环境部署

环境说明

本次使用一台设备多实例进行,如果你准备在多台设备上部署,你需要考虑的如下:1)时钟是否一致 2)网络是否通畅 3)SElinux是否关闭或者策略放通 4)环境是否一致

[root@MongoDB ~]# lsb_release -a  LSB Version:    :core-4.1-amd64:core-4.1-noarch:cxx-4.1-amd64:cxx-4.1-noarch:desktop-4.1-amd64:desktop-4.1-noarch:languages-4.1-amd64:languages-4.1-noarch:printing-4.1-amd64:printing-4.1-noarch Distributor ID:    CentOS  Description:    CentOS Linux release 7.6.1810 (Core)   Release:    7.6.1810  Codename:    Core  [root@MongoDB ~]# hostname -I  10.211.55.12 192.168.0.100 fdb2:2c26:f4e4:0:21c:42ff:fedf:4d85

前期准备

#创建用户组  useradd mongod  echo 'abcdef' | passwd --stdin mongod  #下载MongoDB  wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.2.0.tgz  #添加文件打开数和mongod用户进程连接数  cat >> /etc/security/limits.conf << EOF  * soft nofile 65536  * hard nofile 65536  mongod soft nproc 32768  mongod hard nproc 32768  EOF   #使下面两个文件修改为never  echo never > /sys/kernel/mm/transparent_hugepage/enabled  echo never > /sys/kernel/mm/transparent_hugepage/defrag  #开机自动修改  cat >> /etc/rc.local << EOF  if test -f /sys/kernel/mm/transparent_hugepage/enabled;then      echo never > /sys/kernel/mm/transparent_hugepage/enabled  fi  if test -f /sys/kernel/mm/transparent_hugepage/defrag;then      echo never > /sys/kernel/mm/transparent_hugepage/defrag  fi  EOF  此步骤完成后  #官方给出MongoDB需要以下两个库的依赖  yum install libcurl openssl -y  #解压并复制程序文件到bin目录  mkdir /usr/local/mongodb  tar xf mongodb-linux-x86_64-rhel70-4.2.0.tgz  cp -rf mongodb-linux-x86_64-rhel70-4.2.0/bin/ /usr/local/mongodb/  chown -Rf mongod.mongod /usr/local/moongodb/  #添加程序环境  cat >> /etc/profile << EOF  export MONGODB_HOME=/usr/local/mongodb  export PATH=\$MONGODB/bin:\$PATH  EOF  source /etc/profile

环境配置

1)接下来使用mongod用户来操作

su mongod

2)创建文件目录

#!/bin/bash  for i in 27017 27018 27019      do      mkdir -p /usr/local/mongodb/$i/{conf,data,logs,run}  done

配置文件

编辑一台配置文件,然后复制到其它实例,配置文件为yaml语法 关于以下配置文件详解,请参考:”https://abcops.cn/mongodb-conf/“

cat > mongod.conf << EOF  systemLog:    destination: file    path: /usr/local/mongodb/27017/logs/mongodb.log    logAppend: true    verbosity: 0    logRotate: rename storage:    journal:      enabled: true    dbPath: /usr/local/mongodb/27017/data    directoryPerDB: true    engine: wiredTiger    wiredTiger:      engineConfig:        cacheSizeGB: 1        directoryForIndexes: true        journalCompressor: zlib      collectionConfig:        blockCompressor: zlib      indexConfig:        prefixCompression: true  processManagement:    fork: true    pidFilePath: /usr/local/mongodb/27017/run/mongod.pid  net:    port: 27017    bindIp: 10.211.55.12    maxIncomingConnections: 65536    wireObjectCheck: true    ipv6: false  replication:    oplogSizeMB: 4096    replSetName: abcops_repl  setParameter:    connPoolMaxShardedConnsPerHost: 200    connPoolMaxConnsPerHost: 200  EOF

复制配置文件到目录中,并修改其端口及目录位置

#!/bin/bash  for i in 27017 27018 27019      do          \cp /home/mongod/mongod.conf /usr/local/mongodb/$i/conf          sed -i "s/27017/$i/g" /usr/local/mongodb/$i/conf/mongod.conf  done

启动MongoDB实例

启动脚本如下

#!/bin/bash  for i in 27017 27018 27019      do          /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/$i/conf/mongod.conf  done

关闭脚本如下

#!/bin/bash  for i in 27017 27018 27019      do          /usr/local/mongodb/bin/mongod --shutdown -f /usr/local/mongodb/$i/conf/mongod.conf  done

三个实例启动后,不代表复制集已经搭建成功了,还需要进行复制集初始化

配置复制集

连接任何一个实例都可以进行配置

这是我连接27017的实例  /usr/local/mongodb/bin/mongo --host 10.211.55.12 --port 27017
> use admin  > config={  _id:'abcops_repl',  members:[  {_id: 0, host: '10.211.55.12:27017',priority:2},  {_id: 1, host: '10.211.55.12:27018',priority:1},  {_id: 2, host: '10.211.55.12:27019',arbiterOnly:true},      ]  }  > rs.initiate(config)  #以上参数解析  use admin:进入admin数据库  config:配置复制集  _id:'abcops_repl':指定复制集组名称,与配置文件中的 replSetName 参数需要保持一致  members:规定的函数,不能更改  _id:设置组成员的ID编号,可以自定义,我这里为 0、1、2  host:指定加入复制集成员的IP地址及端口,我们在配置文件中指定了bindIp为10.211.55.12,这里就不能写127.0.0.1,必须写为bind_Ip指定的地址  priority:指定优先级0-100,优先级最高的成为Primary节点,优先级为可选选项,如果不指定默认都为1  arbiterOnly:是否开启仲裁节点true/false  rs.initiate(config):初始化复制集配置

以上参数图示如下,给你提供下参照

MongoDB中复制集集群的原理是什么

图中开始为SECONDARY,代表复制集集群正在进行选举Primary节点,大概5s左右,根据选举机制选举成功后,成为主节点的SECONDARY状态变为PRIMARY

复制集常用命令

1)查看谁是主节点

abcops_repl:PRIMARY> db.isMaster()

2)查看当前复制集集群中成员的配置

abcops_repl:PRIMARY> rs.conf()

3)查看复制集集群成员状态

abcops_repl:PRIMARY> rs.status()

4)新增节点到复制集 新增节点之前,该实例的配置中的replSetName复制集名称,必须和集群一致

abcops_repl:PRIMARY> rs.add("10.211.55.12:27020")

5)新增仲裁节点

abcops_repl:PRIMARY> rs.addArb("10.211.55.12:27020")

6)从复制集内删除节点

abcops_repl:PRIMARY> rs.remove("10.211.55.12:27020")

7)检查oplog日志时间和大小

abcops_repl:PRIMARY> rs.printReplicationInfo()   configured oplog size:   4096MB  log length start to end: 2422secs (0.67hrs)  oplog first event time:  Wed Sep 11 2019 12:22:13 GMT+0800 (CST)  oplog last event time:   Wed Sep 11 2019 13:02:35 GMT+0800 (CST)  now:                     Wed Sep 11 2019 13:02:37 GMT+0800 (CST)

8)降级服务器 此操作只能在PRIMARY上操作 通过执行rs.stepDown命令将当前主服务器主动降级为备用节点,120单位为s,为120秒内这个实力不能把自己选为PRIMARY角色,120秒后由于它本身的优先级较高,所以会重新抢占PRIMARY节点。

abcops_repl:PRIMARY> rs.stepDown(120)

9)允许在Secondary节点可以进行查询 在副本节点上操作

rs.slaveOk()

10)查看当前连接

db.getMongo()

修改优先级

修改27018的优先级为3,使其优先级超过27017实例,夺得PRIMARY角色,此操作需在PRIMARY上执行

abcops_repl:PRIMARY> config=rs.conf()  abcops_repl:PRIMARY> config.members[1].priority=3  3  abcops_repl:PRIMARY> rs.reconfig(config)  {      "ok" : 1,      "$clusterTime" : {          "clusterTime" : Timestamp(1568179129, 1),          "signature" : {              "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),              "keyId" : NumberLong(0)          }      },      "operationTime" : Timestamp(1568179129, 1)  } abcops_repl:PRIMARY> rs.conf()  abcops_repl:SECONDARY> db.isMaster()  #以上参数解析  config=rs.conf():将现有的配置读取到变量中进行存储  config.members[1].priority=3:修改变量中的值,1是指执行rs.conf()中看到节点的顺序,不是ID号哦,rs.conf()看到的顺序是从0开始排序,三个副本集排序就是0-3  rs.reconfig(config):将修改后的数据同步到配置,使修改生效  rs.conf():查看当前配置,可以看到优先级哦  db.isMaster():查看谁是Primary节点

复制测试

1)插入数据

#连接当前Primary节点  /usr/local/mongodb/bin/mongo --host 10.211.55.12 --port 27018  #进入abcops数据库,在documents文档中插入以下JSON格式的数据  abcops_repl:PRIMARY> use abcops  abcops_repl:PRIMARY> db.documents.insert(      {name: "xuweiliang",      age: 25,      Job: "DevOps"}  )  #查看documents文档中的数据  abcops_repl:PRIMARY> db.documents.find()  { "_id" : ObjectId("5d78863768fbf9eac4704232"), "name" : "xuweiliang", "age" : 25, "Job" : "DevOps" }  #查看复制节点状态  abcops_repl:PRIMARY> rs.printSlaveReplicationInfo()  source: 10.211.55.12:27017      syncedTo: Wed Sep 11 2019 13:30:42 GMT+0800 (CST)      0 secs (0 hrs) behind the primary

2)登录Secondary节点查看

/usr/local/mongodb/bin/mongo --host 10.211.55.12 --port 27017  abcops_repl:SECONDARY> rs.slaveOk()         #运行副本节点可进行查询  abcops_repl:SECONDARY> show dbs             #查看当前节点的数据库  abcops  0.000GB  admin   0.000GB  config  0.000GB local   0.000GB  abcops_repl:SECONDARY> use abcops               #abcops数据库已经从主节点同步至此  switched to db abcops  abcops_repl:SECONDARY> db.getCollectionNames()  #以下连续三个命令都是查看进入到abcops库中的文档命令  [ "documents" ]  abcops_repl:SECONDARY> show collections  documents  abcops_repl:SECONDARY> show tables  documents  abcops_repl:SECONDARY> db.documents.find()      #查看文档中的内容  { "_id" : ObjectId("5d78863768fbf9eac4704232"), "name" : "xuweiliang", "age" : 25, "Job" : "DevOps" }

创建复制集中的账户

1)连接到主节点,创建用户 以下创建的用户及权限和角色请参考下面用户权限说明

/usr/local/mongodb/bin/mongo --host 10.211.55.12 --port 27018  abcops_repl:PRIMARY> use admin  abcops_repl:PRIMARY> db.createUser( {          user:'abcops',          pwd:'123456',          roles:[ { role: "root", db: "admin" } ]      }  )  Successfully added user: {      "user" : "abcops",      "roles" : [          {              "role" : "root",              "db" : "admin"          }      ]  }  #查看所有创建的用户信息  abcops_repl:PRIMARY> show users

用户中权限的说明

权限说明
Read允许用户读取指定数据库
readWrite允许用户读写指定数据库
dbAdmin允许用户在指定数据库中指定管理函数,如(索引创建、删除、查看统计访问system.profile)
userAdmin允许用户向system.users集合写入,可以找指定数据里面创建、删除和管理用户
clusterAdmin只在admin数据库中可用,赋予用户所有分片和复制集相关函数的管理权限
readAnyDatabase只在admin数据库中可用,赋予用户所有数据库的读权限
readWriteAnyDatabase只在admin数据库中可用,赋予用户所有数据库的读写权限
userWriteAnyDatabase只在admin数据库中可用,赋予用户所有数据库的userAdmin权限
dbAdminAnyDatabase只在admin数据库中可用,赋予用户所有数据库的dbAdmin权限
root只在admin数据库中可用,超级管理员

为复制集集群添加权限认证

复制集我们这里采用keyfile文件实现权限认证,并且副本集中的所有成员使用的keyfile必须一样

添加安全认证配置

三台实例必须都要配置

cat >> /usr/local/mongodb/27017/conf/mongod.conf << EOF  security:    authorization: enabled    clusterAuthMode: keyFile    keyFile: /usr/local/mongodb/27017/conf/keyfile    javascriptEnabled: true  EOF   cat >> /usr/local/mongodb/27018/conf/mongod.conf << EOF security:    authorization: enabled    clusterAuthMode: keyFile    keyFile: /usr/local/mongodb/27018/conf/keyfile    javascriptEnabled: true  EOF  cat >> /usr/local/mongodb/27019/conf/mongod.conf << EOF  security:    authorization: enabled    clusterAuthMode: keyFile    keyFile: /usr/local/mongodb/27019/conf/keyfile    javascriptEnabled: true  EOF

keyfile文件操作

1)生产keyfile文件

openssl rand -base64 90 > ./keyfile

2)复制keyfile文件到其它实例中

#!/bin/bash  for i in 27017 27018 27019      do          \cp /home/mongod/keyfile /usr/local/mongodb/$i/conf/  done

3)修改keyfile权限 keyfile文件权限必须为 X00 ,不能给 group 和 other 成员分配任何权限,否则实例无法启动

#!/bin/bash  for i in 27017 27018 27019      do          chmod 400 /usr/local/mongodb/$i/conf/keyfile  done

4)重启所有实例

#!/bin/bash  for i in 27017 27018 27019      do          /usr/local/mongodb/bin/mongod --shutdown -f /usr/local/mongodb/$i/conf/mongod.conf          sleep 3s          /usr/local/mongodb/bin/mongod -f /usr/local/mongodb/$i/conf/mongod.conf  done

认证验证

登录验证可以在连接的时候指定用户名和密码,也可以先连接到数据库后再进行认证

1)登录指定用户密码

/usr/local/mongodb/bin/mongo --host 10.211.55.12 --port 27018 --username abcops -p 123456  abcops_repl:PRIMARY> show dbs  abcops  0.000GB  admin   0.000GB  config  0.000GB  local   0.000GB

2)先登录,后验证

abcops_repl:PRIMARY> use admin                          #必须要先切换到admin库中才可以进行进行验证  switched to db admin  abcops_repl:PRIMARY> db.auth( 'abcops','123456' )       #认证用户名及密码,认证成功返回1,否则返回0  1  abcops_repl:PRIMARY> show dbs       abcops  0.000GB  admin   0.000GB  config  0.000GB  local   0.000GB  abcops_repl:PRIMARY> db  admin

3)在备库进行验证 备库只能进行查询,勿要在备库上进行任何操作

/usr/local/mongodb/bin/mongo --host 10.211.55.12 --port 27017  abcops_repl:SECONDARY> rs.slaveOk()  abcops_repl:SECONDARY> use admin  switched to db admin  abcops_repl:SECONDARY> db.auth('abcops','123456')  1

客户端验证

我们可以找一台SQL管理工具来连接该库

MongoDB中复制集集群的原理是什么

可以看到我创建的abcops库和一个文档及三个字段

MongoDB中复制集集群的原理是什么

关于MongoDB中复制集集群的原理是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯