zoukankan      html  css  js  c++  java
  • Java对redis的基本操作

    Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。

     这里就不将redis是怎么安装和配置的了,大家自行在CSDN的其他博客看看,这个博客主要分享的是,Java使用jedis库去操作redis。本篇涉及了redis的5种基本数据类型的最基本操作:string、list、set、hash、zset,以及简单的redis消息队列的使用。

      jedis是官方推荐的,使用java操作redis的类库,目前官网的最新稳定版是2.9,推荐大家使用maven或者gradle去构建项目,maven所需的jedis的依赖如下

    1. <dependency>  
    2.     <groupId>redis.clients</groupId>  
    3.     <artifactId>jedis</artifactId>  
    4.     <version>2.9.0</version>  
    5. </dependency>  

       

      其实这个依赖只有3个jar包,jedis-2.9.0.jar,commons-pool-1.6.jar,commons-pool2-2.4.2.jar,后面的两个common的pool包,是配置jedis的连接池用的,如果不想使用连接池,那么只要导入jedis-2.9.0.jar就够了。

      不多说了,直接上代码吧,jedis的这些api,和redis-cli命令是一样的,所以只要熟悉redis-cli的命令,那个jedis的api就可以驾轻就熟了。


    1. import java.util.HashMap;  
    2. import java.util.List;  
    3. import java.util.Map;  
    4. import java.util.Set;  
    5.   
    6. import redis.clients.jedis.Jedis;  
    7. import redis.clients.jedis.JedisPool;  
    8. import redis.clients.jedis.JedisPoolConfig;  
    9. import redis.clients.jedis.JedisPubSub;  
    10. import redis.clients.jedis.ZParams;  
    11.   
    12. /** 
    13.  * redis常用api 
    14.  *  
    15.  * @author Kazz 
    16.  * 
    17.  */  
    18. public class RedisDemo {  
    19.   
    20.     private static JedisPool jedisPool = null;  
    21.   
    22.     public static void main(String[] args) throws Exception {  
    23.   
    24.         // 这个是最简单的redis连接示例,不过不推荐,推荐使用数据库连接池  
    25.         // Jedis jedis = new Jedis("192.168.8.128", 6379);// 连接 Redis 服务  
    26.         // jedis.auth("123456"); // 设置密码  
    27.         // System.out.println("Server is running: " + jedis.ping());//  
    28.         // 查看服务是否运行  
    29.   
    30.         init();  
    31.         string();  
    32.   
    33.         list();  
    34.           
    35.         set();  
    36.           
    37.         sets();  
    38.           
    39.         hash();  
    40.   
    41.         zset();  
    42.   
    43.         zsets();  
    44.   
    45.         publisher();  
    46.   
    47.         subscribe();  
    48.     }  
    49.   
    50.     /** 
    51.      * 初始化redis连接池 
    52.      */  
    53.     private static void init() {  
    54.         JedisPoolConfig config = new JedisPoolConfig(); // Jedis连接池  
    55.         config.setMaxIdle(8); // 最大空闲连接数  
    56.         config.setMaxTotal(8);// 最大连接数  
    57.         config.setMaxWaitMillis(1000); // 获取连接是的最大等待时间,如果超时就抛出异常  
    58.         config.setTestOnBorrow(false);// 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;  
    59.         config.setTestOnReturn(true);  
    60.         jedisPool = new JedisPool(config, "192.168.8.128"63795000"123456"0); // 配置、ip、端口、连接超时时间、密码、数据库编号(0~15)  
    61.     }  
    62.   
    63.     /** 
    64.      * string类型的基本操作,string是redis的最基本数据类型,很多操作都是其他数据类型能用的,如del、exists、expire 
    65.      *  
    66.      * @throws Exception 
    67.      *  
    68.      */  
    69.     private static void string() throws Exception {  
    70.         Jedis jedis = jedisPool.getResource();  
    71.         jedis.flushDB(); // 清空数据库  
    72.   
    73.         jedis.set("testString""123"); // 往redis中放入字符串  
    74.         System.out.println("从redis中获取刚刚放进去的testString:" + jedis.get("testString"));  
    75.   
    76.         jedis.incr("testString"); // 自增,不存在testInt则自增结果是1,如果不是字符串,自增会报JedisDataException  
    77.         System.out.println("从redis中获取自增后的testString:" + jedis.get("testString"));  
    78.   
    79.         jedis.decr("testString"); // 自减,效果同自增  
    80.         System.out.println("从redis中获取自减后的testString:" + jedis.get("testString"));  
    81.         // incrby方法可以自定要增加多少  
    82.   
    83.         jedis.append("testString""456abcd"); // 在后面追加  
    84.         System.out.println("从redis中获取追加后的testString:" + jedis.get("testString"));  
    85.   
    86.         String sub = jedis.substr("testString"26); // 切割字符串  
    87.         System.out.println("substr方法的返回值:" + sub);  
    88.         System.out.println("从redis中获取切割后的testString:" + jedis.get("testString")); // 可以看出,substr方法并不会破坏原有值,只是取出来加工而已  
    89.   
    90.         jedis.rename("testString""newString"); // 字段改名,值不会变  
    91.         System.out.println("testString改名成newString后,值为:" + jedis.get("newString"));  
    92.   
    93.         String type = jedis.type("newString");// 获取其数据类型  
    94.         System.out.println("newString的数据类型是:" + type);  
    95.   
    96.         long length = jedis.strlen("newString"); // 获取字符串长度  
    97.         System.out.println("newString的字符串长度为:" + length);  
    98.   
    99.         jedis.set("testString6""哈哈");  
    100.         jedis.set("testString7""呵呵");  
    101.         jedis.set("testString8""helloword");  
    102.         jedis.set("testString99""SMSP");  
    103.         Set<String> keys = jedis.keys("*"); // 获取所有符合条件的键  
    104.         System.out.println("返回redis中所有的键:" + keys);  
    105.         keys = jedis.keys("*String?");  
    106.         System.out.println("返回redis中所有正则符合*String?的键:" + keys);  
    107.   
    108.         jedis.del("testString"); // 字符串删除  
    109.         System.out.println("从redis删除testInt后,testInt是否还存在:" + jedis.exists("testString"));  
    110.         System.out.println();  
    111.   
    112.         jedis.set("testString2""你好啊!!!");  
    113.         jedis.expire("testString2"2); // 设置有效期,单位是秒  
    114.         System.out.println("从redis中获取testString2的值为:" + jedis.get("testString2"));  
    115.         Thread.sleep(3000);  
    116.         System.out.println("3秒后从redis中获取testString2的值为:" + jedis.get("testString2")); // 过期了,会找不到该字段,返回null  
    117.         // ttl方法可以返回剩余有效时间,expire如果方法不指定时间,就是将该字段有效期设为无限  
    118.         System.out.println();  
    119.         System.out.println();  
    120.         jedis.close();  
    121.     }  
    122.   
    123.     /** 
    124.      * list类的基本操作,有序可重复 
    125.      *  
    126.      */  
    127.     private static void list() {  
    128.         Jedis jedis = jedisPool.getResource();  
    129.         jedis.flushDB(); // 清空数据库  
    130.   
    131.         // 列表的插入与获取(可以重复)  
    132.         jedis.lpush("testList""Redis"); // 从左边插入  
    133.         jedis.lpush("testList""Mongodb");  
    134.         jedis.lpush("testList""Mysql");  
    135.         jedis.lpush("testList""Mysql");  
    136.         jedis.rpush("testList""DB2"); // 从右边插入  
    137.   
    138.         List<String> list = jedis.lrange("testList"0, -1); // 从左到右遍历,3个参数分别是,key,开始位置,结束位置(-1代表到最后)  
    139.         for (int i = 0; i < list.size(); i++) {  
    140.             System.out.printf("从redis中获取刚刚放进去的testList[%d]: %s ", i, list.get(i));  
    141.         }  
    142.   
    143.         System.out.println();  
    144.         String lpop = jedis.lpop("testList"); // 删掉最左边的那个  
    145.         String rpop = jedis.rpop("testList"); // 删掉最右边的那个  
    146.         System.out.printf("被删的左边元素是:%s,被删的右边元素是:%s ", lpop, rpop);  
    147.   
    148.         list = jedis.lrange("testList"0, -1);  
    149.         for (int i = 0; i < list.size(); i++) {  
    150.             System.out.printf("从redis中获取被删除后的testList[%d]: %s ", i, list.get(i));  
    151.         }  
    152.   
    153.         System.out.println();  
    154.         jedis.ltrim("testList"12); // 裁剪列表,三个参数分别是,key,开始位置,结束位置  
    155.         list = jedis.lrange("testList"0, -1);  
    156.         for (int i = 0; i < list.size(); i++) {  
    157.             System.out.printf("从redis中获取被裁剪后的testList[%d]: %s ", i, list.get(i));  
    158.         }  
    159.   
    160.         jedis.del("testList"); // 删除列表  
    161.         System.out.println("从redis删除testList后,testList是否还存在:" + jedis.exists("testList"));  
    162.         System.out.println();  
    163.         System.out.println();  
    164.         jedis.close();  
    165.     }  
    166.   
    167.     /** 
    168.      * 集合类型的基本操作,无序不重复 
    169.      */  
    170.     private static void set() {  
    171.         Jedis jedis = jedisPool.getResource();  
    172.         jedis.flushDB(); // 清空数据库  
    173.   
    174.         jedis.sadd("testSet""lida""wch""chf""lxl""wch"); // 添加元素,不可重复  
    175.   
    176.         Set<String> set = jedis.smembers("testSet"); // 获取集合中的全部元素  
    177.         System.out.println("从testSet中获取的元素:" + set);  
    178.   
    179.         long length = jedis.scard("testSet"); // 求集合的长度  
    180.         System.out.println(" 获取testSet的长度:" + length);  
    181.         System.out.println();  
    182.   
    183.         jedis.srem("testSet""wch"); // 从testSet移除wch  
    184.         set = jedis.smembers("testSet");  
    185.         System.out.println("从testSet中获取移除后的的元素:" + set);  
    186.         System.out.println();  
    187.   
    188.         boolean exist = jedis.sismember("testSet""lida"); // 判断元素是否包含在该集合中  
    189.         System.out.println("检查lida是否包含在testSet中:" + exist);  
    190.         System.out.println();  
    191.   
    192.         String spop = jedis.spop("testSet");// 随机的移除spop中的一个元素,并返回它  
    193.         System.out.println("testSet中被随机移除的元素是:" + spop);  
    194.         System.out.println();  
    195.   
    196.         jedis.del("testSet"); // 删除整个集合  
    197.         System.out.println("删除后,testSet是否还是存在:" + jedis.exists("testSet"));  
    198.         System.out.println();  
    199.         System.out.println();  
    200.   
    201.         jedis.close();  
    202.     }  
    203.   
    204.     /** 
    205.      * 集合之间的运算,交集、并集、差集 
    206.      */  
    207.     private static void sets() {  
    208.         Jedis jedis = jedisPool.getResource();  
    209.         jedis.flushDB(); // 清空数据库  
    210.         jedis.sadd("set1""a""b""c""d");  
    211.         jedis.sadd("set2""b""c""e");  
    212.   
    213.         Set<String> set = jedis.sdiff("set1""set2"); // 求两个集合的差集(只会返回存在于1,但2不存在的)  
    214.         System.out.println("求出两个集合之间的差集:" + set); // 会输出a和d  
    215.         // 还有一个sdiffstore的api,可以把sdiff的计算结果赋值到另一个set中,下面的交集和并集也类似  
    216.         System.out.println();  
    217.   
    218.         set = jedis.sinter("set1""set2"); // 求两个集合的交集  
    219.         System.out.println("求出两个集合之间的交集:" + set); // 会输出b和c  
    220.         System.out.println();  
    221.   
    222.         set = jedis.sunion("set1""set2"); // 求两个集合的并集  
    223.         System.out.println("求出两个集合之间的并集:" + set);  
    224.         System.out.println();  
    225.         System.out.println();  
    226.   
    227.         jedis.close();  
    228.     }  
    229.   
    230.     /** 
    231.      * 散列的基本操作,键值对里面还有键值对,经常用来存储多个字段信息,也可以理解为存放一个map,散列是redis的存储原型 
    232.      */  
    233.     private static void hash() {  
    234.         Jedis jedis = jedisPool.getResource();  
    235.         jedis.flushDB(); // 清空数据库  
    236.   
    237.         Map<String, String> map = new HashMap<String, String>();  
    238.         map.put("k1""v1");  
    239.         map.put("k2""v2");  
    240.         map.put("k3""v3");  
    241.         map.put("k4""123");  
    242.         jedis.hmset("hash1", map); // 存放一个散列  
    243.   
    244.         Map<String, String> getMap = jedis.hgetAll("hash1"); // 从redis中取回来  
    245.         System.out.println("从redis中取回的hash1散列:" + getMap.toString());  
    246.         System.out.println();  
    247.   
    248.         List<String> hmget = jedis.hmget("hash1""k1""k3"); // 从散列中取回一个或多个字段信息  
    249.         System.out.println("从hash1散列中两个字段来看看:" + hmget);  
    250.         System.out.println();  
    251.   
    252.         jedis.hdel("hash1""k1"); // 删除散列中的一个或者多个字段  
    253.         getMap = jedis.hgetAll("hash1");  
    254.         System.out.println("从redis中取回的被删除后的hash1散列:" + getMap);  
    255.         System.out.println();  
    256.   
    257.         long length = jedis.hlen("hash1"); // 求出集合的长度  
    258.         System.out.println("散列hash1的长度为:" + length);  
    259.         System.out.println();  
    260.   
    261.         boolean exists = jedis.hexists("hash1""k5"); // 判断某个字段是否存在于散列中  
    262.         System.out.println("k5字段是否存在于散列中:" + exists);  
    263.         System.out.println();  
    264.   
    265.         Set<String> keys = jedis.hkeys("hash1"); // 获取散列的所有字段名  
    266.         System.out.println("hash1的所有字段名:" + keys);  
    267.         System.out.println();  
    268.   
    269.         List<String> values = jedis.hvals("hash1"); // 获取散列的所有字段值,实质的方法实现,是用上面的hkeys后再用hmget  
    270.         System.out.println("hash1的所有字段值:" + values);  
    271.         System.out.println();  
    272.   
    273.         jedis.hincrBy("hash1""k4"10); // 给散列的某个字段进行加法运算  
    274.         System.out.println("执行加法运行后的hash1散列:" + jedis.hgetAll("hash1"));  
    275.         System.out.println();  
    276.   
    277.         jedis.del("hash1"); // 删除散列  
    278.         System.out.println("删除hash1后,hash1是否还存在redis中:" + jedis.exists("hash1"));  
    279.         System.out.println();  
    280.         System.out.println();  
    281.   
    282.         jedis.close();  
    283.     }  
    284.   
    285.     /** 
    286.      * 有序集合的基本使用,zset是set的升级版,在无序的基础上,加入了一个权重,使其有序化<br/> 
    287.      * 另一种理解,zset是hash的特殊版,一样的存放一些键值对,但这里的值只能是数字,不能是字符串<br/> 
    288.      * zset广泛应用于排名类的场景 
    289.      */  
    290.     private static void zset() {  
    291.         Jedis jedis = jedisPool.getResource();  
    292.         jedis.flushDB(); // 清空数据库  
    293.   
    294.         Map<String, Double> map = new HashMap<String, Double>();  
    295.         map.put("wch"24.3); // 这里以小组成员的年龄来演示  
    296.         map.put("lida"30.0);  
    297.         map.put("chf"23.5);  
    298.         map.put("lxl"22.1);  
    299.         map.put("wch"24.3); // 这个不会被加入,应该重复了  
    300.   
    301.         jedis.zadd("zset1", map); // 添加一个zset  
    302.   
    303.         Set<String> range = jedis.zrange("zset1"0, -1); // 从小到大排序,返回所有成员,三个参数:键、开始位置、结束位置(-1代表全部)  
    304.         // zrange方法还有很多衍生的方法,如zrangeByScore等,只是多了一些参数和筛选范围而已,比较简单,自己看看api就知道了  
    305.         System.out.println("zset返回的所有从小大到排序的成员:" + range);  
    306.         System.out.println("");  
    307.   
    308.         Set<String> revrange = jedis.zrevrange("zset1"0, -1); // 从大到小排序,类似上面的range  
    309.         System.out.println("zset返回的所有排序的成员:" + revrange);  
    310.         System.out.println("");  
    311.   
    312.         long length = jedis.zcard("zset1"); // 求有效长度  
    313.         System.out.println("zset1的长度:" + length);  
    314.         System.out.println();  
    315.   
    316.         long zcount = jedis.zcount("zset1"22.130.0); // 求出zset中,两个成员的排名之差,注意不是求长度,  
    317.         System.out.println("zset1中,22.1和30.0差了" + zcount + "名");  
    318.         System.out.println();  
    319.   
    320.         long zrank = jedis.zrank("zset1""wch"); // 求出zset中某成员的排位,注意第一是从0开始的  
    321.         System.out.println("wch在zset1中排名:" + zrank);  
    322.         System.out.println();  
    323.   
    324.         double zscore = jedis.zscore("zset1""lida"); // 获取zset中某成员的值  
    325.         System.out.println("zset1中lida的值为:" + zscore);  
    326.         System.out.println();  
    327.   
    328.         jedis.zincrby("zset1"10"lxl"); // 给zset中的某成员做加法运算  
    329.         System.out.println("zset1中lxl加10后,排名情况为:" + jedis.zrange("zset1"0, -1));  
    330.         System.out.println();  
    331.   
    332.         jedis.zrem("zset1""chf"); // 删除zset中某个成员  
    333.         // zrem还有衍生的zremByScore和zremByRank,分别是删除某个分数区间和排名区间的成员  
    334.         System.out.println("zset1删除chf后,剩下:" + jedis.zrange("zset1"0, -1));  
    335.         System.out.println();  
    336.   
    337.         jedis.close();  
    338.     }  
    339.   
    340.     /** 
    341.      * 有序集合的运算,交集、并集(最小、最大、总和) 
    342.      */  
    343.     private static void zsets() {  
    344.         Jedis jedis = jedisPool.getResource();  
    345.         jedis.flushDB(); // 清空数据库  
    346.   
    347.         Map<String, Double> map1 = new HashMap<String, Double>();  
    348.         map1.put("wch"24.3); // 这里以小组成员的年龄来演示  
    349.         map1.put("lida"30.0);  
    350.         map1.put("chf"23.5);  
    351.         map1.put("lxl"22.1);  
    352.   
    353.         Map<String, Double> map2 = new HashMap<String, Double>();  
    354.         map2.put("wch"24.3);  
    355.         map2.put("lly"29.6);  
    356.         map2.put("chf"23.5);  
    357.         map2.put("zjl"21.3);  
    358.   
    359.         jedis.zadd("zset1", map1);  
    360.         jedis.zadd("zset2", map2);  
    361.   
    362.         System.out.println("zset1的值有:" + jedis.zrangeWithScores("zset1"0, -1));  
    363.         System.out.println("zset2的值有:" + jedis.zrangeWithScores("zset2"0, -1));  
    364.         System.out.println();  
    365.   
    366.         jedis.zinterstore("zset_inter""zset1""zset2"); // 把两个集合进行交集运算,运算结果赋值到zset_inter中  
    367.         System.out.println("看看两个zset交集运算结果:" + jedis.zrangeWithScores("zset_inter"0, -1));  
    368.   
    369.         jedis.zunionstore("zset_union""zset1""zset2");// 把两个集合进行并集运算,运算结果赋值到zset_union中  
    370.         System.out.println("看看两个zset并集运算结果:" + jedis.zrangeWithScores("zset_union"0, -1));  
    371.         System.out.println("可以看出,zset的交集和并集计算,默认会把两个zset的score相加");  
    372.   
    373.         ZParams zParams = new ZParams();  
    374.         zParams.aggregate(ZParams.Aggregate.MAX);  
    375.         jedis.zinterstore("zset_inter", zParams, "zset1""zset2"); // 通过指定ZParams来设置集合运算的score处理,有MAX MIN SUM三个可以选择,默认是SUM  
    376.         System.out.println("看看两个zset交集max运算结果:" + jedis.zrangeWithScores("zset_inter"0, -1));  
    377.   
    378.         //zrangeWithScores返回的是一个Set<Tuple>类型,如果直接把这个集合打印出来,会把zset的key转成ascii码,看起来不直观,建议还是使用foreach之类的遍历会好看一些  
    379.           
    380.         jedis.close();  
    381.     }  
    382.   
    383.     /** 
    384.      * 发布消息,类似于mq的生产者 
    385.      */  
    386.     private static void publisher() {  
    387.   
    388.         new Thread() {  
    389.             public void run() {  
    390.                 try {  
    391.                     Thread.sleep(1000); // 休眠一下,让订阅者有充足的时间去连上  
    392.                     Jedis jedis = jedisPool.getResource();  
    393.                     jedis.flushAll();  
    394.   
    395.                     for (int i = 0; i < 10; i++) {  
    396.                         jedis.publish("channel""要发送的消息内容" + i); // 每隔一秒推送一条消息  
    397.                         System.out.printf("成功向channel推送消息:%s ", i);  
    398.                         Thread.sleep(1000);  
    399.                     }  
    400.   
    401.                     jedis.close();  
    402.   
    403.                 } catch (Exception e) {  
    404.                     e.printStackTrace();  
    405.                 }  
    406.   
    407.             };  
    408.         }.start();  
    409.     }  
    410.   
    411.     /** 
    412.      * 订阅消息,类似与mq的消费者 
    413.      *  
    414.      */  
    415.     private static void subscribe(){  
    416.         Jedis jedis = jedisPool.getResource();  
    417.         jedis.flushAll();  
    418.         JedisListener listener = new JedisListener();  
    419.         listener.proceed(jedis.getClient(), "channel"); // 开始监听channel频道的消息  
    420.         //listener.unsubscribe(); //取消监听  
    421.         jedis.close();  
    422.     }  
    423.   
    424.     /** 
    425.      * 重写监听器的一些重要方法,JedisPubSub里面的这些回调方法都是空的,不重写就什么事都不会发生 
    426.      *  
    427.      * @author Kazz 
    428.      * 
    429.      */  
    430.     private static class JedisListener extends JedisPubSub {  
    431.   
    432.         /** 
    433.          * 收到消息后的回调 
    434.          */  
    435.         @Override  
    436.         public void onMessage(String channel, String message) {  
    437.             System.out.println("onMessage: 收到频道[" + channel + "]的消息[" + message + "]");  
    438.         }  
    439.   
    440.         @Override  
    441.         public void onPMessage(String pattern, String channel, String message) {  
    442.             System.out.println("onPMessage: channel[" + channel + "], message[" + message + "]");  
    443.         }  
    444.   
    445.         /** 
    446.          * 成功订阅频道后的回调 
    447.          */  
    448.         @Override  
    449.         public void onSubscribe(String channel, int subscribedChannels) {  
    450.             System.out  
    451.                     .println("onSubscribe: 成功订阅[" + channel + "]," + "subscribedChannels[" + subscribedChannels + "]");  
    452.         }  
    453.   
    454.         /** 
    455.          * 取消订阅频道的回调 
    456.          */  
    457.         @Override  
    458.         public void onUnsubscribe(String channel, int subscribedChannels) {  
    459.             System.out.println(  
    460.                     "onUnsubscribe: 成功取消订阅[" + channel + "], " + "subscribedChannels[" + subscribedChannels + "]");  
    461.         }  
    462.   
    463.         @Override  
    464.         public void onPUnsubscribe(String pattern, int subscribedChannels) {  
    465.             System.out.println(  
    466.                     "onPUnsubscribe: pattern[" + pattern + "]," + "subscribedChannels[" + subscribedChannels + "]");  
    467.         }  
    468.   
    469.         @Override  
    470.         public void onPSubscribe(String pattern, int subscribedChannels) {  
    471.             System.out.println(  
    472.                     "onPSubscribe: pattern[" + pattern + "], " + "subscribedChannels[" + subscribedChannels + "]");  
    473.         }  
    474.   
    475.     }  

  • 相关阅读:
    一点优化小知识
    网站结构优化之一
    [JOISC 2016 Day 3] 电报
    [HDU 6157] The Karting
    [JOISC 2015 Day2] Keys
    Educational Codeforces Round 107 (Rated for Div. 2)
    [JOISC 2020 Day4] 治疗计划
    CF1131G Most Dangerous Shark
    [APIO2016] 划艇
    [ICPC World Finals 2018] 绿宝石之岛
  • 原文地址:https://www.cnblogs.com/xiang--liu/p/9710312.html
Copyright © 2011-2022 走看看