zoukankan      html  css  js  c++  java
  • redis连接数高居不下,怎么破?。。。。这么破

          最近项目一直在使用redis,首次用redis,随便从网上找了例子就用了,一开始用的还挺正常,后来发现,当客户端访问量一上来,redis的连接数居高不下,一开始以为是客户端没有关闭,开始怀疑redis-pool有问题,就修改了/etc/redis/6379.cnf中的timeout=5(默认是0,服务端不主动关闭连接),修改之后发现close_wait批量出现。

          经过分析,肯定不是redis的问题了,肯定是自己代码有的逻辑是没有关闭redis的,经过排查,果然有很多redis因为逻辑关系没有关闭,所以建议大家如果并发量不是很大的话,还是直接在操作redis的时候重新获取redis,然后关闭redis即可,这样就不会出现没有关闭redis的情况了。

         对了,使用的redis版本也要对应呦,比如服务端装的是3.0.2,引入的jedis.jar的版本就不能太低,之前我们用的2.1,就经常出现类型转换错误(其实代码中的转换没有问题),我们换成最新的2.7之后,就不报类型转换错误了。

      1 package com.jovision.redisDao;
      2 
      3 
      4 import java.util.List;
      5 import java.util.Map;
      6 import java.util.Set;
      7 
      8 import org.apache.log4j.Logger;
      9 
     10 import redis.clients.jedis.Jedis;
     11 import redis.clients.jedis.JedisPool;
     12 import redis.clients.jedis.JedisPoolConfig;
     13 
     14 import com.jovision.Exception.RedisException;
     15 import com.jovision.system.ConfigBean;
     16 /**
     17  * 
     18  * @Title: redisFactory.java 
     19  * @Package com.jovision.redisDao
     20  * @author Joker(张凯)
     21  * @Description: TODO() 
     22  * @date 2015-9-30 上午11:49:09
     23  */
     24 public class redisFactory {
     25     private static Logger logger = Logger.getLogger(redisFactory.class); 
     26     
     27     public static ConfigBean configBean;
     28     public static String redisIP ;
     29     public static String redisPort ;
     30     public static JedisPool pool;
     31     static
     32     {
     33         //连接redis,连接失败时抛异常
     34         try 
     35         {
     36             configBean = ConfigBean.getInstace();
     37             redisIP = configBean.getRedisIP();
     38             redisPort = configBean.getRedisPort();
     39             if (pool == null)
     40             {
     41                 JedisPoolConfig config = new JedisPoolConfig();
     42                 config.setMaxIdle(10);
     43                 config.setMaxTotal(200);
     44                 config.setMaxWaitMillis(1000 * 10);
     45                 config.setTestOnBorrow(true);
     46                 pool = new JedisPool(config, redisIP, Integer.parseInt(redisPort),10000);
     47              }
     48         } 
     49         catch (Exception e)
     50         {
     51             e.printStackTrace();
     52             logger.error("redis配置异常", e);
     53             //throw new RedisException("redis异常!");
     54         }
     55         
     56     }
     57     
     58     /**
     59      * 初始化Redis连接池
     60      *//*
     61     private static void initialPool(){
     62         try {
     63             configBean = ConfigBean.getInstace();
     64             redisIP = configBean.getRedisIP();
     65             redisPort = configBean.getRedisPort();
     66             JedisPoolConfig config = new JedisPoolConfig();
     67             config.setMaxTotal(10);
     68             config.setMaxIdle(5);
     69             config.setMaxWaitMillis(1000*10);
     70             config.setTestOnBorrow(true);
     71             pool = new JedisPool(config,redisIP, Integer.parseInt(redisPort));
     72         } catch (Exception e) {
     73             logger.error("create JedisPool error : "+e);
     74         }
     75     }
     76     
     77     public synchronized static Jedis getJedis() {  
     78         if (pool == null) {  
     79             poolInit();
     80         }
     81         Jedis jedis = null;
     82         try {  
     83             if (pool != null) {  
     84                 jedis = pool.getResource(); 
     85             }
     86         } catch (Exception e) {  
     87             logger.error("Get jedis error : "+e);
     88         }finally{
     89             returnResource(pool, jedis);
     90         }
     91         System.out.println("pool:---------------------------------"+pool);
     92         return jedis;
     93     }  
     94     
     95     *//**
     96      * 在多线程环境同步初始化
     97      *//*
     98     private static synchronized void poolInit() {
     99         if (pool == null) {  
    100             initialPool();
    101         }
    102     }*/
    103     
    104     public static void main(String[] args) throws Exception {
    105         /*System.out.println(redisIP);
    106         setSingleData("123","123");
    107         System.out.println(getSingleData("123"));*/
    108         //put2Set(0,"test11", "123","456","789");
    109         //System.out.println(getOneSetData("test","123"));
    110         //dev_type dev_version dev_username dev_password
    111         /*hset(8, "B176218924", "dev_type", "2");
    112         hset(8, "B176218924", "dev_version", "v1.1");
    113         hset(8, "B176218924", "dev_username", "abc");
    114         hset(8, "B176218924", "dev_password", "123");
    115         hset(8, "B176218924", "dev_nickname", "B176218924");*/
    116         //setTimeOut(0, "zk", 100);
    117         
    118         
    119         
    120         /*for(int i=0;i<20;i++)
    121         {
    122             
    123             new Thread(new Runnable() {
    124                 public void run() {
    125                         
    126                     // TODO Auto-generated method stub
    127                     for(int j=0;j<100000;j++) {
    128                         try {
    129                             Jedis jedis = redisFactory.getJedis();
    130                             jedis.close();
    131                             //pool.returnResource(jedis);
    132                             System.out.println(jedis.isConnected());
    133                         } catch (Exception e) {
    134                             e.printStackTrace();
    135                         }
    136                     }
    137                     
    138                 }
    139             }).start();
    140         }
    141         for(int i=0;i<100;i++)
    142         {
    143             
    144             //Jedis jedis = redisFactory.getJedis();
    145             Jedis jedis = new Jedis(redisIP, Integer.parseInt(redisPort),10000);
    146             System.out.println(jedis);
    147             jedis.close();
    148             System.out.println("jedis.isConnected()-------------"+jedis.isConnected());
    149         }*/
    150         System.out.println(System.currentTimeMillis());
    151     }
    152     
    153     /**
    154      * 返还到连接池
    155      * 
    156      * @param pool 
    157      * @param redis
    158      */
    159     public static void returnResource(JedisPool pool, Jedis redis) {
    160         if (redis != null) {
    161             pool.returnResource(redis);
    162         }
    163     }
    164     
    165     
    166     /**
    167      * 将数据存到集合
    168      * 
    169      * @param key
    170      * @return
    171      */
    172     public static boolean put2Set(int index,String key , String... value){
    173         Jedis jedis = null;
    174         try {
    175             jedis = pool.getResource();
    176             jedis.select(index);
    177             jedis.sadd(key, value);
    178             return true;
    179         } catch (Exception e) {
    180             //失败就返回jedis
    181             pool.returnBrokenResource(jedis);
    182             e.printStackTrace();
    183             return false;
    184         } finally {
    185             //释放jedis资源
    186             returnResource(pool, jedis);
    187         }
    188         
    189     }
    190     
    191     
    192    /**
    193     * 
    194     * @author Hellon(刘海龙) 
    195     * @param jedis
    196     * @param index
    197     * @param key
    198     * @param value
    199     * @return
    200     */
    201     public static boolean put2Set(Jedis jedis,int index,String key , String... value ){
    202         try {
    203             jedis.select(index);
    204             jedis.sadd(key, value);
    205             return true;
    206         } catch (Exception e) {
    207             //失败就返回jedis
    208             e.printStackTrace();
    209             return false;
    210         } 
    211         
    212     }
    213     
    214     /**
    215      * @Title: 带jedis和有效期
    216      * @Package com.jovision.redisDao
    217      * @author Joker(张凯)
    218      * @Description: TODO() 
    219      * @date 2015-11-18 下午02:42:04
    220      * @param jedis
    221      * @param seconds
    222      * @param index
    223      * @param key
    224      * @param value
    225      * @return
    226      */
    227     public static boolean put2Set(Jedis jedis,int seconds,int index,String key , String... value ){
    228         try {
    229             jedis.select(index);
    230             jedis.sadd(key, value);
    231             jedis.expire(key, seconds);
    232             return true;
    233         } catch (Exception e) {
    234             //失败就返回jedis
    235             e.printStackTrace();
    236             return false;
    237         } 
    238         
    239     }
    240     
    241     /**
    242      * 获取集合数据
    243      * 
    244      * @param key
    245      * @return
    246      * @throws RedisException 
    247      */
    248     public static Set<String> getSet(int index,String key) throws RedisException{
    249         Jedis jedis = null;
    250         try {
    251             jedis = pool.getResource();
    252             jedis.select(index);
    253             return jedis.smembers(key);
    254         } catch (Exception e) {
    255             //失败就返回jedis
    256             pool.returnBrokenResource(jedis);
    257             //e.printStackTrace();
    258             logger.error("getSet异常", e);
    259             throw new RedisException("redis异常!"+e.getMessage());
    260         } finally {
    261             //释放jedis资源
    262             returnResource(pool, jedis);
    263         }
    264     }
    265     
    266     
    267     public static Set<String> getSet(Jedis jedis,int index,String key) {
    268         try {
    269             jedis.select(index);
    270             return jedis.smembers(key);
    271         } catch (Exception e) {
    272             logger.error("getSet异常", e);
    273         } 
    274         return null;
    275     }
    276     
    277     /**
    278      * @Title: hget
    279      * @Package com.jovision.redisDao
    280      * @author Joker(张凯)
    281      * @Description: TODO() 
    282      * @date 2015-9-30 上午11:44:58
    283      * @param index
    284      * @param key
    285      * @param field
    286      * @return
    287      * @throws RedisException
    288      */
    289     public static String hget(int index,String key,String field) throws RedisException{
    290         Jedis jedis = null;
    291         try {
    292             jedis = pool.getResource();
    293             jedis.select(index);
    294             return jedis.hget(key, field);
    295         } catch (Exception e) {
    296             //失败就返回jedis
    297             pool.returnBrokenResource(jedis);
    298             //e.printStackTrace();
    299             logger.error("getSet异常", e);
    300             throw new RedisException("redis异常!"+e.getMessage());
    301         } finally {
    302             //释放jedis资源
    303             returnResource(pool, jedis);
    304         }
    305     }
    306     
    307     public static String hget(Jedis jedis,int index,String key,String field){
    308         try {
    309             jedis.select(index);
    310             return jedis.hget(key, field);
    311         } catch (Exception e) {
    312             logger.error(e, e);
    313         }
    314          return null;
    315     }
    316     
    317     /**
    318      * @Title: hset 
    319      * @Package com.jovision.redisDao
    320      * @author Joker(张凯)
    321      * @Description: TODO() 
    322      * @date 2015-9-30 上午11:45:06
    323      * @param index
    324      * @param key
    325      * @param field
    326      * @param value
    327      * @throws RedisException
    328      */
    329     public static void hset(int index,String key,String field,String value) throws RedisException{
    330         Jedis jedis = null;
    331         try {
    332             jedis = pool.getResource();
    333             jedis.select(index);
    334             jedis.hset(key, field,value);
    335         } catch (Exception e) {
    336             //失败就返回jedis
    337             pool.returnBrokenResource(jedis);
    338             //e.printStackTrace();
    339             logger.error("getSet异常", e);
    340             throw new RedisException("redis异常!"+e.getMessage());
    341         } finally {
    342             //释放jedis资源
    343             returnResource(pool, jedis);
    344         }
    345     }
    346     
    347     public static void hset(Jedis jedis,int index,String key,String field,String value) {
    348         try {
    349             jedis.select(index);
    350             jedis.hset(key, field,value);
    351         } catch (Exception e) {
    352             logger.error(e,e);
    353         } 
    354     }
    355     
    356     /**
    357      * @Title: 带jedis和seconds
    358      * @Package com.jovision.redisDao
    359      * @author Joker(张凯)
    360      * @Description: TODO() 
    361      * @date 2015-11-18 下午02:45:09
    362      * @param jedis
    363      * @param seconds
    364      * @param index
    365      * @param key
    366      * @param field
    367      * @param value
    368      */
    369     public static void hset(Jedis jedis,int seconds,int index,String key,String field,String value) {
    370         try {
    371             jedis.select(index);
    372             jedis.hset(key, field,value);
    373             jedis.expire(key, seconds);
    374         } catch (Exception e) {
    375             logger.error(e,e);
    376         } 
    377     }
    378     
    379     /**
    380      * 获取单个数据
    381      * 
    382      * @param key
    383      * @return
    384      */
    385     public static String getSingleData(int index,String key){
    386         Jedis jedis = null;
    387         try {
    388             jedis = pool.getResource();
    389             jedis.select(index);
    390             return jedis.get(key);
    391         } catch (Exception e) {
    392             //失败就返回jedis
    393             pool.returnBrokenResource(jedis);
    394             e.printStackTrace();
    395         } finally {
    396             //释放jedis资源
    397             returnResource(pool, jedis);
    398         }
    399         return null;
    400         
    401     }
    402     
    403     public static String getSingleData(Jedis jedis,int index,String key){
    404         try {
    405             jedis = pool.getResource();
    406             jedis.select(index);
    407             return jedis.get(key);
    408         } catch (Exception e) {
    409             //失败就返回jedis
    410           logger.error(e,e);
    411         }
    412          return null;
    413     }
    414     
    415     /**
    416      * 存入单个简单数据
    417      * 
    418      * @param key
    419      * @return
    420      */
    421     public static boolean setSingleData(int index,String key ,String value){
    422         Jedis jedis = null;
    423         try {
    424             jedis = pool.getResource();
    425             jedis.select(index);
    426             jedis.set(key, value);
    427             return true;
    428         } catch (Exception e) {
    429             //失败就返回jedis
    430             pool.returnBrokenResource(jedis);
    431             e.printStackTrace();
    432             return false;
    433         } finally {
    434             //释放jedis资源
    435             returnResource(pool, jedis);
    436         }
    437         
    438     }
    439     
    440     public static void setSingleData(Jedis jedis,int seconds,int index,String key ,String value){
    441         try {
    442             jedis.select(index);
    443             jedis.set(key, value);
    444             jedis.expire(key, seconds);
    445         } catch (Exception e) {
    446             logger.error(e,e);
    447         } 
    448         
    449     }
    450     
    451     /**
    452      * 删除set中单个value
    453      * 
    454      * @param key
    455      * @return
    456      */
    457     public static boolean del1SetValue(int index,String key ,String value){
    458         Jedis jedis = null;
    459         try {
    460             jedis = pool.getResource();
    461             jedis.select(index);
    462             jedis.srem(key, value);  
    463             return true;
    464         } catch (Exception e) {
    465             pool.returnBrokenResource(jedis);
    466             e.printStackTrace();
    467             return false;
    468         } finally {
    469             returnResource(pool, jedis);
    470         }
    471         
    472     }
    473     
    474     
    475     public static boolean del1SetValue(Jedis jedis,int index,String key ,String value){
    476         try {
    477             jedis.select(index);
    478             jedis.srem(key, value);  
    479             return true;
    480         } catch (Exception e) {
    481             logger.error(e,e);
    482             return false;
    483         } 
    484     }
    485     
    486     /**
    487      * 删除key对应整个set
    488      * 
    489      * @param key
    490      * @return
    491      */
    492     public static boolean del(int index ,String key){
    493         Jedis jedis = null;
    494         try {
    495             jedis = pool.getResource();
    496             jedis.select(index);
    497             jedis.del(key);
    498             return true;
    499         } catch (Exception e) {
    500             pool.returnBrokenResource(jedis);
    501             e.printStackTrace();
    502             return false;
    503         } finally {
    504             returnResource(pool, jedis);
    505         }
    506         
    507     }
    508     
    509     public static boolean del(Jedis jedis,int index ,String key){
    510         try {
    511             jedis.select(index);
    512             jedis.del(key);
    513             return true;
    514         } catch (Exception e) {
    515             logger.error(e,e);
    516             return false;
    517         } 
    518     }
    519     
    520     /**
    521      * 设置key失效时间 
    522      * @param key 
    523      * @param seconds
    524      * @throws Exception
    525      */
    526     public static void setTimeOut(int index,String key,int seconds) throws Exception{
    527         Jedis jedis = null;
    528         try {
    529             jedis = pool.getResource();
    530             jedis.select(index);
    531             jedis.expire(key, seconds);
    532         } catch (Exception e) {
    533             pool.returnBrokenResource(jedis);
    534             logger.error("redis数据库出现异常", e);
    535             throw e;
    536         } finally {
    537             returnResource(pool, jedis);
    538         }
    539     }
    540     
    541     /**
    542      * @Title: redisFactory.java 
    543      * @Package com.jovision.redisDao
    544      * @author Joker(张凯)
    545      * @Description: TODO() 
    546      * @date 2015-10-29 下午03:54:21
    547      * @param jedis
    548      * @param index
    549      * @param key
    550      * @param seconds
    551      * @throws Exception
    552      */
    553     public static void setTimeOut(Jedis jedis,int index,String key,int seconds){
    554         try {
    555             jedis.select(index);
    556             jedis.expire(key, seconds);
    557         } catch (Exception e) {
    558             logger.error(e, e);
    559         } 
    560     }
    561     
    562     public static byte[] getBytes(int index,byte[] key) throws Exception
    563     {
    564         Jedis jedis = null;
    565         try {
    566             jedis = pool.getResource();
    567             jedis.select(index);
    568             return jedis.get(key);
    569         } catch (Exception e) {
    570             pool.returnBrokenResource(jedis);
    571             logger.error("redis数据库出现异常", e);
    572             throw e;
    573         } finally {
    574             returnResource(pool, jedis);
    575         }
    576     }
    577     
    578     public static byte[] getBytes(Jedis jedis,int index,byte[] key) 
    579     {
    580         try {
    581             jedis.select(index);
    582             return jedis.get(key);
    583         } catch (Exception e) {
    584            // pool.returnBrokenResource(jedis);
    585             logger.error("redis数据库出现异常", e);
    586             //throw e;
    587         } 
    588          return null;
    589     }
    590     
    591     public static Map hgetAll(Jedis jedis,int index,String key) 
    592     {
    593         try {
    594             jedis.select(index);
    595             return jedis.hgetAll(key);
    596         } catch (Exception e) {
    597            // pool.returnBrokenResource(jedis);
    598             logger.error("redis数据库出现异常", e);
    599         }
    600         return null;
    601     }
    602     
    603     public static void hmset(Jedis jedis,int index,String key, Map<String,String> map) 
    604     {
    605         try {
    606             jedis.select(index);
    607             jedis.hmset(key, map);
    608         } catch (Exception e) {
    609             //pool.returnBrokenResource(jedis);
    610             logger.error("redis数据库出现异常", e);
    611         }
    612     }
    613     
    614     public static void hmset(Jedis jedis,int seconds,int index,String key, Map<String,String> map) 
    615     {
    616         try {
    617             jedis.select(index);
    618             jedis.hmset(key, map);
    619             jedis.expire(key, seconds);
    620         } catch (Exception e) {
    621            // pool.returnBrokenResource(jedis);
    622             logger.error("redis数据库出现异常", e);
    623         }
    624     }
    625     
    626     public static void setBytes(int index,byte[] key,byte[] value) throws Exception
    627     {
    628         Jedis jedis = null;
    629         try {
    630             jedis = pool.getResource();
    631             jedis.select(index);
    632             jedis.set(key, value);
    633         } catch (Exception e) {
    634             pool.returnBrokenResource(jedis);
    635             logger.error("redis数据库出现异常", e);
    636             throw e;
    637         } finally {
    638             returnResource(pool, jedis);
    639         }
    640     }
    641     
    642     public static void setBytes(Jedis jedis,int index,byte[] key,byte[] value) 
    643     {
    644         try {
    645             jedis.select(index);
    646             jedis.set(key, value);
    647         } catch (Exception e) {
    648             //pool.returnBrokenResource(jedis);
    649             logger.error("redis数据库出现异常", e);
    650         } 
    651     }
    652     
    653     /**
    654      * 
    655      * @author Hellon(刘海龙) 
    656      * @param key key值
    657      * @return 该key值存储的长度
    658      * @throws Exception
    659      */
    660     public static Long getLLength(int index,String key) throws Exception{
    661         Jedis jedis = null;
    662         try {
    663             jedis = pool.getResource();
    664             jedis.select(index);
    665             Long len = jedis.llen(key);
    666             return len;
    667         } catch (Exception e) {
    668             pool.returnBrokenResource(jedis);
    669             logger.error("redis数据库出现异常", e);
    670             throw e;
    671         } finally {
    672             returnResource(pool, jedis);
    673         }
    674     }
    675     
    676     /**
    677      * 从list获取数据
    678      * @author Hellon(刘海龙) 
    679      * @param key 字节byte
    680      * @param start 查询的开始位置
    681      * @param end 查询的结束位置  -1 代表查询所有
    682      * @return 返回字节list列表
    683      * @throws Exception
    684      */
    685     public static List<byte[]>  lrange(int index,byte[] key,int start,int end) throws Exception{
    686         Jedis jedis = null;
    687         try {
    688             jedis = pool.getResource();
    689             jedis.select(index);
    690             List<byte[]> list = jedis.lrange(key, start, end);
    691             return list;
    692         } catch (Exception e) {
    693             pool.returnBrokenResource(jedis);
    694             logger.error("redis数据库出现异常", e);
    695             throw e;
    696         } finally {
    697             returnResource(pool, jedis);
    698         }
    699     }
    700     
    701     /**
    702      * @Title: 是否存在
    703      * @Package com.jovision.redisDao
    704      * @author Joker(张凯)
    705      * @Description: TODO() 
    706      * @date 2015-9-30 下午02:09:25
    707      * @param index
    708      * @param key
    709      * @return
    710      * @throws Exception
    711      */
    712     public static  boolean isExist(int index,String key) throws Exception{
    713         Jedis jedis = null;
    714         try {
    715             jedis = pool.getResource();
    716             jedis.select(index);
    717             return jedis.exists(key);
    718         } catch (Exception e) {
    719             pool.returnBrokenResource(jedis);
    720             logger.error("redis数据库出现异常", e);
    721             throw e;
    722         } finally {
    723             returnResource(pool, jedis);
    724         }
    725     }
    726     
    727     public static  Boolean isExist(Jedis jedis,int index,String key){
    728         try {
    729             jedis.select(index);
    730             return jedis.exists(key);
    731         } catch (Exception e) {
    732            logger.error(e,e);
    733         } 
    734         return null;
    735     }
    736     
    737     /**
    738      * 向list添加数据
    739      * @author Hellon(刘海龙) 
    740      * @param key
    741      * @param strings
    742      * @return
    743      * @throws Exception
    744      */
    745     public static  Long lpush(int index,byte[] key,byte[]... strings) throws Exception{
    746         Jedis jedis = null;
    747         try {
    748             jedis = pool.getResource();
    749             jedis.select(index);
    750             Long len = jedis.lpush(key, strings);
    751             return len;
    752         } catch (Exception e) {
    753             pool.returnBrokenResource(jedis);
    754             logger.error("redis数据库出现异常", e);
    755             throw e;
    756         } finally {
    757             returnResource(pool, jedis);
    758         }
    759     }
    760     
    761     /**
    762      * 保留指定key 的值范围内的数据
    763      * @author Hellon(刘海龙) 
    764      * @param key 指定的key值
    765      * @param start 开始位置
    766      * @param end 结束位置
    767      * @throws Exception 
    768      */
    769     public static void ltrim(int index,byte[] key,int start,int end) throws Exception{
    770         Jedis jedis = null;
    771         try {
    772             jedis = pool.getResource();
    773             jedis.select(index);
    774            jedis.ltrim(key, start, end);
    775         } catch (Exception e) {
    776             logger.error("redis数据库出现异常", e);
    777             throw e;
    778         } finally {
    779             returnResource(pool, jedis);
    780         }
    781     }
    782     
    783     public static Jedis getJedis()
    784     {
    785         logger.info("publicService redis数据库连接活跃数--<"+pool.getNumActive()+
    786                     ">--空闲连接数--<"+pool.getNumIdle()+
    787                     ">--等待连接数--<"+pool.getNumWaiters()+">");
    788         return pool.getResource();
    789     }
    790     
    791     public static void releaseJedis(Jedis jedis)
    792     {
    793         if (jedis != null) {
    794            jedis.close();
    795         }
    796     }
    797 }
    只要还没死,就不要把自己当废物
  • 相关阅读:
    使用Eolinker拓展API设计
    如何记录API
    API设计
    【翻译】通过API主导的集成来证明您的业务未来
    从状态库迁移分布系统建设方式
    PostgreSql 查询某个模式下面的所有表
    迁移状态库的地市区县信息
    测开和开发的难度对比
    yum源的三种搭建方式
    Harbor实现容器镜像仓库的管理和运维
  • 原文地址:https://www.cnblogs.com/fengyefeiluo/p/fengyefeiluo.html
Copyright © 2011-2022 走看看