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 需要关心的类图
其实 Jedis 的客户端相对来说比较简单,主要的类如图,底层原理就是基于 Socket 创建连接,然后通过 redisClient 发送 Redis 的命令到服务器端。
Jedis 实际工作中的正确使用场景
最常见的场景就是对 Service 这层的数据加缓存。
- 第一种做法通常初级程序员的做法:在 Service 方法中,在得到数据之前,先判断缓存里面有没有通过显示的调用 JedisUtil 类。如果没有就从 DB 层去捞取,然后再丢到 Redis 里面。在更新的时候显示的调用 RedisUtils 去更新缓存,其实这时候会发现大部分代码是重复的,很不优雅。
- 第二种做法稍微资深一点程序员的做法:可能会考虑自定义的一个注解,放在每个方法上,有更新注解、有添加缓存注解,利用 @Aspect 拦截器机制,调用 JedisUtils 在拦截器里面处理上下文,其实这时候已经处理很好了,但是还有很多优化的地方。
未经允许不得转载:极客萌动 » Java 之 Redis 及 Jedis 的应用