Jedis
理论
Jedis是redis的java版本的客户端实现,使用Jedis提供的Java API对Redis进行操作,是Redis官方推崇的方式;并且,使用Jedis提供的对Redis的支持也最为灵活、全面;不足之处,就是编码复杂度较高。
引入包
<!--Jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.1</version>
</dependency>
案例演示
直连案列
获取Redis连接对象
// 获取Jedis对象
private static Jedis getJedis() {
Jedis jedis = new Jedis("自己的地址", 端口);
jedis.auth("111111");
return jedis;
}
Redis 案列
Jedis jedis = getJedis();
//geo
String geo_key = getKey();
jedis.geoadd(geo_key, getGeo());
logger.info(String.valueOf(jedis.geodist(geo_key, "成都", "上海", GeoUnit.KM)));
private static String getKey() {
return "TOC-" + RandomUtil.randomString(6);
}
private static Map<String, GeoCoordinate> getGeo() {
Map<String, GeoCoordinate> geoCoordinateHashMap = new HashMap<>(5);
geoCoordinateHashMap.put("成都", new GeoCoordinate(103.954887, 30.569293));
geoCoordinateHashMap.put("北京", new GeoCoordinate(116.427185, 39.93682));
geoCoordinateHashMap.put("上海", new GeoCoordinate(121.477665, 31.236176));
geoCoordinateHashMap.put("西安", new GeoCoordinate(108.952789, 34.36515));
geoCoordinateHashMap.put("重庆", new GeoCoordinate(106.454377, 29.581309));
return geoCoordinateHashMap;
}
关闭Jedis对象
jedis.close();
池案列
获取连接池对象
JedisPool jedisPool = getJedisPool();
Jedis jedis = jedisPool.getResource();
// 获取Jedispoll对象
private static JedisPool getJedisPool() {
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
poolConfig.setMaxIdle(8);
poolConfig.setMinIdle(2);
poolConfig.setMaxWait(Duration.ofSeconds(30000));
return new JedisPool(poolConfig, "自己的ip", 端口, 100000, "111111");
}
代码案列
//hash
String hash_key = getKey();
jedis.hset(hash_key, getMapStr());
for (String hkey : jedis.hkeys(hash_key)) {
logger.info(jedis.hget(hash_key, hkey));
}
关闭Jedis和Pool
try {
if (!Objects.isNull(jedis)) {
jedis.close();
}
} finally {
jedis = null;
}
try {
if (Objects.isNull(jedisPool)){
jedisPool.close();
}
} finally {
jedisPool = null;
}
总结
|-----|--------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------|
| | 优点 | 缺点 |
| 直连 | 1.简单方便new-close 2.适用于连接数比较少且使用时间较长,可构成长连接的场景(只使用一个Jedis,比如down数据、大量数据更新) | 1.存在每次新建和关闭TCP的开销(三次握手四次挥手) 2.每次都去new,系统资源可能无法在有效范围内进行控制,会存在连接容易发生泄漏 3.Jedis对象本身是线程不安全的 |
| 池连接 | 1.Jedis是预先生成的,不需要随用随创,随用完随关闭,降低了这些开销 2.连接池能够更好地保护和控制资源使用,有固定的参数去控制最大连接数或者空闲数目等 | 1.相对直连,使用起来麻烦,特别是资源管理上需要非常多的参数来保证,一旦出现规划不合理的情况就会出现问题(比如池满、连接空闲、连接超时等情况) |
Lettuce
理论
Lettuce 是一个可伸缩线程安全的 Redis 客户端。多个线程可以共享同一个 RedisConnection。它利用优秀 netty NIO 框架来高效地管理多个连接。
引入包
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.2.5.RELEASE</version>
</dependency>
案列演示
获取连接对象
private static StatefulRedisConnection<String, String> getRedis() {
// 链式编程 创建RedisUri
RedisURI build = RedisURI.builder().withHost("120.77.64.190").withPort(6379).withPassword("111111".toCharArray()).build();
//创建客户端
StatefulRedisConnection<String, String> conn = RedisClient.create().connect(build);
return conn;
}
StatefulRedisConnection<String, String> conn = getRedis();
获取操作命令对象
//链接command
RedisCommands<String, String> jedis = conn.sync();
案列
//geo
String geo_key = getKey();
jedis.geoadd(geo_key, 103.954887, 30.569293,"成都");
jedis.geoadd(geo_key, 121.477665, 31.236176,"上海");
logger.info(String.valueOf(jedis.geodist(geo_key, "成都", "上海", GeoArgs.Unit.km)));
关闭对象
private static void closeConn(StatefulRedisConnection<String, String> conn) {
// 关闭
try {
if (!Objects.isNull(conn)){
conn.close();
}
}catch (Exception e){
System.out.println(e);
}finally {
conn = null;
}
}
总结
Lettuce 相比Jedis 客户端,功能更加强大,不仅解决了线程安全的问题,还支持异步和响应式编程,支持集群,Sentinel,管道,编码器等等功能。
spring-data-redis
单机
理论
RedisTemplate 继承自 RedisAccessor , 实现 RedisOperations 和 BeanClassLoaderAware 两个接口。spring-data-redis针对Jedis提供如下功能:
提供了一个高度封装的"RedisTemplate"类,里面封装了对于Redis的五种数据结构的各种操作
SpringBoot2.x后,RedisTemplate采用是lettuce(基于netty采用异步非阻塞式lO)进行通信,大并发下比jedis效率更高。
RedisTemplate模板使用序列化器操作redis数据
引入包
java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
案列演示
配置文件
java
server:
port: 8080
spring:
application:
name: mop
redis:
host: 自己的ip
port: 6379
database: 0
password: 111111
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 3s
未加序列化配置
加入序列化配置
默认走JDK序列化
加入自定义配置
插入数据后,客户端显示正常
后续案列可自行实践
集群
集群配置可参考 【Redis 7 第八讲 集群模式(cluster)架构篇】,集群搭建如下
配置文件
java
server:
port: 8080
spring:
application:
name: mop
redis:
cluster:
max-redirects: 3
nodes:
- 自己的IP:端口
- 自己的IP:端口
- 自己的IP:端口
- 自己的IP:端口
- 自己的IP:端口
- 自己的IP:端口
database: 0
password: 111111
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: 3s
案列演示
意外模拟6381宕机,从机上位验证代码是否正常
写入时出现无法连接
主机宕机,从机上位 原因分析:
SpringBoot 客户端无法动态感知到集群最新变化。
SpringBoot 2.X版本, Redis默认的连接池采用 Lettuce当Redis 集群节点发生变化后,Letture默认是不会刷新节点拓扑
加入配置参数,动态刷新
java
server:
port: 8080
spring:
application:
name: mop
redis:
cluster:
max-redirects: 3
nodes:
- IP:PORT
- IP:PORT
- IP:PORT
- IP:PORT
- IP:PORT
- IP:PORT
database: 0
password: 111111
lettuce:
cluster:
refresh:
adaptive: true
period: 2000
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
java
#支持集群拓扑动态感应码斯,户适应拓扑树新是查使用所有可用的更斯,默认false 关闭spring.redis.lettuce.cluster .refresh,adaptive=true
#定时刷新
spring.redis,lettuce.cluster.refresh.period-2000