Redis缓存方案

1 Redis简介
  Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。
  redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
2 安装
  2.1 安装环境:
    CentOS 6.5
    Redis 2.8.13

 

  2.2 下载安装:
    下载文件到 /opt/ 目录下
      wget http://download.redis.io/releases/redis-2.8.13.tar.gz
    解压文件
      tar zxvf redis-2.8.13.tar.gz
    切换目录到 redis-2.8.13 目录下
      cd redis-2.8.13
    执行make命令,最后几行的输出结果
      Hint: To run ‘make test’ is a good idea

      make[1]: Leaving directory `/opt/redis-2.8.13/src’

  2.3 执行安装命令
    make install
  提示:
    cd src && make install
    make[1]: Entering directory `/opt/redis-2.8.13/src'

    Hint: To run 'make test' is a good idea

    INSTALL install
    INSTALL install
    INSTALL install
    INSTALL install
    INSTALL install
    make[1]: Leaving directory `/opt/redis-2.8.13/src'
  根据提示,执行:cd src && make install
  提示:
  Hint: To run 'make test' is a good idea

  INSTALL install
  INSTALL install
  INSTALL install
  INSTALL install
  INSTALL instal
  按照提示执行:make test
  提示:
    You need tcl 8.5 or newer in order to run the Redis test
   make: *** [test] Error 1
  解决方法参考:http://www.linuxfromscratch.org/blfs/view/cvs/general/tcl.html

3 Redis之java操作篇(Jedis)

  主要介绍如何用Jedis对Redis数据库进行操作,只做了简单的例子。前提:安装好Redis数据库,并已启动好服务。

  1) 新建测试工程,修改 pom.xml 配置文件

 

 1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 2     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 3     <modelVersion>4.0.0</modelVersion>
 4     <groupId>org.viking</groupId>
 5     <artifactId>redis_demo</artifactId>
 6     <packaging>war</packaging>
 7     <version>0.0.1-SNAPSHOT</version>
 8     <name>redis_demo Maven Webapp</name>
 9     <url>http://maven.apache.org</url>
10     <dependencies>
11         <dependency>
12             <groupId>jdk.tools</groupId>
13             <artifactId>jdk.tools</artifactId>
14             <version>1.7</version>
15             <scope>system</scope>
16             <systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
17         </dependency>
18         <dependency>
19           <groupId>junit</groupId>
20           <artifactId>junit</artifactId>
21           <version>4.12</version>
22         </dependency>
23         <dependency>
24           <groupId>redis.clients</groupId>
25           <artifactId>jedis</artifactId>
26           <version>2.8.0</version>
27         </dependency>
28         <dependency>
29           <groupId>commons-pool</groupId>
30           <artifactId>commons-pool</artifactId>
31           <version>1.6</version>
32         </dependency>
33     </dependencies>
34     <build>
35         <finalName>${project.artifactId}</finalName>
36         <plugins>
37             <plugin>
38                 <groupId>org.apache.maven.plugins</groupId>
39                 <artifactId>maven-compiler-plugin</artifactId>
40                 <version>2.3.2</version>
41                 <configuration>
42                     <source>${java-version}</source>
43                     <target>${java-version}</target>
44                     <encoding>utf8</encoding>
45                 </configuration>
46             </plugin>
47         </plugins>
48     </build>
49 </project>

 

  2) 创建 RedisUtil.java (用来测试Reids连接池使用)

 1 package demo;
 2 
 3 import redis.clients.jedis.Jedis;
 4 import redis.clients.jedis.JedisPool;
 5 import redis.clients.jedis.JedisPoolConfig;
 6 
 7 public final class RedisUtil {
 8 
 9 //Redis服务器IP
10 private static String ADDR = "127.0.0.1";
11 
12 //Redis的端口号
13 private static int PORT = 6379;
14 
15 //访问密码
16 //private static String AUTH = "admin";
17 
18 //可用连接实例的最大数目,默认值为8;
19 //如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
20 private static int MAX_ACTIVE = 1024;
21 
22 //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
23 private static int MAX_IDLE = 200;
24 
25 //等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
26 private static int MAX_WAIT = 10000;
27 
28 private static int TIMEOUT = 10000;
29 
30 //在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
31 private static boolean TEST_ON_BORROW = true;
32 
33 private static JedisPool jedisPool = null;
34 
35 /**
36  * 初始化Redis连接池
37  */
38 static {
39 try {
40 JedisPoolConfig config = new JedisPoolConfig();
41 config.setMaxIdle(MAX_ACTIVE);
42 //change "maxActive" -> "maxTotal" and "maxWait" -> "maxWaitMillis" in all examples.
43 config.setMaxTotal(MAX_ACTIVE);
44 config.setMaxIdle(MAX_IDLE);
45 config.setMaxWaitMillis(MAX_WAIT);
46 config.setTestOnBorrow(TEST_ON_BORROW);
47 jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT);
48 } catch (Exception e) {
49 e.printStackTrace();
50 }
51 }
52 
53 /**
54  * 获取Jedis实例
55  * @return
56  */
57 public synchronized static Jedis getJedis() {
58 try {
59 if (jedisPool != null) {
60 Jedis resource = jedisPool.getResource();
61 return resource;
62 } else {
63 return null;
64 }
65 } catch (Exception e) {
66 e.printStackTrace();
67 return null;
68 }
69 }
70 
71 /**
72  * 释放jedis资源
73  * @param jedis
74  */
75 public static void returnResource(final Jedis jedis) {
76 if (jedis != null) {
77 jedisPool.returnResource(jedis);
78 }
79 }
80 }

  3) 创建 TestRedis.java

  1 package demo;
  2 
  3 import java.util.HashMap;
  4 import java.util.Iterator;
  5 import java.util.List;
  6 import java.util.Map;
  7 import org.junit.Before;
  8 import org.junit.Test;
  9 import redis.clients.jedis.Jedis;
 10 
 11 public class TestRedis {
 12     private Jedis jedis; 
 13 
 14 @Before
 15 public void setup() {
 16 //连接redis服务器
 17 jedis = new Jedis("127.0.0.1", 6379);
 18 }
 19 
 20 /**
 21  * redis存储字符串
 22  */
 23 @Test
 24 public void testString() {
 25 //-----添加数据----------  
 26 jedis.set("name","yuanxj");//向key-->name中放入了value-->yuanxj  
 27 System.out.println(jedis.get("name"));//执行结果:yuanxj  
 28 
 29 jedis.append("name", " is Fraud"); //拼接
 30 System.out.println(jedis.get("name")); 
 31 
 32 jedis.del("name");  //删除某个键
 33 System.out.println(jedis.get("name"));
 34 //设置多个键值对
 35 jedis.mset("name","Viking","age","30","qq","1001");
 36 jedis.incr("age"); //进行加1操作
 37 System.out.println(jedis.get("name") + "-" + jedis.get("age") + "-" + jedis.get("qq"));
 38 
 39 }
 40 
 41 /**
 42  * redis操作Map
 43  */
 44 @Test
 45 public void testMap() {
 46 //-----添加数据----------  
 47 Map<String, String> map = new HashMap<String, String>();
 48 map.put("name", "yuanxj");
 49 map.put("age", "30");
 50 map.put("qq", "1001");
 51 jedis.hmset("user1",map);
 52 //第一个参数是存入redis中map对象的key,后面跟的是放入map中的对象的key,后面的key可以跟多个,是可变参数  
 53 List<String> rsmap = jedis.hmget("user1", "name", "age", "qq");
 54 System.out.println(rsmap);  
 55   
 56 //删除map中的某个键值  
 57 jedis.hdel("user1","age");
 58 System.out.println(jedis.hmget("user1", "age")); //因为删除了,所以返回的是null  
 59 System.out.println(jedis.hlen("user1")); //返回key为user的键中存放的值的个数2 
 60 System.out.println(jedis.exists("user1"));//是否存在key为user的记录 返回true  
 61 System.out.println(jedis.hkeys("user1"));//返回map对象中的所有key  
 62 System.out.println(jedis.hvals("user1"));//返回map对象中的所有value 
 63   
 64 Iterator<String> iter=jedis.hkeys("user1").iterator();  
 65 while (iter.hasNext()){  
 66 String key = iter.next();  
 67 System.out.println(key+":"+jedis.hmget("user1",key));  
 68 }  
 69 }
 70 
 71 /** 
 72  * jedis操作List 
 73  */  
 74 @Test  
 75 public void testList(){  
 76 //开始前,先移除所有的内容  
 77 jedis.del("java framework");  
 78 System.out.println(jedis.lrange("java framework",0,-1));  
 79 //先向key java framework中存放三条数据  
 80 jedis.lpush("java framework","spring");  
 81 jedis.lpush("java framework","struts");  
 82 jedis.lpush("java framework","hibernate");  
 83 //再取出所有数据jedis.lrange是按范围取出,  
 84 // 第一个是key,第二个是起始位置,第三个是结束位置,jedis.llen获取长度 -1表示取得所有  
 85 System.out.println(jedis.lrange("java framework",0,-1));  
 86 
 87 jedis.del("java framework");
 88 jedis.rpush("java framework","spring");  
 89 jedis.rpush("java framework","struts");  
 90 jedis.rpush("java framework","hibernate"); 
 91 System.out.println(jedis.lrange("java framework",0,-1));
 92 }  
 93 
 94 /** 
 95  * jedis操作Set 
 96  */  
 97 @Test  
 98 public void testSet(){  
 99 //添加  
100 jedis.sadd("user2","yuanxj");  
101 jedis.sadd("user2","Fraud");  
102 jedis.sadd("user2","Viking");  
103 jedis.sadd("user2","Jake");
104 jedis.sadd("user2","who");  
105 //移除noname  
106 jedis.srem("user2","who");  
107 System.out.println(jedis.smembers("user2"));//获取所有加入的value  
108 System.out.println(jedis.sismember("user2", "who"));//判断 who 是否是user集合的元素  
109 System.out.println(jedis.srandmember("user2"));  
110 System.out.println(jedis.scard("user2"));//返回集合的元素个数  
111 }  
112   
113 @Test  
114 public void test() throws InterruptedException {  
115 //jedis 排序  
116 //注意,此处的rpush和lpush是List的操作。是一个双向链表(但从表现来看的)  
117 jedis.del("a");//先清除数据,再加入数据进行测试  
118 jedis.rpush("a", "1");  
119 jedis.lpush("a","6");  
120 jedis.lpush("a","3");  
121 jedis.lpush("a","9");  
122 System.out.println(jedis.lrange("a",0,-1));// [9, 3, 6, 1]  
123 System.out.println(jedis.sort("a")); //[1, 3, 6, 9]  //输入排序后结果  
124 System.out.println(jedis.lrange("a",0,-1));  
125 }  
126 
127 @Test
128 public void testRedisPool() {
129 RedisUtil.getJedis().set("newname", "中文测试");
130 System.out.println(RedisUtil.getJedis().get("newname"));
131 }
132 }

  4) 测试运行结果

  Run as Junit Test  -> See the result!

 

4 Redis之java操作篇(数据对象的存取)

  摘要:Redis本身没有存取对象的功能,而是有存取byte数据的功能,我们可以对要存取的对象进行序列化后,进行操作.

  创建SerializeUtil.java

 1 public class SerializeUtil {
 2 /**
 3  * 序列化
 4  * @param object
 5  */
 6 public static byte[] serialize(Object object) {
 7 ObjectOutputStream oos = null;
 8 ByteArrayOutputStream baos = null;
 9 try {
10 // 序列化
11 baos = new ByteArrayOutputStream();
12 oos = new ObjectOutputStream(baos);
13 oos.writeObject(object);
14 byte[] bytes = baos.toByteArray();
15 return bytes;
16 } catch (Exception e) {
17     e.printStackTrace();
18 }
19 return null;
20 }
21 
22 /**
23  * 反序列化
24  * @param bytes
25  */
26 public static Object unserialize(byte[] bytes) {
27 ByteArrayInputStream bais = null;
28 try {
29 // 反序列化
30 bais = new ByteArrayInputStream(bytes);
31 ObjectInputStream ois = new ObjectInputStream(bais);
32 return ois.readObject();
33 } catch (Exception e) {
34     e.printStackTrace();
35 }
36 return null;
37 }
38 }

  1) 新建测试对象

 1 package demo.bean;
 2 
 3 import java.io.Serializable;
 4 
 5 public class Goods implements Serializable {
 6     /**
 7      * 
 8      */
 9     private static final long serialVersionUID = 6856239042967045162L;
10     private String name;
11     private Float price;
12     private String desc;
13     public String getName() {
14         return name;
15     }
16     public void setName(String name) {
17         this.name = name;
18     }
19     public Float getPrice() {
20         return price;
21     }
22     public void setPrice(Float price) {
23         this.price = price;
24     }
25     public String getDesc() {
26         return desc;
27     }
28     public void setDesc(String desc) {
29         this.desc = desc;
30     }
31     
32 }

  2) 新建测试代码

 1 package demo;
 2 
 3 import demo.bean.Goods;
 4 
 5 public class TestObject {
 6     public static void main (String[] args) {
 7         Goods g1 = new Goods();
 8         g1.setName("苹果");
 9         g1.setPrice(5f);
10         g1.setDesc("这里的苹果大又甜");
11         
12         Goods g2 = new Goods();
13         g2.setName("橘子");
14         g2.setPrice(3.5f);
15         g2.setDesc("这里的橘子水很多");
16         
17         RedisUtil.getJedis().set("g1".getBytes(), SerializeUtil.serialize(g1));
18         byte[] bg1 = RedisUtil.getJedis().get("g1".getBytes());
19         Goods rg1 = (Goods)SerializeUtil.unserialize(bg1);
20         System.out.println(rg1.getName());
21         System.out.println(rg1.getPrice());
22         System.out.println(rg1.getDesc());
23     }
24 }

  5 使用mybatis-redis

  mybatis-redis是mybatis集成redis实现二级缓存的一个实现,和mybatis-memcached类似,这种方式是mybatis集成redis最快的方式,无需自己编写代码实现cache接口
  mybatis-redis的官方git地址https://github.com/mybatis/redis-cache
  项目集成mybatis-redis

 1 <dependency>
 2 <groupId>org.mybatis.caches</groupId>
 3 <artifactId>mybatis-redis</artifactId>
 4 <version>1.0.0-beta2</version>
 5 </dependency>
 6 <dependency>
 7 <groupId>redis.clients</groupId>
 8 <artifactId>jedis</artifactId>
 9 <version>2.8.0</version>
10 </dependency>

  在mybatis配置中开启缓存

 1 <setting name="cacheEnabled" value="true"/>
 2 <?xml version="1.0" encoding="UTF-8" ?>
 3 <!DOCTYPE configuration
 4         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 5         "http://mybatis.org/dtd/mybatis-3-config.dtd">
 6 <configuration>
 7     <settings>
 8         <setting name="cacheEnabled" value="true"/>
 9         <!--<setting name="lazyLoadingEnabled" value="false"/>-->
10         <!--<setting name="aggressiveLazyLoading" value="true"/>-->
11     </settings>
12     <plugins>
13         <!--mybatis 分页插件-->
14         <plugin interceptor="com.github.pagehelper.PageHelper">
15             <property name="dialect" value="mysql" />
16         </plugin>
17     </plugins>
18 </configuration>

  在项目的资源(maven resource)目录中加入redis.propertis文件

1 host=localhost

 2 port=6379    

3 timeout=5000   

  最后在mapper映射文件中开启cache即可

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 3 <mapper namespace="com.lisi.test.dao.UserDao">
 4 <!--启用mybatis-redis-->
 5 <cache type="org.mybatis.caches.redis.RedisCache"/>
 6 <resultMap type="User" id="UserResult">
 7 <result property="id" column="id"/>
 8 <result property="uuid" column="uuid"/>
 9 <result property="username" column="username"/>
10 <result property="password" column="password"/>
11 <result property="createTime" column="create_time"/>
12 </resultMap>
13 <insert id="save" parameterType="User">
14 <selectKey resultType="int" keyProperty="id" order="AFTER">
15 SELECT LAST_INSERT_ID()
16 </selectKey>
17 insert into
18 t_user(uuid,username,password,create_time)
19 values(#{uuid},#{username},#{password},#{createTime})
20 </insert>
21   
22 <delete id="delete" parameterType="long">
23         delete from t_user where id =
24         #{id}
25     </delete>
26    
27 <select id="getById" parameterType="long" resultType="User">
28         select
29         id,uuid,username,password,create_time as createTime from t_user
30         where id=#{id}
31     </select>
32   
33 <update id="update" parameterType="User">
34         update t_user set
35         username=#{username}
36         where id = #{id}
37     </update>
38 </mapper>

6 使用spring-data-redis

  spring-data-redis是比较有名的项目,相关资料很多。
  使用spring-data-redis的关键依赖,因为用到spring,所以spring的相关依赖是需要加入的。

 1 <dependency>
 2 <groupId>redis.clients</groupId>
 3 <artifactId>jedis</artifactId>
 4 <version>2.8.0</version>
 5 </dependency>
 6 <dependency>
 7 <groupId>org.springframework.data</groupId>
 8 <artifactId>spring-data-redis</artifactId>
 9 <version>1.7.2.RELEASE</version>
10 </dependency>

  mybatis使用spring-data-redis集成redis缓存和第一种方式很相似,都需要自己实现cache接口,只是使用spring-data-redis后方便使用spring的方式来管理,

  代码:

  1 /**
  2  * Use JedisConnectionFactory
  3  */
  4 public class SpringRedisCache implements Cache {
  5 private static Logger logger = LoggerFactory.getLogger(SpringRedisCache.class);
  6 
  7 private JedisConnectionFactory jedisConnectionFactory = (JedisConnectionFactory)SpringContextHolder.getBean("jedisConnectionFactory");
  8 private final String id;
  9 
 10 private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
 11 
 12 public SpringRedisCache(final String id) {
 13 if (id == null) {
 14 throw new IllegalArgumentException("Cache instances require an ID");
 15 }
 16 logger.debug("SpringRedisCache:id=" + id);
 17 this.id = id;
 18 }
 19 
 20 @Override
 21 public void clear() {
 22 RedisConnection connection = null;
 23 try {
 24 connection = jedisConnectionFactory.getConnection();
 25 connection.flushDb();
 26 connection.flushAll();
 27 } catch (JedisConnectionException e) {
 28 e.printStackTrace();
 29 } finally {
 30 if (connection != null) {
 31 connection.close();
 32 }
 33 }
 34 }
 35 
 36 @Override
 37 public String getId() {
 38 return this.id;
 39 }
 40 
 41 @Override
 42 public Object getObject(Object key) {
 43 Object result = null;
 44 RedisConnection connection = null;
 45 try {
 46 connection = jedisConnectionFactory.getConnection();
 47 RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
 48 result = serializer.deserialize(connection.get(serializer.serialize(key)));
 49 } catch (JedisConnectionException e) {
 50 e.printStackTrace();
 51 } finally {
 52 if (connection != null) {
 53 connection.close();
 54 }
 55 }
 56 return result;
 57 }
 58 
 59 @Override
 60 public ReadWriteLock getReadWriteLock() {
 61 return this.readWriteLock;
 62 }
 63 
 64 @Override
 65 public int getSize() {
 66 int result = 0;
 67 RedisConnection connection = null;
 68 try {
 69 connection = jedisConnectionFactory.getConnection();
 70 result = Integer.valueOf(connection.dbSize().toString());
 71 } catch (JedisConnectionException e) {
 72 e.printStackTrace();
 73 } finally {
 74 if (connection != null) {
 75 connection.close();
 76 }
 77 }
 78 return result;
 79 }
 80 
 81 @Override
 82 public void putObject(Object key, Object value) {
 83 RedisConnection connection = null;
 84 try {
 85 connection = jedisConnectionFactory.getConnection();
 86 RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
 87 connection.set(serializer.serialize(key), serializer.serialize(value));
 88 } catch (JedisConnectionException e) {
 89 e.printStackTrace();
 90 } finally {
 91 if (connection != null) {
 92 connection.close();
 93 }
 94 }
 95 }
 96 
 97 @Override
 98 public Object removeObject(Object key) {
 99 RedisConnection connection = null;
100 Object result = null;
101 try {
102 connection = jedisConnectionFactory.getConnection();
103 RedisSerializer<Object> serializer = new JdkSerializationRedisSerializer();
104 result = connection.expire(serializer.serialize(key), 0);
105 } catch (JedisConnectionException e) {
106 e.printStackTrace();
107 } finally {
108 if (connection != null) {
109 connection.close();
110 }
111 }
112 return result;
113 }
114 }

  上面使用的SpringContextHolder源码

 1 @Component
 2 public class SpringContextHolder implements ApplicationContextAware {
 3 private static ApplicationContext context;
 4 
 5 @Override
 6 public void setApplicationContext(ApplicationContext context)
 7 throws BeansException {
 8 SpringContextHolder.context = context;
 9 }
10 public static <T> T getBean(String name){
11 return (T)context.getBean(name);
12 }
13 }

  在项目的资源(maven resource)目录中加入redis.propertis文件

1 redis.host=localhost
2 redis.port=6379
3 redis.pass=
4 redis.timeout=5000
5 redis.default.db=0
6 redis.maxIdle=300
7 redis.maxActive=600
8 redis.maxWait=1000
9 redis.testOnBorrow=true

  在spring中配置加入

 1  <!-- redis数据池配置 -->
 2 <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
 3 <property name="maxIdle" value="${redis.maxIdle}" />
 4 <property name="maxTotal" value="${redis.maxActive}" />
 5 <property name="maxWaitMillis" value="${redis.maxWait}" />
 6 <property name="testOnBorrow" value="${redis.testOnBorrow}" />
 7 </bean>
 8 <!-- Spring-data-redis connectFactory -->
 9 <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
10 <property name="usePool" value="true"></property>
11 <property name="hostName" value="${redis.host}" />
12 <property name="port" value="${redis.port}" />
13 <property name="password" value="${redis.pass}" />
14 <property name="timeout" value="${redis.timeout}" />
15 <property name="database" value="${redis.default.db}"/>
16 <constructor-arg index="0" ref="jedisPoolConfig" />
17 </bean>

  最后修改mapper文件中cache的type为SpringRedisCache即可;

  使用spring-data-redis还可以使用它的RedisTemplate去实现cache接口,不过因为RedisTemplate是对JedisConnectionFactory的包装,在spring配置文件的配置更多.

 

<!-- Start -->

获知及时信息,请关注我的个人微信订阅号:0与1的那点事

 

<!-- End -->

 

本文为博主原创文章,转载请注明出处!

http://www.cnblogs.com/libingbin/

感谢您的阅读。

posted on 2016-10-10 22:06  bingbinlee  阅读(1683)  评论(0编辑  收藏  举报