封装一个属于自己的Redis API

前言

Redis作为一款强大的key-value型数据库,其应用是十分广泛的。在Java语言中,常用来与Redis数据库建立连接用到的是Jedis Pool连接池。

今天我们来简单了解下它们然后实现一个可移植的操作Redis的API。

正文

知识准备

我们知道Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。并提供了一系列的命令操作这些数据类型。

Jedis相当于对这些操作进行了代码封装,及提供了一些其它常用操作。

我们先来了解下Jedis的连接池配置参数。

commons-pool2 有一个配置类GenericObjectPoolConfig里面的通用参数设置如下:

参数说明默认值备注
maxTotal说明一个pool最多可以有多少个Jedis实例8-1表示不限制
maxIdle一个pool最多可以有多少个空闲的Jedis实例8
minIdle一个pool最少有多少个空闲的Jedis实例0

可以看到它继承BaseObjectPoolConfig。我们可以看到BaseObjectPoolConfig的参数如下。

upload successful

部分参数意义如下:

参数说明默认值备注
lifopool中的idle列表是双端队列,设定是否last in first outtrue
maxWaitMillis当active数量为max时,等待的时长-1L(代表一直等)配合blockWhenExhausted使用
blockWhenExhausted当active数量为max时,是否阻塞等待一段时间true
testOnCreate创建实例时有效性检测false
testOnReturn归还实例时有效性检测false
testOnBorrow借出实例时有效性检测false

思路分析

首先Redis连接池属性我们应当放置在配置文件里,解析并获得,连接池最好设计成单例的,每次不用在初始化过多连接资源。同时Redis有单机模式和集群模式区分,这两种模式我们也应该区分开来。单机模式下,可以选择多个database,集群模式下只能选择database0.集群模式下,如果redis地址过多,我们如何分开呢?

我们可以考虑如下样式:
address =127.0.0.1:6379;127.0.0.1:6380

每个redis地址用分号分隔,解析配置时把每个解析到并建立连接。

当然,最后完成JedisPool的创建后,我们应该编写工具类对一些常用操作方法进行封装,便于我们使用。

代码

我们根据上述思路,构造了如下图所示的小项目。

upload successful

其中:

RedisException是用来统一处理程序过程中的异常的类。

JedisFactory可以认为是一个JedisPool工厂,用来提供单机模式的连接池或者集群模式的连接池。

RedisConfiguration是与配置文件对应的配置类,用于存放配置的数据。

RedisConstants用来放置一些项目中用到的常量。

RedisUtil工具类接口,提供了多种操作Redis的方法。

RedisSingleUtil工具接口的单机模式实现。

RedisClusterUtil工具接口的集群模式实现。

redis-config.properties Redis的配置文件存放

JedisFactory和RedisUtill为主要类。我们看下他们的具体实现。

JedisFactory的主要代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
private volatile JedisPool jedisPool;
private volatile JedisCluster jedisCluster;
private RedisConfiguration redisConfig;
private Pattern addRessPattern = Pattern.compile("^.+[:]\\d{1,5}\\s*(;.+[:]\\d{1,5}\\s*)*[;]?\\s*$");

public JedisFactory(final RedisConfiguration redisConfiguration){
this.redisConfig=redisConfiguration;
}

public JedisPool getJedisPool(){
if(jedisPool==null){
synchronized (JedisFactory.class){
if(jedisPool==null){
init();
}
}
}
return jedisPool;
}

public JedisCluster getJedisCluster(){
if(jedisCluster==null){
synchronized (JedisFactory.class){
if(jedisCluster==null){
init();
}
}
}
return jedisCluster;
}

public void init(){
logger.info("JedisFactory init start...");
try{
if(StringUtils.isNotBlank(redisConfig.getLocalPropertiesPath())){
fillData();
}
logger.info("redis config is: {}.", redisConfig.toString());
Set<HostAndPort> hostAndPortSet = this.parseHostAndPort(redisConfig.getAddress());

GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
genericObjectPoolConfig.setMaxWaitMillis(redisConfig.getMaxWaitMillis());
genericObjectPoolConfig.setMaxTotal(redisConfig.getMaxTotal());
genericObjectPoolConfig.setMinIdle(redisConfig.getMinIdle());
genericObjectPoolConfig.setMaxIdle(redisConfig.getMaxIdle());

if(redisConfig.getMode()== RedisConstants.REDIS_MODE_SINGLE){
HostAndPort hostAndPort=(HostAndPort)hostAndPortSet.toArray()[0];
jedisPool=new JedisPool(genericObjectPoolConfig, hostAndPort.getHost(), hostAndPort.getPort(), redisConfig.getTimeout(), null,redisConfig.getDatabase());
logger.info("jedisPool init is finished");
}else{
if(redisConfig.getDatabase()!=0){
logger.warn("当前配置的database为:"+redisConfig.getDatabase()+",集群模式下不能选择database,只能使用database0");
}
jedisCluster = new JedisCluster(hostAndPortSet, redisConfig.getTimeout(), redisConfig.getMaxRedirections(), genericObjectPoolConfig);
logger.info("jedisCluster init is finished");
}

}catch(Exception ex){
throw new RedisException(ex);
}


}

private void fillData() throws Exception {

Properties localProperties = PropertiesUtils.loadLocalProperties(redisConfig.getLocalPropertiesPath());

String address=localProperties.getProperty("address", "");
if (StringUtils.isBlank(address)) {
throw new RedisException("error:redis config address is blank!");
}

// 设置初始值
long maxWaitMillis=Long.parseLong(localProperties.getProperty("maxWaitMillis", String.valueOf(GenericObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS)));
int maxTotal=Integer.parseInt(localProperties.getProperty("maxTotal", String.valueOf(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL)));
int minIdle=Integer.parseInt(localProperties.getProperty("minIdle", String.valueOf(GenericObjectPoolConfig.DEFAULT_MIN_IDLE)));
int maxIdle=Integer.parseInt(localProperties.getProperty("maxIdle", String.valueOf(GenericObjectPoolConfig.DEFAULT_MAX_IDLE)));
int timeout=Integer.parseInt((localProperties.getProperty("timeout", "2000")));
int maxRedirections=Integer.parseInt((localProperties.getProperty("maxRedirections", "6")));
int database=Integer.parseInt((localProperties.getProperty("database", "0")));
//1单机模式,2集群模式
int mode=Integer.parseInt((localProperties.getProperty("mode", String.valueOf(RedisConstants.REDIS_MODE_SINGLE))));

redisConfig.setAddress(address);
redisConfig.setMaxWaitMillis(maxWaitMillis);
redisConfig.setMaxTotal(maxTotal);
redisConfig.setMinIdle(minIdle);
redisConfig.setMaxIdle(maxIdle);
redisConfig.setTimeout(timeout);
redisConfig.setMaxRedirections(maxRedirections);
redisConfig.setDatabase(database);
redisConfig.setMode(mode);

}
//部分代码略

对于RedisUtil接口,应有两个实现,单机和集群的,这里为了简化代码,只简单列举了一个方法。

1
2
3
4
public interface RedisUtil {
String setString(String key, String value);
//部分代码略
}

单机模式的实现:

1
2
3
4
5
6
7
8
9
10
11
12
public class RedisSingleUtil implements RedisUtil{
@Override
public String setString(String key, String value) {
Jedis jedis = this.getResource();
try{
return jedis.set(key, value);
}finally{
this.closeResource(jedis);
}
}
//部分代码略
}

集群模式的实现:

1
2
3
4
5
6
7
8
public class RedisClusterUtil implements RedisUtil{
@Override
public String setString(String key, String value) {
JedisCluster cluster=getResource();
return getResource().set(key, value);
}
//部分代码略
}

其他方法及实现不在赘述,有兴趣的可以在

https://github.com/javazwt/framework-base 上查看相关代码。

测试

1
2
3
4
5
6
7
public class RedisTest {
private static RedisSingleUtil redisSingleUtil=new RedisSingleUtil();
public static void main(String[] args) {
redisSingleUtil.setString("str","123");
redisSingleUtil.getString("str");
}
}

可以检测我们的正确性。

结束语

经过封装后,我们可以把该工具类使用在任何项目上,提高开发效率,降低项目耦合性,同时对Redis有了更深入的认知。




-------------文章结束啦 ~\(≧▽≦)/~ 感谢您的阅读-------------

您的支持就是我创作的动力!

欢迎关注我的其它发布渠道