万字讲解连接池——Jedis连接池

Jedis连接池

  Redis连接池和数据库连接池一样,也是预先创建和管理一组连接,这样当需要与Redis服务器交互时,就可以直接复用连接。Redis的客户端JedisLettuce都实现了连接池的功能。我们本篇文章先以 Jedis为例,从连接的获取、归还、关闭、创建几个方面详细介绍具体的实现功能。

多线程的使用

  在数据库中如果多个线程复用一个连接会存在数据库事务问题,那么在Redis中我们先来看下在多线程环境下使用一个连接会产生什么问题?

  首先启动两个线程,共同操作同一个 Jedis 实例,每一个线程循环 500 次,分别读取 Key 为 a 和 b 的值

 

java

复制代码

Jedis jedis = new Jedis("127.0.0.1", 6379); new Thread(() -> { for (int i = 0; i < 500; i++) { String result = jedis.get("a"); System.out.println(result); } }).start(); new Thread(() -> { for (int i = 0; i < 500; i++) { String result = jedis.get("b"); System.out.println(result); } }).start();

  执行程序多次,可以看到日志中出现了各种奇怪的异常信息,有的未知答复错误,还有的是连接关闭异常等

错误1:redis.clients.jedis.exceptions.JedisConnectionException: Unknown reply: 1

错误2:java.io.IOException: Socket Closed

  那我们先来看下 Jedis常用的(3.x)版本的源码

 

java

复制代码

public class Jedis extends BinaryJedis implements JedisCommands, MultiKeyCommands, AdvancedJedisCommands, ScriptingCommands, BasicCommands, ClusterCommands, SentinelCommands, ModuleCommands{} public class BinaryJedis implements BasicCommands, BinaryJedisCommands, MultiKeyBinaryCommands, AdvancedBinaryJedisCommands, BinaryScriptingCommands, Closeable { protected final Client client; } public class Client extends BinaryClient implements Commands {} public class BinaryClient extends Connection {} public class Connection implements Closeable { private Socket socket; private RedisOutputStream outputStream; private RedisInputStream inputStream; }

  首先Jedis 继承了 BinaryJedisBinaryJedis 中保存了单个 Client 的实例,Client最终继承了 ConnectionConnection 中保存了单个 Socket 的实例以及对应的两个读写流一个 RedisOutputStream 一个是 RedisInputStream

image.png

  BinaryClient 封装了各种 Redis 命令,其最终会调用的是 sendCommand 方法,发现其发送命令时是直接操作 RedisOutputStream 写入字节。

 

java

复制代码

private static void sendCommand(final RedisOutputStream os, final byte[] command, final byte[]... args) { try { os.write(ASTERISK_BYTE); os.writeIntCrLf(args.length + 1); os.write(DOLLAR_BYTE); os.writeIntCrLf(command.length); os.write(command); os.writeCrLf(); for (final byte[] arg : args) { os.write(DOLLAR_BYTE); os.writeIntCrLf(arg.length); os.write(arg); os.writeCrLf(); } } catch (IOException e) { throw new JedisConnectionException(e); } }

  所以在多线程环境下使用 Jedis ,其实就是在复用RedisOutputStream。如果多个线程在执行操作,那么无法保证整条命令是原子写入 Socket。比如,写操作互相干扰,多条命令相互穿插的话,必然不是合法的 Redis 命令也就导致等等各种问题。

  这也说明了Jedis是非线程安全。但是可以通过JedisPool连接池去管理实例,在多线程情况下让每个线程有自己独立的Jedis实例,可变为线程安全。

 

java

复制代码

//使用redis连接池,不会有线程安全问题 private static JedisPool jedisPool = new JedisPool("127.0.0.1", 6379); new Thread(() -> { try (Jedis jedis = jedisPool.getResource()) { for (int i = 0; i < 1000; i++) { String result = jedis.get("a"); System.out.println(result); } } }).start(); new Thread(() -> { try (Jedis jedis = jedisPool.getResource()) { for (int i = 0; i < 1000; i++) { String result = jedis.get("b"); System.out.println(result); } } }).start();

连接池的管理

  JedisPool的连接池是基于 Apache Commons Pool

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值