连接池
Jedis是最常用的Redis客户端之一,它的方法、参数基本与Redis官方指令保持一致,节省了学习API的成本,
因为Java是多线程程序,所以一般要将Jedis对象放到一个连接池中,当每个线程需要进行Redis访问时,从连接池中获取Jedis对象, 代码如下:
JedisPool jedisPool = new JedisPool();
Jedis jedis = jedisPool.getResource();
// 业务逻辑...
jedis.set("hello", "world");
// 业务逻辑...
jedis.close();
不过这种写法存在一些漏洞,如果业务逻辑处理中发生了异常,那么下面的 jedis.close 就执行不到,会导致连接池中的连接都处于占用没有释放的状态,最后影响到其他新的请求使用jedis。
这时候需要用到 try finally 或者 try with resource 语句来及时释放连接,这样能保证即使发生了异常,也能将连接还给连接池。
public static void main(String[] args) {
JedisPool jedisPool = new JedisPool();
// try with resource
try(Jedis jedis = jedisPool.getResource()) {
// 业务逻辑...
jedis.set("hello", "world");
// 业务逻辑...
}
}
但是上面的写法可能不是每个开发同事都会使用 try ... 语法释放的,甚至有的close都懒得写,因此我们需要在代码使用上做一个约束,
获取Jedis这个操作由一个类全权负责,业务只需要编写自己的逻辑代码就好了,只需使用上层传过来的Jedis对象使用即可,不需要关心如果获取与关闭,代码如下:
static class RedisUtils {
private RedisUtils() {}
private static final JedisPool jedisPool = new JedisPool();
public static void execute(Consumer<Jedis> consumer) {
try(Jedis jedis = jedisPool.getResource()) {
consumer.accept(jedis);
}
}
}
public static void main(String[] args) {
RedisUtils.execute(jedis -> {
// 业务逻辑...
jedis.set("hello", "world");
// 业务逻辑...
});
}
可以看到,我们定义了一个 RedisUtils 类,并定义了一个 execute 方法,接收一个Consumer接口的实现,
业务代码只需要调用这个方法,并实现自己的业务逻辑即可,这个方法会自动传jedis实例给用户实现的接口,并且最后由 RedisUtils 自行关闭连接,这样就可以防止忘记关闭连接的情况。
闭包
由于Java闭包内不允许修改外面的变量的值,这种情况,可以考虑使用一个包装对象,将值传进去,作为修改。
增加一个包装类:
重试
有的时候,如果网络发生波动,指令没有发送出去,需要进行一次重新发送,也可以将这个逻辑放在上面的代码里去做。
static class RedisUtils {
private RedisUtils() {}
private static final JedisPool jedisPool = new JedisPool();
public static void execute(Consumer<Jedis> consumer) {
Jedis jedis = jedisPool.getResource();
try {
consumer.accept(jedis);
} catch (JedisConnectionException e) {
// 连接异常,重试一次
consumer.accept(jedis);
} finally {
jedis.close();
}
}
}
public static void main(String[] args) {
RedisUtils.execute(jedis -> {
// 业务逻辑...
jedis.set("hello", "world");
// 业务逻辑...
jedis.close();
});
}
代码地址:https://github.com/qiaomengnan16/redis-demo/tree/main/redis-jedis-pool