Java 之 Redis 及 Jedis 的应用

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

主要有这么几种方式:

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

进入正题

利用 Maven 添加 Jedis 的依赖 jar:

<!--这个注意,建议一般都选最新的-->  
<dependency> 
    <groupId>redis.clients</groupId> 
    <artifactId>jedis</artifactId>                      
    <version>2.9.0</version> 
    <type>jar</type> 
    <scope>compile</scope> 
</dependency>

基本使用

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

/** 
  * 单线程环境下使用,简单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; 

/** 
 * 单线程环境下使用,简单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 的类。

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; 

/** 
 * 多线程环境下,线程池的正确使用方法,单例的连接池,单例的配置。 
 * 此处给大家提供一个种思路,如果用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)高可用场景
JedisSentinelJedis 提供的哨兵模式的使用,我们都知道 Redis 支持 master 和 salve 模式,当发生故障的时候如何做专业。新版的 Redis 和 Jedis 已经做了很好的支持,来保证我们的 Reids 高可用,服务器端的配置这里忽略一下,我们看看 Jedis 的客户端下怎么写的。

/** 
 * 通过哨兵获得一个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 需要关心的类图

d0

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

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

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

  • 第一种做法通常初级程序员的做法:在 Service 方法中,在得到数据之前,先判断缓存里面有没有通过显示的调用 JedisUtil 类。如果没有就从 DB 层去捞取,然后再丢到 Redis 里面。在更新的时候显示的调用 RedisUtils 去更新缓存,其实这时候会发现大部分代码是重复的,很不优雅。
  • 第二种做法稍微资深一点程序员的做法:可能会考虑自定义的一个注解,放在每个方法上,有更新注解、有添加缓存注解,利用 @Aspect 拦截器机制,调用 JedisUtils 在拦截器里面处理上下文,其实这时候已经处理很好了,但是还有很多优化的地方。

未经允许不得转载:极客萌动 » Java 之 Redis 及 Jedis 的应用

赞 (2)

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址