• Redis高可用策略与集群方案(主从复制、哨兵、Cluster集群)-实践篇


    引言:本篇文章以笔者亲身实践过程来总结和记录Redis的主从复制、哨兵故障转移、集群等内容,避免单纯的理论性知识分享,以具体操作实践来引导来学习的朋友们,希望为大家提供有力的支持与帮助。

    文章目录:

    Redis环境搭建

    Redis主从复制

    Redis哨兵模式

    Redis集群模式

     

    Redis环境搭建

    Redis作为NoSQL体系中的最具有代表性的数据库之一,是一款高性能的key-value数据库,概况的讲具有以下特点:

    • 支持数据持久化,可将内存数据保存到硬盘,重启后将从硬盘中再次加载到内容
    • 支持数据类型String、List、Set、Zset、Hash、HyperLogLog 。
    • 支持数据备份、(Master-Slave)主从复制、(Cluster)集群模式
    • 读取速度高达8万-10万次每秒。
    • 支持publish/subscribe,key过期等特性、支持事务。

    1.1 下载Redis

    官网地址:https://redis.io/

    中文官网地址:http://www.redis.cn

    下载地址:http://download.redis.io/releases/

    1.2  安装Redis

    下载版本redis-4.0.11.tar.gz,此版本是需要先编译、再安装。

    解压:tar -zxvf redis-4.0.11.tar.gz

    切换目录:cd redis-4.0.11

    编译安装:make install PREFIX=/usr/local/redis (记住可不能写成小写prefix)

    完整的步骤命令:

    [root@hadoop01 modules]# tar -zxvf redis-4.0.11.tar.gz
    [root@hadoop01 modules]# cd redis-4.0.11
    [root@hadoop01 modules]# make install PREFIX=/usr/local/redis

    安装成功后在/usr/local/目录下有redis:

    进入redis目录中,会看到redis的一些操作命令工具:

    Redis操作命令工具

    redis-server

    启动redis服务

    redis-cli

    进入redis命令客户端

    redis-benchmark

    性能测试的工具

    redis-check-aof

    aof文件进行检查的工具

    redis-check-dump

    rdb文件进行检查的工具

    redis-sentinel

    启动哨兵监控服务

     

    1.3 启动Redis

    启动命令:./redis-server redis.conf

    [root@hadoop01 bin]# ./redis-server redis.conf

    启动成功后控制台信息:

     

    2   Redis主从复制

    Redis的持久化保证了Redis重启也不会丢失数据,因为Redis在存储时会将内存中的数据存储到硬盘上,当重启后会再次将硬盘上的数据加载到内存中;但是如果硬盘有损坏的话,可能会照成数据丢失,那么通过Redis的主从复制可以避免这种单点故障。如图:

     

    Redis主从模式

    说明:Master中的数据有两个副本Slave1Slave2,其中任何一台Redis宕机,其他两台任然能继续提供服务。MasterSlave的数据实时保持同步,当Master写入数据后会复制到Slave1Slave2

    2.1   配置主从

    这里我们采用一主两从的模式,配备3台机器,分别安装好Redis,具体分配入下表:

    服务类型

    服务器角色

    IP

    端口

    Redis

    Master

    192.168.100.129

    6379

    Redis

    Slave1

    192.168.100.130

    6379

    Redis

    Slave2

    192.168.100.131

    6379

    Master配置文件redis.conf:

    redis服务器可以跨网络访问,将默认的127.0.0.1修改为0.0.0.0

    bind 0.0.0.0

    设定Redis密码

    # requirepass foobared
      requirepass redis123

    Slave配置文件redis.conf:(两台Slave设置相同)

    redis服务器可以跨网络访问,将默认的127.0.0.1修改为0.0.0.0

    bind 0.0.0.0

    设定Redis密码

    # requirepass foobared
      requirepass redis123

    设置Slave登录 Master的密码,此处为Master的密码

    # masterauth <master-password>
      masterauth redis123

    设置所属的主机的IP和端口

    # slaveof <masterip> <masterport>
      slaveof 192.168.100.129 6379

    2.2    启动主从模式

     启动Master:

    启动slave1:

    从控制台信息中可以看到,Slave1成功连接Maser-192.168.100.129,并接收到Master的同步数据。

    启动slave2:

    同样,Slave2也成功连接Maser-192.168.100.129,并接收到Master的同步数据。

    查看Master:

    再回头看一下Master,当两台Slave都启动之后,控制台信息告诉我们Slave1-192.168.100.130、Slave2-192.168.100.131均同步成功。

    2.3    验证主从复制同步

    Master中写入k1

    [root@hadoop01 ~]# cd /usr/local/redis/bin/
    [root@hadoop01 bin]# ./redis-cli 
    127.0.0.1:6379> AUTH redis123
    OK
    127.0.0.1:6379> set k1 vv1
    OK
    127.0.0.1:6379> get k1
    "vv1"
    127.0.0.1:6379>

    Slave1中读取k1

    [root@hadoop02 ~]# cd /usr/local/redis/bin/
    [root@hadoop02 bin]# ./redis-cli 
    127.0.0.1:6379> AUTH redis123
    OK
    127.0.0.1:6379> set k1 vv1
    OK
    127.0.0.1:6379> get k1
    "vv1"
    127.0.0.1:6379>

    Slave2中读取k1

    [root@hadoop03 ~]# cd /usr/local/redis/bin/
    [root@hadoop03 bin]# ./redis-cli 
    127.0.0.1:6379> AUTH redis123
    OK
    127.0.0.1:6379> set k1 vv1
    OK
    127.0.0.1:6379> get k1
    "vv1"
    127.0.0.1:6379>

    2.4    优缺点

    优点:主从结构模式实现了读写分离,较单机又读又写来说,提高读写操作的性能。

    缺点:当其中的任何一台Master或Slave宕机,无法实现自动处理故障。利用Redis-Sentinel哨兵模式可以完美的进行自动故障转移。

     3   Redis哨兵模式

    3.1   Redis Sentinel介绍

    Redis Sentinel是Redis HA 的实现方案,Sentinel是一个可以对Redis进行监控、通知、故障转移的工具。

    3.2   哨兵模式配置

    本例中架构模式设置了三台虚拟机,分别为一台作为Redis主节点,另外两台作为Redis从节点;其中开启三个Redis Sentinel来监控各节点的状态,在其中的主节点宕机后,会自动的故障转移,从另外两台从节点中选出一个作为主节点,替代旧的主节点的工作。具体机器的分配如下表:

    服务类型

    服务器角色

    IP

    端口

    Redis

    Master

    192.168.100.129

    6379

    Redis

    Slave1

    192.168.100.130

    6379

    Redis

    Slave2

    192.168.100.131

    6379

    Sentinel

    -

    192.168.100.129

    26379

    Sentinel

    -

    192.168.100.130

    26379

    Sentinel

    -

    192.168.100.131

    26379

    Master主节点配置文件redis.conf:

    bind 0.0.0.0 //可以跨网络访问
    protected-mode no  //默认为yes,只允许127.0.0.1访问
    requirepass redis123 //Redis的密码
    masterauth redis123  //主节点的密码,此处可以不设置;设置的好处是当Master宕机后,Sentinel会自动完成故障转移,再次启动旧的Msater后,它会以Slave的身份自动连接新的Master。

    Slave从节点配置文件redis.conf:

    bind 0.0.0.0 
    protected-mode no   
    requirepass redis123
    masterauth redis123
    slaveof 192.168.100.129 6379 //设置所属的Master的IP和端口

    哨兵配置文件sentinel.conf,三个哨兵节点相同:

    protected-mode no   //保护模式
    sentinel monitor mymaster 192.168.100.129 6379 2
    sentinel auth-pass mymaster redis123 //master的密码
    sentinel down-after-milliseconds mymaster 10000 //节点认为主观下线的有效时间
    sentinel failover-timeout mymaster 60000 //故障转移超时时间*毫秒
    sentinel parallel-syncs mymaster 1 //设定同步新主机的节点数

    特别注意:一定要将配置文件中redis.conf和sentinel.conf的protected-mode设置为no,否则哨兵无法正常的完成故障转移切换新的主节点

    哨兵模式下的一些配置说明:

    配置项

    参数

    作用

    Port

    整数

    哨兵进程端口

    Dir

    文件目录

    哨兵进程服务文件夹,默认为/tmp,要保证有写权限

    sentinel down-after-milliseconds <master-name> <milliseconds>

    <服务名称><毫秒数(整数)>

    指定哨兵在监测Redis服务时,当Redis服务在一个亳秒数内都无法回答时,单个哨兵认为的主观下线时间,默认为 30000(30秒)

    sentinel parallel-syncs <master-name> <numslaves>

    <服务名称><服务器数(整数)>

    指定可以有多少 Redis 服务同步新的主机,一般而言,这个数字越 小同步时间就越长,而越大,则对网络资源要求则越高

    sentinel failover-timeout <master-name> <milliseconds>

    <服务名称><亳秒数(整数)>

    指定在故障切换允许的亳秒数,当超过这个亳秒数的时候,就认为 切换故障失败,默认为 3 分钟

    sentinel notification-script <master-name> <script-path>

    <服务名称><脚本路径>

    指定 sentinel 检测到该监控的 redis 实例指向的实例异常时,调用的 报警脚本。该配置项可选,比较常用

    3.3   启动哨兵监控

    按先Master后Slave顺序启动各节点:

    ./redis-server redis.conf

    启动3个哨兵监控:

    ./redis-sentinel sentinel.conf

    解释一下这些信息的意思:

    Sentinel ID is 4318def4c053269e7750e9452147e04b80dc7f77
    //哨兵的ID标识
    
    +monitor master mymaster 192.168.100.129 6379 quorum 2
    //增加了对Master -192.168.100.129的监控, 主观下线的判定数量为2或大于2。
    
    +slave slave 192.168.100.130:6379 192.168.100.130 6379 @ mymaster 192.168.100.129 6379
    //增加了对slave的监控,后面为该slave-192.168.100.130的信息
    
    +slave slave 192.168.100.131:6379 192.168.100.131 6379 @ mymaster 192.168.100.129 6379
    //增加了对slave的监控,后面为该slave-192.168.100.131的信息
    
    +sentinel sentinel 45ccdd2b184adcc047762cf455ebeb3e711b0825 192.168.100.130 26379 @ mymaster 192.168.100.129 6379
    //增加了哨兵节点192.168.100.130,标识ID,信息显示其对主节点192.168.100.129的监控。
    
    +sentinel sentinel 217fd238092167299274673707c6bcec2283ca17 192.168.100.131 26379 @ mymaster 192.168.100.129 6379
    //增加了哨兵节点192.168.100.131,标识ID,信息显示其对主节点192.168.100.129的监控。

    3.4   验证哨兵的故障转移能力

    为了验证哨兵模式下自动故障转移的能力,先将Master节点断掉,模拟宕机。然后查看一下Slave1-192.168.100.130节点的情况:出现连接Master失败,随后以Master的身份与Slave2-192.168.100.131同步成功。

    以上说明哨兵自动完成故障转移,将Slave1-192.168.100.130的节点选举为新的Master节点,这是刚刚当选主节点Master-192.168.100.130的信息,经过这样一个故障迁移的过程:

    旧Master主观下线—>投票->旧Master客观下线—>新Master产生—>通知所有节点

    查看下Slave1-192.168.100.130的信息,发现当前节点的角色已经变成Master。

     

    接下来重启一下刚刚宕机的那台旧的Master节点,发现它成功同步连接到新的Master-192.168.100.130,此时它是作为一个Slave的节点存在。

    可以查看一下节点192.168.100.129的信息,发现它已经变成一个Slave的角色了。

     

    3.5    优缺点

    优点:读写分离,解决单点故障,自动完成故障转移。

    缺点: 在读写高并发状况下,主节点单点写入,无法实现单点扩容,虽然只解决高可用的问题,不能完美的体现高可用的性能。解决这一问题,就要引入Redis Cluster集群来解决。

    4  Redis-Cluster集群

    Redis的集群需要通过集群管理脚本redis-trib.rb来运行,而Redis5之前的版本需要依赖Ruby环境,我们当前版本是Redis4,所以需要安装Ruby。

    4.1   安装Ruby

    yum install ruby  //安装ruby,为了可以运行redis-trib.rb
    yum install rubygems  //安装gem工具包
    gem install redis  //安装ruby与redis的接口程序

    gem是管理Ruby库和程序的标准包,通过rubygems(https://rubygems.org/)源来查找、安装、升级、卸载软件包,非常方便。

    4.2   安装Redis Cluster集群

    Redis Cluster最少需要3个节点,1个Master,2个Slave。例用两台虚拟机模式6个节点:一台机器3个节点,创建出3个Master、3个Slave。所有节点在开始并不指定具体的Master-Slave关系,而是在创建完成集群后会指定哪些节点是Master,哪些节点是Slave。

    服务类型

    IP

    端口

    Redis-Node

    192.168.100.129

    7000

    Redis-Node

    192.168.100.129

    7001

    Redis-Node

    192.168.100.129

    7002

    Redis-Node

    192.168.100.130

    7001

    Redis-Node

    192.168.100.130

    7002

    Redis-Node

    192.168.100.130

    7003

    4.3   创建Redis节点

    1、在IP为192.168.100.129的机器上/opt/modules/redis-4.0.11目录下创建redis-cluster目录:

     [root@hadoop01 redis-4.0.11]# mkdir redis-cluster

    在redis-cluster目录下创建名为7000、7001、7002的目录

    [root@hadoop01 redis-4.0.11]# cd redis-cluster/
    [root@hadoop01 redis-cluster]# mkdir 7000 7001 7002

    2、将redis.conf拷贝到三个目录中。

    [root@hadoop01 redis-cluster]# cp ../redis.conf ./7000/
    [root@hadoop01 redis-cluster]# cp ../redis.conf ./7001/
    [root@hadoop01 redis-cluster]# cp ../redis.conf ./7002/

    指定数据文件存放目录:

    [root@hadoop01 opt]# mkdir redis-data/

    3、修改节点配置文件redis.conf:

    目录文件:/opt/modules/redis-4.0.11/redis-cluster/7000/redis.conf

    port 7000   //端口 700070017002
    bind 0.0.0.0  //默认127.0.0.1,设置可以跨网络访问
    daemonize yes  //后台运行
    pidfile /var/run/redis_7000.pid  
    cluster-enabled yes  //开启集群模式
    cluster-config-file  /opt/modules/redis-4.0.11/redis-cluster/7000/nodes-7000.conf  //集群配置,配置文件首次启动自动生成
    cluster-node-timeout 15000 //请求超时时间*毫秒,默认15秒
    appendonly yes  //aof日志
    dir /opt/redis-data/  数据文件的目录

    同样配置:

    vim /opt/modules/redis-4.0.11/redis-cluster/7001/redis.conf
    vim /opt/modules/redis-4.0.11/redis-cluster/7002/redis.conf

    在另一台机器192.168.100.130上重复以上的3个步骤,以完成节点7000、7001、7002的配置。

    4.4  启动集群节点

    在机器192.168.100.129上执行:

    [root@hadoop01 src]# redis-server ../redis-cluster/7000/redis.conf
    [root@hadoop01 src]# redis-server ../redis-cluster/7001/redis.conf
    [root@hadoop01 src]# redis-server ../redis-cluster/7002/redis.conf

    在另一台机器192.168.100.130上执行:

    [root@hadoop02 src]# ./redis-server ../redis-cluster/7000/redis.conf
    [root@hadoop02 src]# ./redis-server ../redis-cluster/7001/redis.conf
    [root@hadoop02 src]# ./redis-server ../redis-cluster/7002/redis.conf

    4.5   检查节点运行状态

    检查机器192.168.100.129 Redis节点运行:

    [root@hadoop01 src]# ps -ef |grep redis
    root       5054      1  0 00:51 ?        00:00:00 redis-server 0.0.0.0:7000 [cluster]          
    root       5059      1  0 00:51 ?        00:00:00 redis-server 0.0.0.0:7001 [cluster]          
    root       5081      1  0 00:57 ?        00:00:00 redis-server 0.0.0.0:7002 [cluster]          
    root       5088   5031  0 00:57 pts/1    00:00:00 grep redis

    查看端口监听状态:

    [root@hadoop01 src]# netstat -tlnp |grep redis
    tcp        0      0 0.0.0.0:17000               0.0.0.0:*                   LISTEN      5054/redis-server 0 
    tcp        0      0 0.0.0.0:17001               0.0.0.0:*                   LISTEN      5059/redis-server 0 
    tcp        0      0 0.0.0.0:17002               0.0.0.0:*                   LISTEN      5081/redis-server 0 
    tcp        0      0 0.0.0.0:7000                0.0.0.0:*                   LISTEN      5054/redis-server 0 
    tcp        0      0 0.0.0.0:7001                0.0.0.0:*                   LISTEN      5059/redis-server 0 
    tcp        0      0 0.0.0.0:7002                0.0.0.0:*                   LISTEN      5081/redis-server 0 

    检查另一台机器192.168.100.130 Redis各节点运行状态:

    4.6   创建集群

     执行命令:

    ./redis-trib.rb  create --replicas 1 192.168.100.129:7000 192.168.100.129:7001 192.168.100.129:7002 192.168.100.130:7000 192.168.100.130:7001 192.168.100.130:7002

    [root@hadoop01 src]# ./redis-trib.rb  create --replicas 1 192.168.100.129:7000 192.168.100.129:7001 192.168.100.129:7002 192.168.100.130:7000 192.168.100.130:7001 192.168.100.130:7002
    >>> Creating cluster
    >>> Performing hash slots allocation on 6 nodes...
    Using 3 masters:
    192.168.100.129:7000
    192.168.100.130:7000
    192.168.100.129:7001
    Adding replica 192.168.100.130:7002 to 192.168.100.129:7000
    Adding replica 192.168.100.129:7002 to 192.168.100.130:7000
    Adding replica 192.168.100.130:7001 to 192.168.100.129:7001
    M: c16510616904828d6fe93c36b37c54b6b7457423 192.168.100.129:7000
       slots:0-5460 (5461 slots) master
    M: 6d9ee8662293b622f696952fb4e75ea8d06650f1 192.168.100.129:7001
       slots:10923-16383 (5461 slots) master
    S: 1bab0e61aff928a0265201c64fd4c9193761c081 192.168.100.129:7002
       replicates adcb60cc28d035928636473ed9c2c873c5ec8dad
    M: adcb60cc28d035928636473ed9c2c873c5ec8dad 192.168.100.130:7000
       slots:5461-10922 (5462 slots) master
    S: db757a83be0fe89e5082a3cfcb145c41c8f5e429 192.168.100.130:7001
       replicates 6d9ee8662293b622f696952fb4e75ea8d06650f1
    S: ce1c2b9521c8d3a3dd18cd6ae585dbfec5d6448b 192.168.100.130:7002
       replicates c16510616904828d6fe93c36b37c54b6b7457423
    Can I set the above configuration? (type 'yes' to accept): yes
    >>> Nodes configuration updated
    >>> Assign a different config epoch to each node
    >>> Sending CLUSTER MEET messages to join the cluster
    Waiting for the cluster to join....
    >>> Performing Cluster Check (using node 192.168.100.129:7000)
    M: c16510616904828d6fe93c36b37c54b6b7457423 192.168.100.129:7000
       slots:0-5460 (5461 slots) master
       1 additional replica(s)
    S: 1bab0e61aff928a0265201c64fd4c9193761c081 192.168.100.129:7002
       slots: (0 slots) slave
       replicates adcb60cc28d035928636473ed9c2c873c5ec8dad
    M: 6d9ee8662293b622f696952fb4e75ea8d06650f1 192.168.100.129:7001
       slots:10923-16383 (5461 slots) master
       1 additional replica(s)
    M: adcb60cc28d035928636473ed9c2c873c5ec8dad 192.168.100.130:7000
       slots:5461-10922 (5462 slots) master
       1 additional replica(s)
    S: ce1c2b9521c8d3a3dd18cd6ae585dbfec5d6448b 192.168.100.130:7002
       slots: (0 slots) slave
       replicates c16510616904828d6fe93c36b37c54b6b7457423
    S: db757a83be0fe89e5082a3cfcb145c41c8f5e429 192.168.100.130:7001
       slots: (0 slots) slave
       replicates 6d9ee8662293b622f696952fb4e75ea8d06650f1
    [OK] All nodes agree about slots configuration.
    >>> Check for open slots...
    >>> Check slots coverage...
    [OK] All 16384 slots covered.

    出现下面的说明集群创建成功。

    参考https://blog.csdn.net/qq_39244264/article/details/80277484

    4.7   验证集群

    使用客户端连接到redis集群,命令: ./redis-cli -h host -p port –c

    这里我们连接192.168.100.129:7000这个节点,-c表示连接redis集群。

    ./redis-cli -h 192.168.100.129 -p 7000 –c

    然后创建k1=v1数据写入位于192.168.100.129:7000节的槽12706位置。

    创建k2=v2, 数据写入位于192.168.100.129:7000节的槽449位置。

    创建k3=v3, 数据写入到本节点。

    [root@hadoop03 src]# ./redis-cli -h 192.168.100.129 -p 7000 -c
    192.168.100.129:7000> set k1 v1
    -> Redirected to slot [12706] located at 192.168.100.129:7001
    OK
    192.168.100.129:7001> set k2 v2
    -> Redirected to slot [449] located at 192.168.100.129:7000
    OK
    192.168.100.129:7000> get k1
    -> Redirected to slot [12706] located at 192.168.100.129:7001
    "v1"
    192.168.100.129:7001> get k2
    -> Redirected to slot [449] located at 192.168.100.129:7000
    "v2"
    192.168.100.129:7000> set k3 v3
    OK
    192.168.100.129:7000> get k3
    "v3"

    4.8   Redis集群常用命令

    //集群(cluster)

    CLUSTER INFO 打印集群的信息

    CLUSTER NODES 列出集群当前已知的所有节点(node),以及这些节点的相关信息。

    //节点(node)

    CLUSTER MEET <ip> <port> 将 ip 和 port 所指定的节点添加到集群当中,让它成为集群的节点。

    CLUSTER FORGET <node_id> 从集群中移除 node_id 指定的节点。

    CLUSTER REPLICATE <node_id> 将当前节点设置为 node_id 指定的节点的从节点。

    CLUSTER SAVECONFIG 将节点的配置文件保存到硬盘里面。

    //槽(slot)

    CLUSTER ADDSLOTS <slot> [slot ...] 将一个或多个槽(slot)指派(assign)给当前节点。

    CLUSTER DELSLOTS <slot> [slot ...] 移除一个或多个槽对当前节点的指派。

    CLUSTER FLUSHSLOTS 移除指派给当前节点的所有槽,让当前节点变成一个没有指派任何槽的节点。

    CLUSTER SETSLOT <slot> NODE <node_id> 将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给另一个节点,那么先让另一个节点删除该槽>,然后再进行指派。

    CLUSTER SETSLOT <slot> MIGRATING <node_id> 将本节点的槽 slot 迁移到 node_id 指定的节点中。

    CLUSTER SETSLOT <slot> IMPORTING <node_id> 从 node_id 指定的节点中导入槽 slot 到本节点。

    CLUSTER SETSLOT <slot> STABLE 取消对槽 slot 的导入(import)或者迁移(migrate)。

    //键 (key)

    CLUSTER KEYSLOT <key> 计算键 key 应该被放置在哪个槽上。

    CLUSTER COUNTKEYSINSLOT <slot> 返回槽 slot 目前包含的键值对数量。

    CLUSTER GETKEYSINSLOT <slot> <count> 返回 count 个 slot 槽中的键。

    4.9   Redis集群原理

    Redis Cluster在设计中的每个节点都是平等的,每个节点都保存各自的数据和整个集群的状态。每个节点和其他节点都保持连接,这就保证了我们只需要连接集群中的任何一个节点,就可以获取到其他任何节点上存放的数据了。

    Redis Cluster没有使用传统的哈希来存储数据,而是采用一种叫做哈希槽(hash slot)的存储方式来分配的,共分配了16348个哈希槽。当 set一个key时,会用CRC16算法取模得到所属的slot,然后将这个key分配到哈希槽区间的节点上,算法为:CRC16(key)% 16348。所以刚刚在例子中set 和get  k1、k2的时候,跳转到了192.168.100.129:7001节点上。

    Redis Cluster会把数据存在一个Master节点,然后这个Master节点和其他的Slave节点进行数据同步。当Master宕机后,会选举一个Slave替代Master。

    参考:

    https://blog.csdn.net/qq_39244264/article/details/80277484

    https://www.cnblogs.com/wuxl360/p/5920330.html

     

    感谢技术圈的朋友们常来常往,希望我的每一个分享能帮助到大家,如果帮助到了您,请给我留下点点的评论或关注,我也想和您成为朋友,感谢大家一起交流一起进步
  • 相关阅读:
    cf Round 633
    Django学习手册
    Django学习手册
    Django学习手册
    Django学习手册
    Django学习手册
    Django学习手册
    ERROR CL .exe……错误
    DLL、lib等链接库文件的使用
    HTTP协议
  • 原文地址:https://www.cnblogs.com/fengguozhong/p/12192690.html
Copyright © 2020-2023  润新知