前言
通过《redis功能实现》的了解,并且redis官方提供了《JAVA Redis客户端》。其中Jedis,lettuce提供对redis基本封装, redisson为高级封装。
三架构对比
针对redis java client,从多角度进行选型对比
Jedis | Lettuce | Redisson | |
---|---|---|---|
地址 | https://github.com/xetorthio/jedis | https://lettuce.io/ | https://github.com/redisson/redisson |
what | 且健全的redis java客户端,支持redis的所有特性和命令,如事务、管道、发布订阅。 | 线程安全的redis客户端,封装同步、异步、交互API。不执行阻塞和事务操作时,多线程可共享连接。 | 线程安全的redis客户端,支持多场景,提供基于redis的某些分布式服务解决方案 |
实现与使用 | 实现简单,使用简单 | 实现较复杂,使用简单 | 实现复杂,使用简单 |
网络 | 阻塞IO | Netty | Netty |
redis命令特性支持 | redis命令与特性提供健全的支持 | redis命令与特性支持 | redis命令和特性支持 |
抽象封装程度 | 没有做特别的抽象,特性使用是否正确依赖使用者。 | 同步、异步、交互场景封装 | 丰富的数据模型、分布式服务、特性的封装,第三方框架的扩展实现 |
操作的维度 | 指令维度的操作 | 指令维度的操作 | 使用对象、服务将redis指令和业务分离,对象维度的操作,使用更方便,没有使用指令灵活,支持redisClient执行指令;分布式服务特性缺乏管理平台。 |
redis连接 | 每次操作均需要从连接池中获取连接,线程间不可以共享连接,高并发时需要考虑连接过多对client和server的影响。 | 共享连接,连接是long-lived和线程安全的,而且自动重连 | 共享连接 |
client分片 | 支持,提供实现 | 不支持,未提供实现 | 不支持,针对特殊数据模型提数据分片 |
read slave | 未找到使用slave执行read指令的API,但可以自行实现 | 支持slave执行read指令 | 支持slave执行read指令 |
排他+超时 | 支持 | 支持 | 需要使用封装的数据模型 |
社区维护 | 社区维护好,版本更新快 | 社区维护一般,版本更新较慢 | 分为企业版和开源版,维护好,版本更新快 |
Jedis
jedis主要采用连接池+bio的方式完成jedis的通试。
- Jedis.keys方法
// redis.clients.jedis.Jedis最终会调用以下方法
// redis.clients.jedis.Connection
public void sendCommand(final ProtocolCommand cmd, final byte[]... args) {
try {
// 以bio的方式连接
connect();
// 组装命令向outputStream写入协议
Protocol.sendCommand(outputStream, cmd, args);
} catch (JedisConnectionException ex) {
// ...
}
}
- JedisPool连接池接入
// JedisPool最终会通过些访求进行初始化
// poolConfig 实现类 JedisPoolConfig 主要提供连接commons.pool2连接池的配置参数
// factory 实现类 RedisPooledObjectFactory 主要实现commons.pool2资源创建接口PooledObjectFactory
public void initPool(final GenericObjectPoolConfig poolConfig, PooledObjectFactory<T> factory) {
this.internalPool = new GenericObjectPool<>(factory, poolConfig);
}
- JedisPool例子
public static void main(String[] args) throws Exception {
JedisPool jedisPool = new JedisPool("localhost", 6379);
// 不可以多线程使用
Jedis jedis = jedisPool.getResource();
System.out.println(jedis.get("foo"));
jedis.close();
}
lettuce
lettuce使用netty + nio,复用同一个连接。《netty连接redis例子》
- 通过PlainChannelInitializer实现ChannelInitializer接入netty
- CommandHandler 为核心的命令入口
- 通过ArrayDeque<RedisCommand<?, ?, ?>> stack 并结合CompletableFuture实现命令的发送和接收的关系
public static void main(String[] args) throws Exception {
RedisURI redisUri = RedisURI.Builder.redis("localhost")
.withDatabase(1)
.build();
RedisClient client = RedisClient.create(redisUri);
// connection普通命令多线程复用,但是使用事务不可以
StatefulRedisConnection<String, String> connection = client.connect();
RedisCommands<String, String> sync = connection.sync();
System.out.println(sync.get("foo"));
connection.close();
}
Redisson
redisson也是使用netty + nio,复用同一个连接。《netty连接redis例子》
- 通过RedisChannelInitializer实现ChannelInitializer接入netty
- CommandDecoder,CommandEncoder 为核心的命令入口
- 通过CommandsQueue 并结合CompletableFuture 子类 RPromise 实现命令的发送和接收的关系
public static void main(String[] args) {
RedisClientConfig redisClientConfig = new RedisClientConfig();
redisClientConfig.setAddress("redis://127.0.0.1:6379");
RedisClient client = RedisClient.create(redisClientConfig);
// connection普通命令多线程复用,但是使用事务不可以
RedisConnection redisConnection = client.connect();
// 封装了个命令对象
// @see RedisCommands.SET
RedisCommand<String> SET = new RedisCommand<>("SET");
String setValue = redisConnection.sync(SET, "foo", "yoyo1");
System.out.println(setValue);
RedisCommand<String> GET = new RedisCommand<>("GET");
String value = redisConnection.sync(GET, "foo");
System.out.println(value);
}
以下是Redisson比较热门的高级封装
- Redis分布式锁
- 布隆过滤器