第01课:Redis

第01课:Redis 之 Jedis 的使用

Jedis 是最受欢迎的 Redis 的 Java 版本的 Client 的实现端。这种使用方式属于裸用,就是不加任何修饰,直接通过 Jedis 操作 Redis 的 N 多特性。

主要有这么几种方式:

  • 基本使用;
  • 连接池的使用;
  • 高可用连接(master/salve);
  • 客户端分片。

通过本节来体验一下 Jedis 的传统模式下是如何使用的。

条件加入 Jedis 的 jar 依赖

利用 Maven 添加 Jedis 的依赖 jar:

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

#### 基本使用

(1)单线程环境下使用:

/**

  • Created By jack on 16/12/2017
  • 单线程环境下使用,简单Util **/ public class JedisClientUtil { public static void main(String[] args) { Jedis jedis = new Jedis("localhost",6379); jedis.set("foot", "bar"); String value = jedis.get("foot"); //通过这种方式就可以直接使用redis里面的很多命令了 } }
(2)单线程环境的正确使用姿势如下,但是在实际环境中,我们(1)里面的写法可能过于简单,真正再生产模式下,写法如下:

package com.example.redis.utils;

import org.springframework.beans.factory.annotation.Value; import redis.clients.jedis.Jedis;

/**

  • Created By jack on 16/12/2017

  • 单线程环境下使用,简单Util

  • 正常正式开发中,会把Jedis包装在一个单利模式中,避免每次都去重新连接,把localhost和port放到properties的配置文件中 **/ public class JedisClientUtil { @Value("{spring.redis.host}") private String host; @Value("{spring.redis.port}") private Integer port;

    private final byte[] temp_lock = new byte[1]; private Jedis jedis;

    private JedisClientUtil(){}

    public Jedis getRedisClient() { if (jedis == null) { synchronized (temp_lock) { if (jedis == null) { jedis = new Jedis(host,port); } } } return jedis; } public static void main(String[] args) { // @Autowired // JedisClientUtil jedisClientUtil; // 如果在其他地方使用,直接Autowired即可。 JedisClientUtil jedisClientUtil = new JedisClientUtil(); Jedis jedis = jedisClientUtil.getRedisClient(); try { jedis.set("foot", "bar"); String value = jedis.get("foot"); System.out.println(value); } finally { //注意关闭 jedis.close(); } } } ```

连接池的使用

(1)多线程环境的正确使用姿势

一般正常工作中很少有单线程模式,在 Web 环境下都是多线程进行的,这个时候引入连接池的概念来帮我们管理各个连接。简单概念提一下,引入连接池是为了管理连接对象,也就是 Jedis 对象可能要从一个池里面取,所以 Jedis 提供了 JedisPool 的类。

PS:连接池、线程池、线程概念不清楚的,可以出门右拐,看我的另外一篇 Chat 哦

public class JedisClientPoolUtil {
    public static void main(String[] args) {
        JedisPool pool = new JedisPool(new JedisPoolConfig(), "127.0.0.1",6379);
        Jedis jedis = pool.getResource();
        try {
    jedis.set("foot", "bar");
    String value = jedis.get("foot");
    System.out.println(value);
} finally {
    //注意关闭
    jedis.close();
}
    }
}

(2)工作中一般会做如下改进,来保证可用性。

package com.example.redis.utils;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Created By jack on 16/12/2017
 *
 * 多线程环境下,线程池的正确使用方法,单例的连接池,单例的配置。
 * 此处给大家提供一个种思路,如果用spring boot的话,可以基于@Configuration 和@Bean的配置方法,此处仅仅是举例说明。
 **/
@Component
public class JedisClientPoolUtil {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private Integer port;

    private final static byte[] temp_lock = new byte[1];
    private JedisPool jedisPool;

    /**
     * 把连接池做成单例的,这点需要注意
     *
     * @return
     */
    private JedisPool getJedisPool() {
        if (jedisPool == null) {
            synchronized (temp_lock) {
                if (jedisPool == null) {
                    jedisPool = new JedisPool(jedisPoolConfig(),host,port);
                }
            }
        }
        return jedisPool;
    }

    /**
     * 设置一些连接池的配置,来管理每一个连接。
     *
     * @return
     */
    private JedisPoolConfig jedisPoolConfig(){
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(20);
        jedisPoolConfig.setMaxIdle(10);
        jedisPoolConfig.setMaxWaitMillis(1000);
        return jedisPoolConfig;
    }

    /**
     * 对外只暴露这一个方法即可
     *
     * @return
     */
    public Jedis getJedis(){
        return getJedisPool().getResource();
    }
    public static void main(String[] args) {
//        @Autowired
//        JedisClientPoolUtil jedisClientPoolUtil;
//        如果在其他地方使用,直接Autowired即可。
        JedisClientPoolUtil jedisClientPoolUtil = new JedisClientPoolUtil();
        Jedis jedis = jedisClientPoolUtil.getJedis();
        try {
            jedis.set("foot", "bar");
            String value = jedis.get("foot");
            System.out.println(value);
        } finally {
            //注意关闭

            jedis.close();
        }
    }
}

高可用连接(master/salve)

(1)高可用场景 JedisSentinel

Jedis 提供的哨兵模式的使用,我们都知道 Redis 支持 master 和 salve 模式,当发生故障的时候如何做专业。新版的 Redis 和 Jedis 已经做了很好的支持,来保证我们的 Reids 高可用,服务器端的配置这里忽略一下,我们看看 Jedis 的客户端下怎么写的。

/**
 * Created By jack on 16/12/2017
 * 通过哨兵获得一个Master连接,DEMO
 **/
public class JedisSentinelPoolUtil {
    public static void main(String[] args) {
        //添加N个哨兵,当添加的时候,此时如果去看源码的化就会发现,顺带通过哨兵帮我们初始化了一个master连接地址
        JedisSentinelPool pool = new JedisSentinelPool("redis_master_name",Sets.newHashSet("127.0.0.1:63791","127.0.0.1:63792"));
        //通过哨兵获得Master节点,如果有问题会重新通过哨兵获得一个Master节点
        Jedis jedis = pool.getResource();
        try {
            jedis.set("foot", "bar");
            String value = jedis.get("foot");
        } finally {
            //注意关闭
            jedis.close();
        }
    }
}

(2)生产正确姿势

和上面连接池的用法一样,也需要建立一个单利模式来获得 Pool,然后根据 Pool 对调用者提供 Jedis 的使用,此处不再重复叙述。

客户端分片

/**
 * 简单测试切片的写法
 */
public class ShardedJedisPoolUtil {
   public static void main(String[] args) {
      List<JedisShardInfo> shards = Lists.newArrayList();
      shards.add(new JedisShardInfo("127.0.0.1",6379));
      shards.add(new JedisShardInfo("127.0.0.1",6378));
      //通过list可以创建N个切片
      ShardedJedisPool shardedJedisPool = new ShardedJedisPool(new GenericObjectPoolConfig(),shards);
      ShardedJedis shardedJedis =shardedJedisPool.getResource();
      shardedJedis.set("key1","abc");
      System.out.println(shardedJedis.get("key1"));
   }
}

Cluster 和 Sentinel 的应用场景和使用方法基本上同理,目前切片个人觉得 Jedis 实现的还不是特别成熟,这里就不多说了,感兴趣的读者可以私下交流。

Jedis 需要关心的类图

enter image description here

其实 Jedis 的客户端相对来说比较简单,主要的类如图,底层原理就是基于 Socket 创建连接,然后通过 redisClient 发送 Redis 的命令到服务器端。

此章节介绍了基于 Jedis 的比较常见的配置方法,后面带读者领略一下 Spring 体系下面怎么玩?

Jedis 实际工作中的正确使用场景

最常见的场景就是对 Service 这层的数据加缓存。

  • 第一种做法

通常初级程序员的做法:在 Service 方法中,在得到数据之前,先判断缓存里面有没有通过显示的调用 JedisUtil 类。如果没有就从 DB 层去捞取,然后再丢到 Redis 里面。在更新的时候显示的调用 RedisUtils 去更新缓存,其实这时候会发现大部分代码是重复的,很不优雅。

  • 第二种做法

稍微资深一点程序员的做法:可能会考虑自定义的一个注解,放在每个方法上,有更新注解、有添加缓存注解,利用 @Aspect 拦截器机制,调用 JedisUtils 在拦截器里面处理上下文,其实这时候已经处理很好了,但是还有很多优化的地方。

  • 第 N 种做法

作者比较推荐的,请继续往后面的章节中寻找答案。

上一篇
下一篇
目录