zoukankan      html  css  js  c++  java
  • Redis 安装、配置、集群

    1.   Redis的安装

    1.1. Redis的安装

    Redis是c语言开发的。

    安装redis需要c语言的编译环境。如果没有gcc需要在线安装。yum install gcc-c++

    安装步骤:

    第一步:redis的源码包上传到linux系统。

    第二步:解压缩redis。

    第三步:编译。进入redis源码目录。make

    第四步:安装。make install PREFIX=/usr/local/redis

    PREFIX参数指定redis的安装目录。一般软件安装到/usr目录下

    1.2. 连接redis

    1.2.1.    redis的启动:

    前端启动:在redis的安装目录下直接启动redis-server

    [root@localhost bin]# ./redis-server

    后台启动:

    把/root/redis-3.0.0/redis.conf复制到/usr/local/redis/bin目录下

    [root@localhost redis-3.0.0]# cp redis.conf /usr/local/redis/bin/

    修改配置文件:

     

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

    查看redis进程:

    [root@localhost bin]# ps aux|grep redis

    root      5190  0.1  0.3  33936  1712 ?        Ssl  18:23   0:00 ./redis-server *:6379   

    root      5196  0.0  0.1   4356   728 pts/0    S+   18:24   0:00 grep redis

    [root@localhost bin]#

    1.2.2.    Redis-cli

    [root@localhost bin]# ./redis-cli

    默认连接localhost运行在6379端口的redis服务。

    [root@localhost bin]# ./redis-cli -h 192.168.25.153 -p 6379

    -h:连接的服务器的地址

    -p:服务的端口号

    关闭redis:[root@localhost bin]# ./redis-cli shutdown

    1.3. Redis五种数据类型

    String:key-value(做缓存)

    Redis中所有的数据都是字符串。命令不区分大小写,key是区分大小写的。Redis是单线程的。Redis中不适合保存内容大的数据。

    get、set、

    incr:加一(生成id)

    Decr:减一

    Hash:key-fields-values(做缓存)

    相当于一个key对于一个map,map中还有key-value

    使用hash对key进行归类。

    Hset:向hash中添加内容

    Hget:从hash中取内容

    List:有顺序可重复

    192.168.25.153:6379> lpush list1 a b c d

    (integer) 4

    192.168.25.153:6379> lrange list1 0 -1

    1) "d"

    2) "c"

    3) "b"

    4) "a"

    192.168.25.153:6379> rpush list1 1 2 3 4

    (integer) 8

    192.168.25.153:6379> lrange list1 0 -1

    1) "d"

    2) "c"

    3) "b"

    4) "a"

    5) "1"

    6) "2"

    7) "3"

    8) "4"

    192.168.25.153:6379>

    192.168.25.153:6379> lpop list1

    "d"

    192.168.25.153:6379> lrange list1 0 -1

    1) "c"

    2) "b"

    3) "a"

    4) "1"

    5) "2"

    6) "3"

    7) "4"

    192.168.25.153:6379> rpop list1

    "4"

    192.168.25.153:6379> lrange list1 0 -1

    1) "c"

    2) "b"

    3) "a"

    4) "1"

    5) "2"

    6) "3"

    192.168.25.153:6379>

    Set:元素无顺序,不能重复

    192.168.25.153:6379> sadd set1 a b c c c d

    (integer) 4

    192.168.25.153:6379> smembers set1

    1) "b"

    2) "c"

    3) "d"

    4) "a"

    192.168.25.153:6379> srem set1 a

    (integer) 1

    192.168.25.153:6379> smembers set1

    1) "b"

    2) "c"

    3) "d"

    192.168.25.153:6379>

    还有集合运算命令,自学。

    SortedSet(zset):有顺序,不能重复

    192.168.25.153:6379> zadd zset1 2 a 5 b 1 c 6 d

    (integer) 4

    192.168.25.153:6379> zrange zset1 0 -1

    1) "c"

    2) "a"

    3) "b"

    4) "d"

    192.168.25.153:6379> zrem zset1 a

    (integer) 1

    192.168.25.153:6379> zrange zset1 0 -1

    1) "c"

    2) "b"

    3) "d"

    192.168.25.153:6379> zrevrange zset1 0 -1

    1) "d"

    2) "b"

    3) "c"

    192.168.25.153:6379> zrange zset1 0 -1 withscores

    1) "c"

    2) "1"

    3) "b"

    4) "5"

    5) "d"

    6) "6"

    192.168.25.153:6379> zrevrange zset1 0 -1 withscores

    1) "d"

    2) "6"

    3) "b"

    4) "5"

    5) "c"

    6) "1"

    192.168.25.153:6379>

    1.4. Key命令

    设置key的过期时间。

    Expire key second:设置key的过期时间

    Ttl key:查看key的有效期

    Persist key:清除key的过期时间。Key持久化。

    192.168.25.153:6379> expire Hello 100

    (integer) 1

    192.168.25.153:6379> ttl Hello

    (integer) 77

    2.   Redis的持久化方案

    Redis的所有数据都是保存到内存中的。

    Rdb:快照形式,定期把内存中当前时刻的数据保存到磁盘。Redis默认支持的持久化方案。

    aof形式:append only file。把所有对redis数据库操作的命令,增删改操作的命令。保存到文件中。数据库恢复时把所有的命令执行一遍即可。

    在redis.conf配置文件中配置。

    Rdb:

     

    Aof的配置:

     

    两种持久化方案同时开启使用aof文件来恢复数据库。

    3.   Redis集群的搭建

    3.1. redis-cluster架构图



    redis-cluster投票:容错

     

    架构细节:

    (1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.

    (2)节点的fail是通过集群中超过半数的节点检测失效时才生效.

    (3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可

    (4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->value

    Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点

     

    3.2. Redis集群的搭建

    Redis集群中至少应该有三个节点。要保证集群的高可用,需要每个节点有一个备份机。

    Redis集群至少需要6台服务器。

    搭建伪分布式。可以使用一台虚拟机运行6个redis实例。需要修改redis的端口号7001-7006

    3.2.1.    集群搭建环境

    1、使用ruby脚本搭建集群。需要ruby的运行环境。

    安装ruby

    yum install ruby

    yum install rubygems

    2、安装ruby脚本运行使用的包。

    [root@localhost ~]# gem install redis-3.0.0.gem

    Successfully installed redis-3.0.0

    1 gem installed

    Installing ri documentation for redis-3.0.0...

    Installing RDoc documentation for redis-3.0.0...

    [root@localhost ~]#

    [root@localhost ~]# cd redis-3.0.0/src

    [root@localhost src]# ll *.rb

    -rwxrwxr-x. 1 root root 48141 Apr  1  2015 redis-trib.rb

    3.2.2.    搭建步骤

    需要6台redis服务器。搭建伪分布式。

    需要6个redis实例。

    需要运行在不同的端口7001-7006

    第一步:创建6个redis实例,每个实例运行在不同的端口。需要修改redis.conf配置文件。配置文件中还需要把cluster-enabled yes前的注释去掉。

     

    第二步:启动每个redis实例。

    第三步:使用ruby脚本搭建集群。

    ./redis-trib.rb create --replicas 1 192.168.25.153:7001 192.168.25.153:7002 192.168.25.153:7003 192.168.25.153:7004 192.168.25.153:7005 192.168.25.153:7006

    创建关闭集群的脚本:

    [root@localhost redis-cluster]# vim shutdow-all.sh

    redis01/redis-cli -p 7001 shutdown

    redis01/redis-cli -p 7002 shutdown

    redis01/redis-cli -p 7003 shutdown

    redis01/redis-cli -p 7004 shutdown

    redis01/redis-cli -p 7005 shutdown

    redis01/redis-cli -p 7006 shutdown

    [root@localhost redis-cluster]# chmod u+x shutdow-all.sh

    [root@localhost redis-cluster]# ./redis-trib.rb create --replicas 1 192.168.25.153:7001 192.168.25.153:7002 192.168.25.153:7003 192.168.25.153:7004 192.168.25.153:7005  192.168.25.153:7006

    >>> Creating cluster

    Connecting to node 192.168.25.153:7001: OK

    Connecting to node 192.168.25.153:7002: OK

    Connecting to node 192.168.25.153:7003: OK

    Connecting to node 192.168.25.153:7004: OK

    Connecting to node 192.168.25.153:7005: OK

    Connecting to node 192.168.25.153:7006: OK

    >>> Performing hash slots allocation on 6 nodes...

    Using 3 masters:

    192.168.25.153:7001

    192.168.25.153:7002

    192.168.25.153:7003

    Adding replica 192.168.25.153:7004 to 192.168.25.153:7001

    Adding replica 192.168.25.153:7005 to 192.168.25.153:7002

    Adding replica 192.168.25.153:7006 to 192.168.25.153:7003

    M: 2e48ae301e9c32b04a7d4d92e15e98e78de8c1f3 192.168.25.153:7001

       slots:0-5460 (5461 slots) master

    M: 8cd93a9a943b4ef851af6a03edd699a6061ace01 192.168.25.153:7002

       slots:5461-10922 (5462 slots) master

    M: 2935007902d83f20b1253d7f43dae32aab9744e6 192.168.25.153:7003

       slots:10923-16383 (5461 slots) master

    S: 74f9d9706f848471583929fc8bbde3c8e99e211b 192.168.25.153:7004

       replicates 2e48ae301e9c32b04a7d4d92e15e98e78de8c1f3

    S: 42cc9e25ebb19dda92591364c1df4b3a518b795b 192.168.25.153:7005

       replicates 8cd93a9a943b4ef851af6a03edd699a6061ace01

    S: 8b1b11d509d29659c2831e7a9f6469c060dfcd39 192.168.25.153:7006

       replicates 2935007902d83f20b1253d7f43dae32aab9744e6

    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.25.153:7001)

    M: 2e48ae301e9c32b04a7d4d92e15e98e78de8c1f3 192.168.25.153:7001

       slots:0-5460 (5461 slots) master

    M: 8cd93a9a943b4ef851af6a03edd699a6061ace01 192.168.25.153:7002

       slots:5461-10922 (5462 slots) master

    M: 2935007902d83f20b1253d7f43dae32aab9744e6 192.168.25.153:7003

       slots:10923-16383 (5461 slots) master

    M: 74f9d9706f848471583929fc8bbde3c8e99e211b 192.168.25.153:7004

       slots: (0 slots) master

       replicates 2e48ae301e9c32b04a7d4d92e15e98e78de8c1f3

    M: 42cc9e25ebb19dda92591364c1df4b3a518b795b 192.168.25.153:7005

       slots: (0 slots) master

       replicates 8cd93a9a943b4ef851af6a03edd699a6061ace01

    M: 8b1b11d509d29659c2831e7a9f6469c060dfcd39 192.168.25.153:7006

       slots: (0 slots) master

       replicates 2935007902d83f20b1253d7f43dae32aab9744e6

    [OK] All nodes agree about slots configuration.

    >>> Check for open slots...

    >>> Check slots coverage...

    [OK] All 16384 slots covered.

    [root@localhost redis-cluster]#

    3.3. 集群的使用方法

    Redis-cli连接集群。

    [root@localhost redis-cluster]# redis01/redis-cli -p 7002 -c

    -c:代表连接的是redis集群

    4.   Jedis

    需要把jedis依赖的jar包添加到工程中。Maven工程中需要把jedis的坐标添加到依赖。

    推荐添加到服务层。E3-content-Service工程中。

    4.1. 连接单机版

    第一步:创建一个Jedis对象。需要指定服务端的ip及端口。

    第二步:使用Jedis对象操作数据库,每个redis命令对应一个方法。

    第三步:打印结果。

    第四步:关闭Jedis

    @Test

         public void testJedis() throws Exception {

              // 第一步:创建一个Jedis对象。需要指定服务端的ip及端口。

              Jedis jedis = new Jedis("192.168.25.153", 6379);

              // 第二步:使用Jedis对象操作数据库,每个redis命令对应一个方法。

              String result = jedis.get("hello");

              // 第三步:打印结果。

              System.out.println(result);

              // 第四步:关闭Jedis

              jedis.close();

         }

    4.2. 连接单机版使用连接池

    第一步:创建一个JedisPool对象。需要指定服务端的ip及端口。

    第二步:从JedisPool中获得Jedis对象。

    第三步:使用Jedis操作redis服务器。

    第四步:操作完毕后关闭jedis对象,连接池回收资源。

    第五步:关闭JedisPool对象。

    @Test

         public void testJedisPool() throws Exception {

              // 第一步:创建一个JedisPool对象。需要指定服务端的ip及端口。

              JedisPool jedisPool = new JedisPool("192.168.25.153", 6379);

              // 第二步:从JedisPool中获得Jedis对象。

              Jedis jedis = jedisPool.getResource();

              // 第三步:使用Jedis操作redis服务器。

              jedis.set("jedis", "test");

              String result = jedis.get("jedis");

              System.out.println(result);

              // 第四步:操作完毕后关闭jedis对象,连接池回收资源。

              jedis.close();

              // 第五步:关闭JedisPool对象。

              jedisPool.close();

         }

    4.3. 连接集群版

    第一步:使用JedisCluster对象。需要一个Set<HostAndPort>参数。Redis节点的列表。

    第二步:直接使用JedisCluster对象操作redis。在系统中单例存在。

    第三步:打印结果

    第四步:系统关闭前,关闭JedisCluster对象。

    @Test

         public void testJedisCluster() throws Exception {

              // 第一步:使用JedisCluster对象。需要一个Set<HostAndPort>参数。Redis节点的列表。

              Set<HostAndPort> nodes = new HashSet<>();

              nodes.add(new HostAndPort("192.168.25.153", 7001));

              nodes.add(new HostAndPort("192.168.25.153", 7002));

              nodes.add(new HostAndPort("192.168.25.153", 7003));

              nodes.add(new HostAndPort("192.168.25.153", 7004));

              nodes.add(new HostAndPort("192.168.25.153", 7005));

              nodes.add(new HostAndPort("192.168.25.153", 7006));

              JedisCluster jedisCluster = new JedisCluster(nodes);

              // 第二步:直接使用JedisCluster对象操作redis。在系统中单例存在。

              jedisCluster.set("hello", "100");

              String result = jedisCluster.get("hello");

              // 第三步:打印结果

              System.out.println(result);

              // 第四步:系统关闭前,关闭JedisCluster对象。

              jedisCluster.close();

         }

    5.   向业务逻辑中添加缓存

    5.1. 接口封装

    常用的操作redis的方法提取出一个接口,分别对应单机版和集群版创建两个实现类。

    5.1.1.    接口定义

    public interface JedisClient {

     

         String set(String key, String value);

         String get(String key);

         Boolean exists(String key);

         Long expire(String key, int seconds);

         Long ttl(String key);

         Long incr(String key);

         Long hset(String key, String field, String value);

         String hget(String key, String field);

         Long hdel(String key, String... field);

    }

    5.1.2.    单机版实现类

    public class JedisClientPool implements JedisClient {

        

         @Autowired

         private JedisPool jedisPool;

     

         @Override

         public String set(String key, String value) {

              Jedis jedis = jedisPool.getResource();

              String result = jedis.set(key, value);

              jedis.close();

              return result;

         }

     

         @Override

         public String get(String key) {

              Jedis jedis = jedisPool.getResource();

              String result = jedis.get(key);

              jedis.close();

              return result;

         }

     

         @Override

         public Boolean exists(String key) {

              Jedis jedis = jedisPool.getResource();

              Boolean result = jedis.exists(key);

              jedis.close();

              return result;

         }

     

         @Override

         public Long expire(String key, int seconds) {

              Jedis jedis = jedisPool.getResource();

              Long result = jedis.expire(key, seconds);

              jedis.close();

              return result;

         }

     

         @Override

         public Long ttl(String key) {

              Jedis jedis = jedisPool.getResource();

              Long result = jedis.ttl(key);

              jedis.close();

              return result;

         }

     

         @Override

         public Long incr(String key) {

              Jedis jedis = jedisPool.getResource();

              Long result = jedis.incr(key);

              jedis.close();

              return result;

         }

     

         @Override

         public Long hset(String key, String field, String value) {

              Jedis jedis = jedisPool.getResource();

              Long result = jedis.hset(key, field, value);

              jedis.close();

              return result;

         }

     

         @Override

         public String hget(String key, String field) {

              Jedis jedis = jedisPool.getResource();

              String result = jedis.hget(key, field);

              jedis.close();

              return result;

         }

     

         @Override

         public Long hdel(String key, String... field) {

              Jedis jedis = jedisPool.getResource();

              Long result = jedis.hdel(key, field);

              jedis.close();

              return result;

         }

     

    }

    配置:applicationContext-redis.xml

    <?xml version="1.0" encoding="UTF-8"?>

    <beans xmlns="http://www.springframework.org/schema/beans"

         xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"

         xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"

         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans4.2.xsd

         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context4.2.xsd

         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx4.2.xsd

         http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util4.2.xsd">

     

         <!-- 配置单机版的连接 -->

         <bean id="jedisPool" class="redis.clients.jedis.JedisPool">

              <constructor-arg name="host" value="192.168.25.153"></constructor-arg>

              <constructor-arg name="port" value="6379"></constructor-arg>

         </bean>

         <bean id="jedisClientPool" class="cn.e3mall.jedis.JedisClientPool"/>

        

    </beans>

    5.1.3.    集群版实现类

    package cn.e3mall.jedis;

     

    import org.springframework.beans.factory.annotation.Autowired;

     

    import redis.clients.jedis.JedisCluster;

     

    public class JedisClientCluster implements JedisClient {

        

         @Autowired

         private JedisCluster jedisCluster;

     

         @Override

         public String set(String key, String value) {

              return jedisCluster.set(key, value);

         }

     

         @Override

         public String get(String key) {

              return jedisCluster.get(key);

         }

     

         @Override

         public Boolean exists(String key) {

              return jedisCluster.exists(key);

         }

     

         @Override

         public Long expire(String key, int seconds) {

              return jedisCluster.expire(key, seconds);

         }

     

         @Override

         public Long ttl(String key) {

              return jedisCluster.ttl(key);

         }

     

         @Override

         public Long incr(String key) {

              return jedisCluster.incr(key);

         }

     

         @Override

         public Long hset(String key, String field, String value) {

              return jedisCluster.hset(key, field, value);

         }

     

         @Override

         public String hget(String key, String field) {

              return jedisCluster.hget(key, field);

         }

     

         @Override

         public Long hdel(String key, String... field) {

              return jedisCluster.hdel(key, field);

         }

     

    }

    Spring的配置:

    <!-- 集群版的配置 -->

         <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">

              <constructor-arg>

                   <set>

                        <bean class="redis.clients.jedis.HostAndPort">

                             <constructor-arg name="host" value="192.168.25.153"></constructor-arg>

                             <constructor-arg name="port" value="7001"></constructor-arg>

                        </bean>

                        <bean class="redis.clients.jedis.HostAndPort">

                             <constructor-arg name="host" value="192.168.25.153"></constructor-arg>

                             <constructor-arg name="port" value="7002"></constructor-arg>

                        </bean>

                        <bean class="redis.clients.jedis.HostAndPort">

                             <constructor-arg name="host" value="192.168.25.153"></constructor-arg>

                             <constructor-arg name="port" value="7003"></constructor-arg>

                        </bean>

                        <bean class="redis.clients.jedis.HostAndPort">

                             <constructor-arg name="host" value="192.168.25.153"></constructor-arg>

                             <constructor-arg name="port" value="7004"></constructor-arg>

                        </bean>

                        <bean class="redis.clients.jedis.HostAndPort">

                             <constructor-arg name="host" value="192.168.25.153"></constructor-arg>

                             <constructor-arg name="port" value="7005"></constructor-arg>

                        </bean>

                        <bean class="redis.clients.jedis.HostAndPort">

                             <constructor-arg name="host" value="192.168.25.153"></constructor-arg>

                             <constructor-arg name="port" value="7006"></constructor-arg>

                        </bean>

                   </set>

              </constructor-arg>

         </bean>

         <bean id="jedisClientCluster" class="cn.e3mall.jedis.JedisClientCluster"/>

    注意:单机版和集群版不能共存,使用单机版时注释集群版的配置。使用集群版,把单机版注释。

    5.2. 封装代码测试

    @Test

         public void testJedisClient() throws Exception {

              //初始化Spring容器

              ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-redis.xml");

              //从容器中获得JedisClient对象

              JedisClient jedisClient = applicationContext.getBean(JedisClient.class);

              jedisClient.set("first", "100");

              String result = jedisClient.get("first");

              System.out.println(result);

             

                       

         }

    5.3. 添加缓存

    5.3.1.    功能分析

    查询内容列表时添加缓存。

    1、查询数据库之前先查询缓存。

    2、查询到结果,直接响应结果。

    3、查询不到,缓存中没有需要查询数据库。

    4、把查询结果添加到缓存中。

    5、返回结果。

    向redis中添加缓存:

    Key:cid

    Value:内容列表。需要把java对象转换成json。

    使用hash对key进行归类。

    HASH_KEY:HASH

                |--KEY:VALUE

                |--KEY:VALUE

                |--KEY:VALUE

                |--KEY:VALUE

    注意:添加缓存不能影响正常业务逻辑。

    5.3.2.    代码实现

    @Override

         public List<TbContent> getContentList(long cid) {

              //查询缓存

              try {

                   String json = jedisClient.hget(CONTENT_KEY, cid + "");

                   //判断json是否为空

                   if (StringUtils.isNotBlank(json)) {

                        //把json转换成list

                        List<TbContent> list = JsonUtils.jsonToList(json, TbContent.class);

                        return list;

                   }

              } catch (Exception e) {

                   e.printStackTrace();

              }

              //根据cid查询内容列表

              TbContentExample example = new TbContentExample();

              //设置查询条件

              Criteria criteria = example.createCriteria();

              criteria.andCategoryIdEqualTo(cid);

              //执行查询

              List<TbContent> list = contentMapper.selectByExample(example);

              //向缓存中添加数据

              try {

                   jedisClient.hset(CONTENT_KEY, cid + "", JsonUtils.objectToJson(list));

              } catch (Exception e) {

                   e.printStackTrace();

              }

              return list;

         }

    5.4. 缓存同步

    对内容信息做增删改操作后只需要把对应缓存删除即可。

    可以根据cid删除。

    @Override

         public E3Result addContent(TbContent content) {

              //补全属性

              content.setCreated(new Date());

              content.setUpdated(new Date());

              //插入数据

              contentMapper.insert(content);

              //缓存同步

              jedisClient.hdel(CONTENT_KEY, content.getCategoryId().toString());

             

              return E3Result.ok();

         }

  • 相关阅读:
    Representation Data in OpenCascade BRep
    Render OpenCascade Geometry Surfaces in OpenSceneGraph
    Render OpenCascade Geometry Curves in OpenSceneGraph
    OpenCascade Shape Representation in OpenSceneGraph
    Geometry Surface of OpenCascade BRep
    Geometry Curve of OpenCascade BRep
    Tyvj2017清北冬令营入学测试
    Spfa算法模板
    洛谷1016 旅行家的预算
    洛谷1290 欧几里得的游戏
  • 原文地址:https://www.cnblogs.com/limn/p/9391088.html
Copyright © 2011-2022 走看看