文章目录
一、Redis的Java客户端
java客户端官网:https://redis.io/resources/clients/#java
1、Jedis
以Redis命令作为方法名称,学习成本低,简单使用。但是Jedis实例是线程不安全的,多线程环境下需要基于连接池来使用。
(1)Jedis操作Redis
官网地址:https://github.com/redis/jedis
Jedis使用的基本步骤:
- 引入依赖。
- 创建Jedis对象,建立连接。
- 使用Jedis,方法名与Redis命令一致。
- 释放资源。
(2)Jedis连接池
Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此推荐大家使用Jedis连接池代替Jedis的直连方式。
Jedis非线程安全问题可以参考文章:Jedis非线程安全问题 ,该文章总结了Jedis非线程安全的原因:
- 共享socket引起的异常。
- 共享数据流引起的异常。
2、lettuce
Lettuce是基于Netty实现的,支持同步、异步和响应式编程方式,并且线程安全的。支持Redis的哨兵模式、集群模式和管道模式。
3、Redisson
Redisson是一个基于Redis实现的分布式、可伸缩的Java数据结构集合。包含了诸如Map、Queue、Lock、Semaphore、AtomicLong等强大功能。
4、SpringDataRedis客户端
(1)介绍
SpringData是Spring中数据操作的模块,包含了对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis,官网地址:https://spring.io/projects/spring-data-redis:
- 提供了对不同Redis客户端的整合(Lettuce和Jedis)。
- 提供了RedisTemplate统一API来操作Redis。
- 支持Redis的发布订阅模型。
- 支持Redis哨兵和Redis集群。
- 支持基于Lettuce的响应式编程。
- 支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化。
- 支持基于Redis的JDKCollection实现。
SpringDataRedis中提供了RedisTemplate工具类,其中封装了各种对Redis的操作。并且将不同数据类型的操作API封装到了不同的类型中:
(2)序列化
-
JDK
RedisTemplate可以接收任意Object作为值写入redis,只不过写入前会把Object序列化为字节形式,默认是采用JDK序列化,形式:
- 缺点
- 可读性差
- 内存占用较大
- 序列化对象需要实现Serializable接口
- 缺点
-
JSON
这里采用了JSON序列化来代替默认的JDK序列化方式,结果:
整体可读性有了很大提升,并且能将Java对象自动的序列化为JSON字符串,并且查询时能自动把JSON反序列化为Java对象。不过,其中记录了序列化时对应的class名称,目的是为了查询时实现自动反序列化。这会带来额外的内存开销。
(3)StringRedisTemplate
为了节省内存空间,我们可以不使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。因为存入和读取时的序列化及反序列化都是我们自己实现的,SpringDataRedis就不会将class信息写入Redis了。这种用法比较普遍,因此SpringDataRedis就提供了RedisTemplate的子类:StringRedisTemplate,它的key和value的序列化方式默认就是String方式。
二、jedis连接工具类
import cn.hutool.core.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.util.CollectionUtils;
import redis.clients.jedis.*;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
@Slf4j
public class RedisOperateUtil {
private final static Map<String, ConnectStrategy> CONNECT_CACHE = new HashMap<>(3);
private final static AtomicBoolean INIT_STATUS = new AtomicBoolean(Boolean.FALSE);
private final static String PONG = "PONG";
public static ConnectStrategy getConnectStrategy(String connectType) {
if (!INIT_STATUS.getAndSet(Boolean.TRUE)) {
log.info("线程: {}, 初始化redis连接测试策略", Thread.currentThread().getName());
MasterSlaveConnect.getInstance();
ClusterConnect.getInstance();
SentinelConnect.getInstance();
SingleConnect.getInstance();
}
return Optional.ofNullable(CONNECT_CACHE.get(connectType)).orElseThrow(() -> new RuntimeException("该连接模式未适配或并未查询到该集群类型!"));
}
public interface ConnectStrategy {
/**
* 连接测试方法
*/
void connectTest(Map<String, String> parameters);
}
static abstract class AbstractConnectStrategy {
public void register(String name, ConnectStrategy strategy