|
|
|
@ -16,89 +16,126 @@ import redis.clients.jedis.JedisShardInfo;
|
|
|
|
|
import redis.clients.jedis.ShardedJedis;
|
|
|
|
|
import redis.clients.jedis.ShardedJedisPool;
|
|
|
|
|
|
|
|
|
|
// 标识该类是一个Spring组件,在Spring容器启动时会被扫描并实例化,其Bean名称为"redisConfigure",用于配置和管理Redis相关的连接等操作。
|
|
|
|
|
@Component("redisConfigure")
|
|
|
|
|
// 实现InitializingBean接口,意味着该类在所有属性被设置后,会执行afterPropertiesSet方法来进行一些初始化操作,通常用于加载配置等前置准备工作。
|
|
|
|
|
public class RedisXMLConfigure implements InitializingBean {
|
|
|
|
|
|
|
|
|
|
// 创建一个日志记录器实例,用于记录与该类相关的操作日志,方便在运行过程中排查问题以及查看关键信息,记录的日志类别基于当前类RedisXMLConfigure。
|
|
|
|
|
private static final Logger logger = Logger.getLogger(RedisXMLConfigure.class);
|
|
|
|
|
// 用于存储Redis键的前缀,是一个静态变量,在整个应用中可以统一为Redis中的键添加特定的前缀,方便管理和区分不同用途的键。
|
|
|
|
|
private static String preKey;
|
|
|
|
|
// 用于存储解析后的XML配置文档对象,后续通过操作这个对象来获取各种Redis配置相关的元素和属性信息,初始化为null,后续在初始化方法中进行赋值。
|
|
|
|
|
private static Document document = null;
|
|
|
|
|
// 定义一个ShardedJedisPool对象,用于管理多个Redis节点的连接池,通过它可以获取到ShardedJedis连接,实现对Redis集群等多节点环境下的数据访问。
|
|
|
|
|
private ShardedJedisPool shardedJedisPool;
|
|
|
|
|
|
|
|
|
|
// 实现InitializingBean接口的方法,在Spring容器完成属性注入后自动调用,用于进行Redis相关配置的初始化工作,比如加载XML配置文件、解析配置信息、创建连接池等操作。
|
|
|
|
|
@Override
|
|
|
|
|
public void afterPropertiesSet() throws Exception {
|
|
|
|
|
// 创建一个XMLConfiguration对象,用于读取和解析XML配置文件,它可能封装了一些底层的XML解析相关的逻辑。
|
|
|
|
|
XMLConfiguration xmlConfiguration = new XMLConfiguration();
|
|
|
|
|
// 定义要加载的Redis配置文件的路径和文件名,这里指定为"redis.xml",表示从类路径下查找该文件来获取Redis配置信息。
|
|
|
|
|
String REDIS_PATH = "redis.xml";
|
|
|
|
|
InputStream stream = null;
|
|
|
|
|
try {
|
|
|
|
|
// 通过类加载器获取指定路径下的配置文件输入流,如果获取不到(即配置文件不存在或路径错误),则记录错误日志并抛出运行时异常,表示加载配置文件失败。
|
|
|
|
|
stream = this.getClass().getClassLoader().getResourceAsStream(REDIS_PATH);
|
|
|
|
|
if (stream == null) {
|
|
|
|
|
logger.error("load redis.xml failed!!!" + REDIS_PATH);
|
|
|
|
|
throw new RuntimeException("load redis.xml failed");
|
|
|
|
|
}
|
|
|
|
|
logger.info("Redis XML config path:" + REDIS_PATH);
|
|
|
|
|
// 使用xmlConfiguration对象读取配置文件输入流中的内容,如果读取成功(返回true),则将解析后的文档对象赋值给document变量,以便后续使用;若读取失败则记录错误日志。
|
|
|
|
|
if (xmlConfiguration.readConfigFile(stream)) {
|
|
|
|
|
document = xmlConfiguration.getDocument();
|
|
|
|
|
} else {
|
|
|
|
|
logger.error("load redis.xml failed!!!");
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
if (null != stream)
|
|
|
|
|
// 无论是否成功读取配置文件,都要关闭输入流,释放相关资源,避免资源泄漏,通过判断输入流是否为null来进行关闭操作。
|
|
|
|
|
if (null!= stream)
|
|
|
|
|
stream.close();
|
|
|
|
|
}
|
|
|
|
|
//初始化参数
|
|
|
|
|
|
|
|
|
|
// 调用初始化方法,用于初始化Redis键的前缀信息,从解析后的XML配置中获取相应的值进行设置。
|
|
|
|
|
initPreKey();
|
|
|
|
|
// 调用方法初始化连接池的配置参数,从XML配置中读取相关属性值,创建并返回一个PoolConfigBean对象,该对象封装了如最大空闲连接数、最大等待时间等连接池配置信息。
|
|
|
|
|
PoolConfigBean pcb = initPoolConfigBean();
|
|
|
|
|
// 调用方法解析配置文件中关于Redis服务器节点的信息,读取每个服务器节点的IP、端口、是否需要认证以及认证密码等信息,返回一个包含多个RedisServerNodeBean对象的列表。
|
|
|
|
|
List<RedisServerNodeBean> rsnbs = initRedisServerNodeBeans();
|
|
|
|
|
//实现shardedJedisPool
|
|
|
|
|
|
|
|
|
|
// 创建一个JedisPoolConfig对象,用于配置Jedis连接池的相关参数,后续会基于这个配置对象来创建ShardedJedisPool连接池。
|
|
|
|
|
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
|
|
|
|
|
// 注意这里没有设置maxActive(可能在当前配置逻辑中有其他考虑,比如依赖默认值或者后续有特殊处理),而是设置了最大空闲连接数和最大等待时间,从之前初始化得到的PoolConfigBean对象中获取相应的参数值进行设置。
|
|
|
|
|
//no maxActive config
|
|
|
|
|
jedisPoolConfig.setMaxIdle(pcb.getMax_idle());
|
|
|
|
|
jedisPoolConfig.setMaxWaitMillis(pcb.getMax_wait());
|
|
|
|
|
shardedJedisPool = new ShardedJedisPool(jedisPoolConfig,getJedisShardInfo(rsnbs));
|
|
|
|
|
if(shardedJedisPool == null){
|
|
|
|
|
|
|
|
|
|
// 创建ShardedJedisPool连接池对象,传入配置好的JedisPoolConfig对象以及根据解析得到的Redis服务器节点信息转换而来的JedisShardInfo列表,用于管理多个Redis节点的连接池,方便后续获取连接进行数据操作。
|
|
|
|
|
shardedJedisPool = new ShardedJedisPool(jedisPoolConfig, getJedisShardInfo(rsnbs));
|
|
|
|
|
if (shardedJedisPool == null) {
|
|
|
|
|
// 如果创建连接池失败(返回值为null),则抛出运行时异常,表示Redis配置文件存在错误,导致无法正确创建连接池。
|
|
|
|
|
throw new RuntimeException("config redis.xml error");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 初始化jedis参数
|
|
|
|
|
* 该方法用于从解析后的XML配置文件中读取连接池相关的配置参数,创建并返回一个PoolConfigBean对象,其中封装了连接池的最大活动连接数、最大空闲连接数以及最大等待时间等参数信息。
|
|
|
|
|
*/
|
|
|
|
|
private PoolConfigBean initPoolConfigBean() {
|
|
|
|
|
PoolConfigBean poolConfigBean = new PoolConfigBean();
|
|
|
|
|
// 通过文档对象查找名为"pool"的XML元素,获取配置文件中关于连接池的配置节点,假设配置文件中以特定的XML结构来定义连接池相关属性。
|
|
|
|
|
Element poolElement = (Element) document.getElementsByTagName("pool").item(0);
|
|
|
|
|
int max_active = poolElement.hasAttribute("maxActive") ? Integer.parseInt(poolElement.getAttribute("maxActive")) : -1;
|
|
|
|
|
int max_idle = poolElement.hasAttribute("maxIdle") ? Integer.parseInt(poolElement.getAttribute("maxIdle")) : -1;
|
|
|
|
|
long max_wait = poolElement.hasAttribute("maxWait") ? Long.parseLong(poolElement.getAttribute("maxWait")) : -1;
|
|
|
|
|
// 判断"pool"元素是否有"maxActive"属性,如果有则将其解析为整数并赋值给max_active变量,否则将其设置为 -1(表示可能使用默认值或者后续有其他处理逻辑来确定该参数)。
|
|
|
|
|
int max_active = poolElement.hasAttribute("maxActive")? Integer.parseInt(poolElement.getAttribute("maxActive")) : -1;
|
|
|
|
|
// 类似地,获取"maxIdle"属性的值,如果存在则解析为整数赋值给max_idle变量,否则设置为 -1。
|
|
|
|
|
int max_idle = poolElement.hasAttribute("maxIdle")? Integer.parseInt(poolElement.getAttribute("maxIdle")) : -1;
|
|
|
|
|
// 获取"maxWait"属性的值,存在则解析为长整型赋值给max_wait变量,否则设置为 -1。
|
|
|
|
|
long max_wait = poolElement.hasAttribute("maxWait")? Long.parseLong(poolElement.getAttribute("maxWait")) : -1;
|
|
|
|
|
|
|
|
|
|
// 通过PoolConfigBean对象的setter方法,将获取到的各个参数值设置到对象中,以便后续返回并用于创建JedisPoolConfig对象时设置相应的连接池参数。
|
|
|
|
|
poolConfigBean.setMax_active(max_active);
|
|
|
|
|
poolConfigBean.setMax_idle(max_idle);
|
|
|
|
|
poolConfigBean.setMax_wait(max_wait);
|
|
|
|
|
|
|
|
|
|
return poolConfigBean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 解析配置redis的server列表
|
|
|
|
|
* 该方法用于从解析后的XML配置文件中读取关于Redis服务器节点的配置信息,解析每个服务器节点的IP、端口、是否需要认证以及认证密码等属性,创建并返回一个包含多个RedisServerNodeBean对象的列表,每个对象代表一个Redis服务器节点的配置信息。
|
|
|
|
|
*/
|
|
|
|
|
private List<RedisServerNodeBean> initRedisServerNodeBeans() {
|
|
|
|
|
List<RedisServerNodeBean> redisServers = new ArrayList<RedisServerNodeBean>();
|
|
|
|
|
// 通过文档对象查找所有名为"server"的XML元素,获取配置文件中定义的所有Redis服务器节点的配置节点列表,后续遍历该列表来解析每个服务器节点的详细信息。
|
|
|
|
|
NodeList serverElements = document.getElementsByTagName("server");
|
|
|
|
|
int serverLen = serverElements.getLength();
|
|
|
|
|
if (serverLen < 1) {
|
|
|
|
|
logger.error("redis.servers.server must have one !");
|
|
|
|
|
// 如果没有找到任何"server"元素,即没有配置Redis服务器节点信息,记录错误日志并返回null,表示配置不合法。
|
|
|
|
|
logger.error("redis.servers.server must have one!");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < serverLen; i++) {
|
|
|
|
|
// 遍历每个"server"元素,获取对应的Element对象,用于解析该服务器节点的具体属性信息。
|
|
|
|
|
Element serverElement = (Element) serverElements.item(i);
|
|
|
|
|
String temp_ip = serverElement.hasAttribute("ip") ? serverElement.getAttribute("ip") : null;
|
|
|
|
|
// 获取"server"元素中"ip"属性的值,如果存在则赋值给temp_ip变量,否则设置为null,表示IP地址未配置(这是不符合要求的情况,后续会进行相应的错误处理)。
|
|
|
|
|
String temp_ip = serverElement.hasAttribute("ip")? serverElement.getAttribute("ip") : null;
|
|
|
|
|
if (temp_ip == null) {
|
|
|
|
|
// 如果IP地址为空,记录错误日志并返回null,表示配置不合法,因为IP地址是连接Redis服务器必需的信息。
|
|
|
|
|
logger.error("redis.servers.server.ip must be supplied!");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String temp_port = serverElement.hasAttribute("port") ? serverElement.getAttribute("port") : "6379";
|
|
|
|
|
String temp_needAuth = serverElement.hasAttribute("needAuth") ? serverElement.getAttribute("needAuth") : "false";
|
|
|
|
|
// 获取"server"元素中"port"属性的值,如果存在则赋值给temp_port变量,否则默认设置为"6379"(Redis默认端口号),确保端口号有一个合理的值。
|
|
|
|
|
String temp_port = serverElement.hasAttribute("port")? serverElement.getAttribute("port") : "6379";
|
|
|
|
|
// 获取"server"元素中"needAuth"属性的值,如果存在则赋值给temp_needAuth变量,否则默认设置为"false",表示默认不需要进行身份验证。
|
|
|
|
|
String temp_needAuth = serverElement.hasAttribute("needAuth")? serverElement.getAttribute("needAuth") : "false";
|
|
|
|
|
String temp_auth = null;
|
|
|
|
|
// need auth
|
|
|
|
|
// 如果"needAuth"属性的值为"true",即表示需要进行身份验证,那么获取"auth"属性的值(认证密码),如果不存在则记录错误日志并返回null,表示配置不合法,因为需要认证时密码是必需的。
|
|
|
|
|
if ("true".equals(temp_needAuth)) {
|
|
|
|
|
temp_auth = serverElement.hasAttribute("auth") ? serverElement.getAttribute("auth") : null;
|
|
|
|
|
temp_auth = serverElement.hasAttribute("auth")? serverElement.getAttribute("auth") : null;
|
|
|
|
|
if (null == temp_auth) {
|
|
|
|
|
logger.error("since needAuth is true,auth must be supplied!");
|
|
|
|
|
return null;
|
|
|
|
@ -107,11 +144,14 @@ public class RedisXMLConfigure implements InitializingBean {
|
|
|
|
|
|
|
|
|
|
RedisServerNodeBean rs = null;
|
|
|
|
|
try {
|
|
|
|
|
// 尝试创建一个RedisServerNodeBean对象,传入解析得到的IP地址、端口号、是否需要认证的布尔值以及认证密码(如果需要认证)等信息,将创建的对象赋值给rs变量。
|
|
|
|
|
// 如果端口号解析为整数失败(即不是合法的数字格式),会捕获NumberFormatException异常,记录错误日志并返回null,表示配置不合法。
|
|
|
|
|
rs = new RedisServerNodeBean(temp_ip, Integer.parseInt(temp_port), Boolean.parseBoolean(temp_needAuth), temp_auth);
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
logger.error("port must be a number!\n" + e.getMessage());
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
// 将创建好的代表Redis服务器节点配置信息的对象添加到列表中,最终返回包含所有服务器节点配置信息的列表。
|
|
|
|
|
redisServers.add(rs);
|
|
|
|
|
}
|
|
|
|
|
return redisServers;
|
|
|
|
@ -119,55 +159,71 @@ public class RedisXMLConfigure implements InitializingBean {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 转换自定义配置为JedisShardInfo对象
|
|
|
|
|
* @param redisServers
|
|
|
|
|
* @return
|
|
|
|
|
* 该方法用于将之前解析得到的包含Redis服务器节点配置信息的列表(List<RedisServerNodeBean>)转换为适用于创建ShardedJedisPool连接池的JedisShardInfo列表,
|
|
|
|
|
* 每个JedisShardInfo对象包含了单个Redis服务器节点的连接信息,如IP、端口以及密码(如果需要认证)等,方便连接池管理和使用这些节点连接。
|
|
|
|
|
*
|
|
|
|
|
* @param redisServers 包含Redis服务器节点配置信息的列表,由initRedisServerNodeBeans方法解析得到。
|
|
|
|
|
* @return 返回一个包含JedisShardInfo对象的列表,用于创建ShardedJedisPool连接池时传入,以配置连接池与各个Redis服务器节点的连接信息。
|
|
|
|
|
*/
|
|
|
|
|
private List<JedisShardInfo> getJedisShardInfo(List<RedisServerNodeBean> redisServers) {
|
|
|
|
|
if(redisServers == null){
|
|
|
|
|
if (redisServers == null) {
|
|
|
|
|
// 如果传入的Redis服务器节点列表为null,记录错误日志并返回null,表示参数不合法,无法进行后续的转换操作。
|
|
|
|
|
logger.error("redisServers must not be empty null");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
int serverLen = redisServers.size();
|
|
|
|
|
if (serverLen < 1) {
|
|
|
|
|
// 如果列表中没有任何元素(即没有配置有效的Redis服务器节点),记录错误日志并返回null,表示不符合要求,因为连接池至少需要一个有效的节点信息来创建。
|
|
|
|
|
logger.error("redisServers must not be empty ");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
List<JedisShardInfo> servers = new ArrayList<JedisShardInfo>(serverLen);
|
|
|
|
|
for (int i = 0; i < serverLen; i++) {
|
|
|
|
|
// 遍历传入的Redis服务器节点列表,获取每个RedisServerNodeBean对象,代表一个服务器节点的配置信息。
|
|
|
|
|
RedisServerNodeBean redisServer = redisServers.get(i);
|
|
|
|
|
// 创建一个JedisShardInfo对象,传入该服务器节点的IP地址和端口号信息,用于构建与该节点的连接相关信息,初始状态下不包含密码信息(如果需要认证,后续再单独设置)。
|
|
|
|
|
JedisShardInfo jedisShardInfo = new JedisShardInfo(redisServer.getIp(), redisServer.getPort());
|
|
|
|
|
if (redisServer.isNeedAuth()) {
|
|
|
|
|
// 如果该服务器节点需要进行身份验证(通过RedisServerNodeBean对象的isNeedAuth方法判断),则设置对应的认证密码,从RedisServerNodeBean对象中获取密码信息并设置到JedisShardInfo对象中。
|
|
|
|
|
jedisShardInfo.setPassword(redisServer.getAuth());
|
|
|
|
|
}
|
|
|
|
|
// 将配置好的JedisShardInfo对象添加到列表中,最终返回包含所有服务器节点连接信息的JedisShardInfo列表,用于创建ShardedJedisPool连接池。
|
|
|
|
|
servers.add(jedisShardInfo);
|
|
|
|
|
}
|
|
|
|
|
return servers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 初始化redis的key前缀
|
|
|
|
|
* 该方法用于从解析后的XML配置文件中读取Redis键的前缀信息,将其赋值给静态变量preKey,方便后续在操作Redis键时统一添加前缀,进行规范管理。
|
|
|
|
|
*/
|
|
|
|
|
private void initPreKey() {
|
|
|
|
|
Element preKeyElement = (Element) document.getElementsByTagName("preKey").item(0);
|
|
|
|
|
preKey = preKeyElement.hasAttribute("value") ? preKeyElement.getAttribute("value") : "";
|
|
|
|
|
preKey = preKeyElement.hasAttribute("value")? preKeyElement.getAttribute("value") : "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取Redis键前缀的方法,外部代码可以通过调用该方法获取当前配置的Redis键前缀信息,用于在操作Redis时为键添加合适的前缀。
|
|
|
|
|
public String getPreKey() {
|
|
|
|
|
return preKey;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 从jedis连接池获得一个连接
|
|
|
|
|
* @return
|
|
|
|
|
* 该方法用于从之前创建并配置好的ShardedJedisPool连接池中获取一个ShardedJedis连接对象,以便后续通过该连接对象对Redis进行数据操作,如读写数据等。
|
|
|
|
|
*
|
|
|
|
|
* @return 返回一个ShardedJedis连接对象,用于与Redis进行交互操作,如果连接池配置或获取连接出现问题可能会抛出相应的异常。
|
|
|
|
|
*/
|
|
|
|
|
public ShardedJedis getConnection() {
|
|
|
|
|
return shardedJedisPool.getResource();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 把连接放回jedis连接池
|
|
|
|
|
* @param resource
|
|
|
|
|
* 该方法用于将使用完的ShardedJedis连接对象归还到ShardedJedisPool连接池中,释放连接资源,以便连接池可以对连接进行管理和复用,避免资源浪费和连接泄漏等问题。
|
|
|
|
|
*
|
|
|
|
|
* @param resource 要归还的ShardedJedis连接对象,通常是之前通过getConnection方法获取并使用完的连接。
|
|
|
|
|
*/
|
|
|
|
|
public void closeConnection(ShardedJedis resource) {
|
|
|
|
|
resource.close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|