简介

Jedis Client是Redis官网推荐的一个面向java客户端,库文件实现了对各类API进行封装调用。
Jedis源码工程地址:

https://github.com/xetorthio/jedis

使用

想要使用Jedis必须加载jar包或者添加maven依赖,jar包可以自己上网下载,我的是Maven项目,所以在pom.xml中增加如下语句:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
    <type>jar</type>
    <scope>compile</scope>
</dependency>

简单的Jedis实例

在加载Jedis JAR包之后,我们可以直接使用新建一个Jedis实例的方法,来建立一个到Redis的连接,并进行操作,以下是一个简单的jedis实例:

public void setup() {
 //连接redis服务器
        jedis = new Jedis("172.24.4.183", 6379);
//        jedis.auth("redis");//验证密码,如果需要验证的话
    }
/**
 * 键操作
 */
public void testKey()  throws InterruptedException{
    System.out.println("清空数据:"+jedis.flushDB());
    System.out.println("判断某个键是否存在:"+jedis.exists("username"));
    System.out.println("新增<'username','xmr'>的键值对:"+jedis.set("username", "xmr"));
    System.out.println(jedis.exists("username"));
    System.out.println("新增<'password','password'>的键值对:"+jedis.set("password", "123"));
    System.out.print("系统中所有的键如下:");
    Set<String> keys = jedis.keys("*");
    System.out.println(keys);
    System.out.println("删除键password:"+jedis.del("password"));
    System.out.println("判断键password是否存在:"+jedis.exists("password"));
    System.out.println("设置键username的过期时间为5s:"+jedis.expire("username", 8));
    TimeUnit.SECONDS.sleep(2);
    System.out.println("查看键username的剩余生存时间:"+jedis.ttl("username"));
    System.out.println("移除键username的生存时间:"+jedis.persist("username"));
    System.out.println("查看键username的剩余生存时间:"+jedis.ttl("username"));
    System.out.println("查看键username所存储的值的类型:"+jedis.type("username"));
}

输出结果:

字符串操作

输出结果:

public void testString() throws InterruptedException {
    jedis.flushDB();
    System.out.println("===========增加数据===========");
    System.out.println(jedis.set("key1", "value1"));
    System.out.println(jedis.set("key2", "value2"));
    System.out.println(jedis.set("key3", "value3"));
    System.out.println("删除键key2:" + jedis.del("key2"));
    System.out.println("获取键key2:" + jedis.get("key2"));
    System.out.println("修改key1:" + jedis.set("key1", "1"));
    System.out.println("获取key1的值:" + jedis.get("key1"));
    System.out.println("在key3后面加入值:" + jedis.append("key3", "End"));
    System.out.println("key3的值:" + jedis.get("key3"));
    System.out.println("增加多个键值对:" + jedis.mset("key01", "value01", "key02", "value02", "key03", "value03"));
    System.out.println("获取多个键值对:" + jedis.mget("key01", "key02", "key03"));
    System.out.println("获取多个键值对:" + jedis.mget("key01", "key02", "key03", "key04"));
    System.out.println("删除多个键值对:" + jedis.del(new String[]{"key01", "key02"}));
    System.out.println("获取多个键值对:" + jedis.mget("key01", "key02", "key03"));

    jedis.flushDB();
    System.out.println("===========新增键值对,防止覆盖原先值==============");
    System.out.println(jedis.setnx("key1", "value1"));
    System.out.println(jedis.setnx("key2", "value2"));
    System.out.println(jedis.setnx("key2", "value2-new"));
    System.out.println(jedis.get("key1"));
    System.out.println(jedis.get("key2"));

    System.out.println("===========新增键值对并设置有效时间=============");
    System.out.println(jedis.setex("key3", 2, "value3"));
    System.out.println(jedis.get("key3"));
    TimeUnit.SECONDS.sleep(3);
    System.out.println(jedis.get("key3"));

    System.out.println("===========获取原值,更新为新值==========");//GETSET is an atomic set this value and return the old value command.
    System.out.println(jedis.getSet("key2", "key2GetSet"));
    System.out.println(jedis.get("key2"));

    System.out.println("获得key2的值的字串:" + jedis.getrange("key2", 2, 4));
}

输出结果:

哈希操作

输出结果:

/**
 * redis操作Hash
 */
public void testHash() {
    jedis.flushDB();
    Map<String, String> map = new HashMap<>();
    map.put("key1", "value1");
    map.put("key2", "value2");
    map.put("key3", "value3");
    map.put("key4", "value4");
    jedis.hmset("hash", map);
    jedis.hset("hash", "key5", "value5");
    System.out.println("散列hash的所有键值对为:" + jedis.hgetAll("hash"));//return Map<String,String>
    System.out.println("散列hash的所有键为:" + jedis.hkeys("hash"));//return Set<String>
    System.out.println("散列hash的所有值为:" + jedis.hvals("hash"));//return List<String>
    System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:" + jedis.hincrBy("hash", "key6", 6));
    System.out.println("散列hash的所有键值对为:" + jedis.hgetAll("hash"));
    System.out.println("将key6保存的值加上一个整数,如果key6不存在则添加key6:" + jedis.hincrBy("hash", "key6", 3));
    System.out.println("散列hash的所有键值对为:" + jedis.hgetAll("hash"));
    System.out.println("删除一个或者多个键值对:" + jedis.hdel("hash", "key2"));
    System.out.println("散列hash的所有键值对为:" + jedis.hgetAll("hash"));
    System.out.println("散列hash中键值对的个数:" + jedis.hlen("hash"));
    System.out.println("判断hash中是否存在key2:" + jedis.hexists("hash", "key2"));
    System.out.println("判断hash中是否存在key3:" + jedis.hexists("hash", "key3"));
    System.out.println("获取hash中的值:" + jedis.hmget("hash", "key3"));
    System.out.println("获取hash中的值:" + jedis.hmget("hash", "key3", "key4"));
}

输出结果:

列表操作

代码如下
 

public void testList() {
    jedis.flushDB();
    System.out.println("===========添加一个list===========");
    jedis.lpush("lists", "ArrayList", "Vector", "Stack", "HashMap", "WeakHashMap", "LinkedHashMap");
    jedis.lpush("lists", "HashSet");
    jedis.lpush("lists", "TreeSet");
    jedis.lpush("lists", "TreeMap");
    System.out.println("lists的内容:" + jedis.lrange("lists", 0, -1));//-1代表倒数第一个元素,-2代表倒数第二个元素
    System.out.println("lists区间0-3的元素:" + jedis.lrange("lists", 0, 3));
    System.out.println("===============================");
    // 删除列表指定的值 ,第二个参数为删除的个数(有重复时),后add进去的值先被删,类似于出栈
    System.out.println("删除指定元素个数:" + jedis.lrem("lists", 2, "HashMap"));
    System.out.println("lists的内容:" + jedis.lrange("lists", 0, -1));
    System.out.println("删除下表0-3区间之外的元素:" + jedis.ltrim("lists", 0, 3));
    System.out.println("lists的内容:" + jedis.lrange("lists", 0, -1));
    System.out.println("lists列表出栈(左端):" + jedis.lpop("lists"));
    System.out.println("lists的内容:" + jedis.lrange("lists", 0, -1));
    System.out.println("lists添加元素,从列表右端,与lpush相对应:" + jedis.rpush("lists", "EnumMap"));
    System.out.println("lists的内容:" + jedis.lrange("lists", 0, -1));
    System.out.println("lists列表出栈(右端):" + jedis.rpop("lists"));
    System.out.println("lists的内容:" + jedis.lrange("lists", 0, -1));
    System.out.println("修改lists指定下标1的内容:" + jedis.lset("lists", 1, "LinkedArrayList"));
    System.out.println("lists的内容:" + jedis.lrange("lists", 0, -1));
    System.out.println("===============================");
    System.out.println("lists的长度:" + jedis.llen("lists"));
    System.out.println("获取lists下标为2的元素:" + jedis.lindex("lists", 2));
    System.out.println("===============================");
    jedis.lpush("sortedList", "3", "6", "2", "0", "7", "4");
    System.out.println("sortedList排序前:" + jedis.lrange("sortedList", 0, -1));
    System.out.println(jedis.sort("sortedList"));
    System.out.println("sortedList排序后:" + jedis.lrange("sortedList", 0, -1));
}

输出结果:

集合(Set)操作

public void testSet() {
    jedis.flushDB();
    System.out.println("============向集合中添加元素============");
    System.out.println(jedis.sadd("eleSet", "e1", "e2", "e4", "e3", "e0", "e8", "e7", "e5"));
    System.out.println(jedis.sadd("eleSet", "e6"));
    System.out.println("eleSet的所有元素为:" + jedis.smembers("eleSet"));
    System.out.println("删除一个元素e0:" + jedis.srem("eleSet", "e0"));
    System.out.println("eleSet的所有元素为:" + jedis.smembers("eleSet"));
    System.out.println("删除两个元素e7和e6:" + jedis.srem("eleSet", "e7", "e6"));
    System.out.println("eleSet的所有元素为:" + jedis.smembers("eleSet"));
    System.out.println("随机的移除集合中的一个元素:" + jedis.spop("eleSet"));
    System.out.println("eleSet的所有元素为:" + jedis.smembers("eleSet"));
    System.out.println("eleSet中包含元素的个数:" + jedis.scard("eleSet"));
    System.out.println("e1是否在eleSet中:" + jedis.sismember("eleSet", "e1"));
    System.out.println("=================================");
    System.out.println(jedis.sadd("eleSet1", "e1", "e2", "e4", "e3", "e0", "e8", "e7", "e5"));
    System.out.println(jedis.sadd("eleSet2", "e1", "e2", "e4", "e3", "e0", "e8"));
    System.out.println("将eleSet1中删除e1并存入eleSet3中:" + jedis.smove("eleSet1", "eleSet3", "e1"));
    System.out.println("eleSet1中的元素:" + jedis.smembers("eleSet1"));
    System.out.println("eleSet3中的元素:" + jedis.smembers("eleSet3"));
    System.out.println("============集合运算=================");
    System.out.println("eleSet1中的元素:" + jedis.smembers("eleSet1"));
    System.out.println("eleSet2中的元素:" + jedis.smembers("eleSet2"));
    System.out.println("eleSet1和eleSet2的交集:" + jedis.sinter("eleSet1", "eleSet2"));
    System.out.println("eleSet1和eleSet2的并集:" + jedis.sunion("eleSet1", "eleSet2"));
    System.out.println("eleSet1和eleSet2的差集:" + jedis.sdiff("eleSet1", "eleSet2"));//eleSet1中有,eleSet2中没有
}

输出结果:

============向集合中添加元素============

8

1

eleSet的所有元素为:[e0, e5, e3, e8, e7, e2, e1, e4, e6]

删除一个元素e0:1

eleSet的所有元素为:[e5, e3, e8, e7, e2, e1, e4, e6]

删除两个元素e7和e6:2

eleSet的所有元素为:[e1, e4, e3, e5, e2, e8]

随机的移除集合中的一个元素:e1

eleSet的所有元素为:[e3, e5, e2, e8, e4]

eleSet中包含元素的个数:5

e1是否在eleSet中:false

=================================

8

6

将eleSet1中删除e1并存入eleSet3中:1

eleSet1中的元素:[e0, e5, e3, e8, e7, e2, e4]

eleSet3中的元素:[e1]

============集合运算=================

eleSet1中的元素:[e0, e5, e3, e8, e7, e2, e4]

eleSet2中的元素:[e3, e1, e4, e0, e8, e2]

eleSet1和eleSet2的交集:[e3, e4, e0, e8, e2]

eleSet1和eleSet2的并集:[e2, e1, e4, e0, e3, e5, e7, e8]

eleSet1和eleSet2的差集:[e5, e7]

有序集合

public void testSortedSet(){
    jedis.flushDB();
    Map<String,Double> map = new HashMap<>();
    map.put("key2",1.5);
    map.put("key3",1.6);
    map.put("key4",1.9);
    System.out.println(jedis.zadd("zset", 3,"key1"));
    System.out.println(jedis.zadd("zset",map));
    System.out.println("zset中的所有元素:"+jedis.zrangeByScore("zset", 0,100));
    System.out.println("zset中key2的分值:"+jedis.zscore("zset", "key2"));
    System.out.println("zset中key2的排名:"+jedis.zrank("zset", "key2"));
    System.out.println("删除zset中的元素key3:"+jedis.zrem("zset", "key3"));
    System.out.println("zset中的所有元素:"+jedis.zrange("zset", 0, -1));
    System.out.println("zset中元素的个数:"+jedis.zcard("zset"));
    System.out.println("zset中分值在1-4之间的元素的个数:"+jedis.zcount("zset", 1, 4));
    System.out.println("key2的分值加上5:"+jedis.zincrby("zset", 5, "key2"));
    System.out.println("key3的分值加上4:"+jedis.zincrby("zset", 4, "key3"));
    System.out.println("zset中的所有元素:"+jedis.zrange("zset", 0, -1));
}

输出结果:

1
3
zset中的所有元素:[key2, key3, key4, key1]
zset中key2的分值:1.5
zset中key2的排名:0
删除zset中的元素key3:1
zset中的所有元素:[key2, key4, key1]
zset中元素的个数:3
zset中分值在1-4之间的元素的个数:3
key2的分值加上5:6.5
key3的分值加上4:4.0
zset中的所有元素:[key4, key1, key3, key2]

排序sort

public void testSort(){
    jedis.flushDB();
    jedis.lpush("collections", "ArrayList", "Vector", "Stack", "HashMap", "WeakHashMap", "LinkedHashMap");
    System.out.println("collections的内容:"+jedis.lrange("collections", 0, -1));
    SortingParams sortingParameters = new SortingParams();
    System.out.println(jedis.sort("collections",sortingParameters.alpha()));
    System.out.println("===============================");
    jedis.lpush("sortedList", "3","6","2","0","7","4");
    System.out.println("sortedList排序前:"+jedis.lrange("sortedList", 0, -1));
    System.out.println("升序:"+jedis.sort("sortedList", sortingParameters.asc()));
    System.out.println("升序:"+jedis.sort("sortedList", sortingParameters.desc()));
    System.out.println("===============================");
    jedis.lpush("userlist", "33");
    jedis.lpush("userlist", "22");
    jedis.lpush("userlist", "55");
    jedis.lpush("userlist", "11");
    jedis.hset("user:66", "name", "66");
    jedis.hset("user:55", "name", "55");
    jedis.hset("user:33", "name", "33");
    jedis.hset("user:22", "name", "79");
    jedis.hset("user:11", "name", "24");
    jedis.hset("user:11", "add", "beijing");
    jedis.hset("user:22", "add", "shanghai");
    jedis.hset("user:33", "add", "guangzhou");
    jedis.hset("user:55", "add", "chongqing");
    jedis.hset("user:66", "add", "xi'an");
    sortingParameters = new SortingParams();
    sortingParameters.get("user:*->name");
    sortingParameters.get("user:*->add");
    System.out.println(jedis.sort("userlist",sortingParameters));
}

输出结果:

collections的内容:[LinkedHashMap, WeakHashMap, HashMap, Stack, Vector, ArrayList]
[ArrayList, HashMap, LinkedHashMap, Stack, Vector, WeakHashMap]
===============================
sortedList排序前:[4, 7, 0, 2, 6, 3]
升序:[0, 2, 3, 4, 6, 7]
升序:[7, 6, 4, 3, 2, 0]
===============================
[24, beijing, 79, shanghai, 33, guangzhou, 55, chongqing]

JedisPool应用

虽然我们可以简单地创建Jedis使用,但每次操作的时候,都建立连接,很耗费性能。解决方法就是从一个连接池中取出连接对象,用完还回去。使用连接池的方案还能解决很多同步性问题。在Jedis中,管理Redis连接的类是JedisPool。要想使用JedisPool需要添加jar包或依赖库,在pom.xml中添加

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.4.2</version>
</dependency>
  • 普通连接池连接

实现方式有两种,一种是通过配置文件(properties文件),我的文件名是jedisPool.properties:

#最大分配的对象数

redis.pool.maxTotal=1024

#最大能够保持idel状态的对象数

redis.pool.maxIdle=200

#当池内没有返回对象时,最大等待时间

redis.pool.maxWait=1000

#当调用borrow Object方法时,是否进行有效性检查

redis.pool.testOnBorrow=true

#当调用return Object方法时,是否进行有效性检查

redis.pool.testOnReturn=true

#IP

redis.ip=172.24.4.183

#Port

redis.port=6379

java类如下:

public class JedisUtil {
    private static Jedis jedis;
    private static JedisPool jedisPool = null;
    /**
     * 初始化Redis连接池
     */
    static {
        ResourceBundle bundle = ResourceBundle.getBundle("jedisPool");
        if (bundle == null) {
            throw new IllegalArgumentException(
                    "[jedisPool.properties] is not found!");
        }
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(Integer.valueOf(bundle
                .getString("redis.pool.maxTotal")));
        config.setMaxIdle(Integer.valueOf(bundle
                .getString("redis.pool.maxIdle")));
        config.setMaxWaitMillis(Long.valueOf(bundle.getString("redis.pool.maxWait")));
        config.setTestOnBorrow(Boolean.valueOf(bundle
                .getString("redis.pool.testOnBorrow")));
        config.setTestOnReturn(Boolean.valueOf(bundle
                .getString("redis.pool.testOnReturn")));
        jedisPool = new JedisPool(config, bundle.getString("redis.ip"),
                Integer.valueOf(bundle.getString("redis.port")));
        // 从池中获取一个Jedis对象
         jedis = jedisPool .getResource();
    }
    public void add(String sn) {
        jedis.sadd("snSet", sn);
        jedisPool.destroy();
    }
    public void remove(String sn) {
        jedis.srem("snSet", sn);
    }
    public boolean isExist(String sn) {
        Set<String> snSet = jedis.smembers("snSet");
        return snSet.contains(sn);
    }
    public static void main(String[] args) {
        String keys = "name";
// 删数据
        jedis.del(keys);
// 存数据
        jedis.set(keys, "snowolf");
// 取数据
        String value = jedis.get(keys);
        System.out.println(value);
// 释放对象池
//        jedisPool.returnResource(jedis);
    }
}

而直接通过代码实现的话,其实也是一个原理:

public class JedisUtil1 {
private static JedisUtil1 instance = null;
private Jedis jedis;
private static JedisPool jedisPool = null;
//Redis服务器IP
private static String HOST = "172.24.4.183";
//Redis的端口号
private static int PORT = 6379;
//可用连接实例的最大数目,默认值为8;
//如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
private static int MAX_ACTIVE = 1024;
//控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。
private static int MAX_IDLE = 200;
private static int TIMEOUT = 10000;
//在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
private static boolean TEST_ON_BORROW = true;
/**
 * 初始化Redis连接池
 */
static {
    try {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(MAX_ACTIVE);
        config.setMaxIdle(MAX_IDLE);
        config.setMaxWaitMillis(TIMEOUT);
        config.setTestOnBorrow(TEST_ON_BORROW);
        jedisPool = new JedisPool(config, HOST, PORT);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
  • Sentinel连接池连接

Sentinel连接池用于应对Redis的Sentinel的主从切换机制,能够正确在服务器宕机导致服务器切换时得到正确的服务器连接,当服务器采用该部署策略的时候推荐使用该连接池进行操作

private static Jedis jedis;
private static JedisSentinelPool jedisSentinelPool = null;

/**
 * 初始化Redis连接池
 */
static {
    ResourceBundle bundle = ResourceBundle.getBundle("jedisSentinePool");
    if (bundle == null) {
        throw new IllegalArgumentException(
                "[jedisSentinePool.properties] is not found!");
    }
    JedisPoolConfig config = new JedisPoolConfig();
    config.setMaxTotal(Integer.valueOf(bundle
            .getString("redis.pool.maxTotal")));
    config.setMaxIdle(Integer.valueOf(bundle
            .getString("redis.pool.maxIdle")));
    config.setMaxWaitMillis(Long.valueOf(bundle.getString("redis.pool.maxWait")));
    config.setTestOnBorrow(Boolean.valueOf(bundle
            .getString("redis.pool.testOnBorrow")));
    config.setTestOnReturn(Boolean.valueOf(bundle
            .getString("redis.pool.testOnReturn")));
    //监听器列表
    Set<String> sentinels = new HashSet<>();
    /**
     *
     //监听器1
     sentinels.add(new HostAndPort("172.24.4.183", 26379).toString());
     //监听器2
     sentinels.add(new HostAndPort("172.24.4.184", 26379).toString());
     //实际使用的时候在properties里配置即可:redis.sentinel.hostandports=172.24.4.183:26379, 172.24.4.184:26379
     //然后使用 bundle.getString("redis.sentinel.hostandports");获取地址
     */
    //mastername是服务器上的master的名字,在master服务器的sentinel.conf中配置
    String masterName = bundle.getString("redis.sentinel.masterName");
    sentinels.add(bundle.getString("redis.sentinel.hostandports"));
    //初始化连接池
    jedisSentinelPool = new JedisSentinelPool(masterName,
            sentinels, config);
    // 从池中获取一个Jedis对象
    jedis = jedisSentinelPool.getResource();
}
  • ShardedJedisPool连接池分片连接

Memcached完全基于分布式集群,而Redis是Master-Slave,Redis在容灾处理方面可以通过服务器端配置Master-Slave模式来实现。如果想把Reids做成集群模式,无外乎多做几套Master-Slave,每套Master-Slave完成各自的容灾处理,通过Client工具来实现一致性哈希分布存储,即key分片存储。

shared一致性哈希采用以下方案:

  1. Redis服务器节点划分:将每台服务器节点采用hash算法划分为160个虚拟节点(可以配置划分权重)
  2. 将划分虚拟节点采用TreeMap存储
  3. 对每个Redis服务器的物理连接采用LinkedHashMap存储
  4. 对Key or KeyTag 采用同样的hash算法,然后从TreeMap获取大于等于键hash值得节点,取最邻近节点存储;当key的hash值大于虚拟节点hash值得最大值时,存入第一个虚拟节点

sharded采用的hash算法:MD5 和 MurmurHash两种;默认采用64位的MurmurHash算法;有兴趣的可以研究下~

保留前面的JedisPoolConfig,新增两个Redis的IP(redis1.ip,redis2.ip),完成两个JedisShardInfo实例,并将其丢进List中:

private static ShardedJedis jedis;
private static ShardedJedisPool shardedJedisPool = null;
/**
 * 初始化Redis连接池
 */
static {
    ResourceBundle bundle = ResourceBundle.getBundle("sharedJedisPool");
    if (bundle == null) {
        throw new IllegalArgumentException(
                "[jedisPool.properties] is not found!");
    }
    JedisPoolConfig config = new JedisPoolConfig();
    config.setMaxTotal(Integer.valueOf(bundle
            .getString("redis.pool.maxTotal")));
    config.setMaxIdle(Integer.valueOf(bundle
            .getString("redis.pool.maxIdle")));
    config.setMaxWaitMillis(Long.valueOf(bundle.getString("redis.pool.maxWait")));
    config.setTestOnBorrow(Boolean.valueOf(bundle
            .getString("redis.pool.testOnBorrow")));
    config.setTestOnReturn(Boolean.valueOf(bundle
            .getString("redis.pool.testOnReturn")));
    JedisShardInfo jedisShardInfo1 = new JedisShardInfo(
            bundle.getString("redis1.ip"), Integer.valueOf(bundle.getString("redis.port")));
    JedisShardInfo jedisShardInfo2 = new JedisShardInfo(
            bundle.getString("redis2.ip"), Integer.valueOf(bundle.getString("redis.port")));
    List<JedisShardInfo> list = new LinkedList<>();
    list.add(jedisShardInfo1);
    list.add(jedisShardInfo2);
    shardedJedisPool = new ShardedJedisPool(config,list);
    // 从池中获取一个Jedis对象
    jedis = shardedJedisPool.getResource();
}

 

参考:

http://www.boyunjian.com/javadoc/org.apache.servicemix.bundles/org.apache.servicemix.bundles.jedis/2.1.0_1/_/redis/clients/jedis/JedisShardInfo.html

https://yq.aliyun.com/articles/236384

http://blog.csdn.net/dslztx/article/details/46743053

http://flyingsnail.blog.51cto.com/5341669/1371650

http://www.tuicool.com/articles/vaqABb

http://www.importnew.com/19321.html

http://www.cnblogs.com/liuling/p/2014-4-19-04.html

http://www.cnblogs.com/libaoting/p/4418007.html

http://blog.csdn.net/fachang/article/details/7984123

http://blog.csdn.net/aubdiy/article/details/53511410

http://blog.csdn.net/moxiaomomo/article/details/17588483

 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐