Redis哨兵连接池初始化开发记录
最近在redis的测试,总是不如意,在单节点测试,可以通过,加入哨兵后,也可以通过,但是加入连接池后就一直报错,各种错误,很苦恼,去掉哨兵只加入连接池,也是不报错的,可以通过,但是需要的目的效果是加入哨兵和连接池并初始化连接池。经过这几天的调试查阅资料,总算4.7晚跑通。记录一下错误和自己心得吧。
原先代码
首先,写好redis加载哨兵的配置,以查询一个string的值为例。
最初我是这样写的(注意:这时候是没有连接池的概念和初始化连接池的)
//开启连接
@SuppressWarnings("deprecation")
public static JedisSentinelPool ConnectRedis() {
//加入哨兵
if(redisSentinelJedisPool==null) {
System.out.println("redisSentinelJedisPool为空,新建连接池");
Set<String> sentinels = new HashSet<String>();
String hostAndPort1 = "47.100.97.xxx:26379";
String hostAndPort2 = "47.100.2.xxx:26379";
String hostAndPort3 = "106.14.171.xxx:26379";
sentinels.add(hostAndPort1);
sentinels.add(hostAndPort2);
sentinels.add(hostAndPort3);
String clusterName = "mymaster";
String password = "ymylxxxx";
JedisSentinelPool redisSentinelJedisPool = new JedisSentinelPool("mymaster",sentinels,"ymylxxxx");
return redisSentinelJedisPool;
} else {
System.out.println("redisSentinelJedisPool不为空,直接返回");
return redisSentinelJedisPool;
}
}
写了一个连接的方法,在里面写了加载哨兵,密码之类的,并且做了判断,redisSentinelJedisPool为空时就加载。反之就不用加载。
之后在写业务时候直接先调用这个连接的方法。
public static String GetRedisString(String key) {
JedisSentinelPool redisSentinelJedisPool=ConnectRedis();
Jedis jedis = null;
String object = null;
try {
jedis = redisSentinelJedisPool.getResource();
//读取
object =jedis.get(key);
System.out.println(object);
} catch (Exception e) {
e.printStackTrace();
} finally {
//方法归还
//redisSentinelJedisPool.returnResource(jedis);
jedis.close();
}
return object;
}
这是一个查询redis缓存中一个string类型的一个值。
没问题,是可以跑通的,也正常拿到自己需要的key的值。但是测试时候发现,很容易崩溃。发现没我查询每一次都会去连接,很耗费时间资源,应该加入连接池的概念,只需要第一次启动时候连接并初始化连接池,之后就不用,直接去做具体业务就可以,这样就会大大减少时间。
修改后,加入连接池(可根据自己业务需求定义初始化数据):
/**
* 初始化Redis连接池.
*/
static {
System.out.println("初始化连接池");
JedisPoolConfig config = new JedisPoolConfig();
//设置最大连接总数
config.setMaxTotal(300);
//设置最大空闲数
config.setMaxIdle(50);
//设置最小空闲数
config.setMinIdle(8);
config.setMaxWaitMillis(10000);
//在获取连接的时候检查有效性, 默认false
config.setTestOnBorrow(true);
//在空闲时检查有效性, 默认false
config.setTestOnReturn(true);
//是否启用pool的jmx管理功能, 默认true
config.setJmxEnabled(true);
//Idle时进行连接扫描
config.setTestWhileIdle(true);
//是否启用后进先出, 默认true
config.setLifo(true);
//逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
config.setTimeBetweenEvictionRunsMillis(30000);
//每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
config.setNumTestsPerEvictionRun(10);
//表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义
config.setMinEvictableIdleTimeMillis(60000);
//连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true
config.setBlockWhenExhausted(true);
//对象空闲多久后逐出, 当空闲时间>该值 且 空闲连接>最大空闲数 时直接逐出,不再根据MinEvictableIdleTimeMillis判断 (默认逐出策略)
config.setSoftMinEvictableIdleTimeMillis(1800000);
System.out.println("初始化结束");
然后再加入哨兵机制
Set<String> set = new HashSet<>();
//连接地址以及端口号,有多个就一次增加
set.add("47.100.97.xxx:26379");
set.add("47.100.2.xxx:26379");
set.add("106.14.171.xxx:26379");
jedisSentinelPool = new JedisSentinelPool("mymaster", set, config,"ymylxxxx");
System.out.println("加载哨兵完成");
获取实例
/**
* 获取Jedis实例
* @return 返回Jedis实例
*/
public synchronized static Jedis getJedis() {
try {
if (jedisSentinelPool != null) {
System.out.println("连接池不为空");
return jedisSentinelPool.getResource();
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
释放资源
/**
* 释放资源.
* @param jedis jedis
*/
public static void releaseResource(final Jedis jedis) {
if (jedis != null){
jedis.close();
//jedisSentinelPool.destroy();
}
}
业务逻辑:取一个string的值
/*
* 查询String类型
*/
public static String GetRedisString(String key) {
Jedis jedis = null;
String object = null;
jedis = getJedis();
//读取
System.out.println("开始读取key");
object =jedis.get(key);
System.out.println(object);
//方法归还
releaseResource(jedis);
return object;
}
这种是直接在代码中加载连接池并初始化。另一种方法是也可以写在加载文件配置中直接交给spring去处理,下面记录一下在使用spring配置时候的错误和记录。
<!--配置Jedis连接池-->
<!-- 引入属性文件,在配置中占位使用 -->
1、首先加载你的属性文件(redis.properties),我是直接把值写进去没用属性文件,所以不用加载,注释掉,根据个人需要吧。
<!--<context:property-placeholder location="classpath:redis.properties" ignore-resource-not-found="true" /> -->
设置一系列参数,
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="5000" />
<property name="minIdle" value="5" />
<property name="maxWaitMillis" value="3000" />
<property name="maxIdle" value="20" />
<property name="testOnBorrow" value="true" />
<property name="testOnReturn" value="true" />
</bean>
<beanid="jedisSentinelPool" class="redis.clients.jedis.JedisSentinelPool">
<beanid="jedisSentinelPool" class="redis.clients.jedis.JedisSentinelPool">
<constructor-arg index="0" value="mymaster" />
<constructor-arg index="1">
设置你的哨兵端口,有几个写几个。
<set>
<value>106.14.171.xxx:6379</value>
<value>47.100.97.xxx:6379</value>
<value>47.100.2.xxx:6379</value>
</set>
</constructor-arg>
<constructor-arg index="2" ref="poolConfig" />
如果有密码就在这里加上你的密码
<constructor-arg index="3" value="ymylxxxx" />
</bean>
然后注入redis工具类
<bean class="com.manager.util.RedisUtil">
<property name="jedisSentinelPool" ref="jedisSentinelPool"/>
</bean>
我这样写了后,刚开始使用属性文件的,在我的目录下创建了redis.properties,里面设置各种参数,和我 <property name="maxTotal" value="${redis.maxTotal}" />对应,
利用
<context:property-placeholder location="classpath:redis.properties" ignore-resource-not-found="true" />直接加载进来,获取属性文件中的参数。但是这时候运行起来却报错了,发现和我的数据库读取属性文件发生冲突。如何解决这个冲突,之后再谈。
然后我尝试直接在配置中吧参数写进去,如上面的代码。
这样就不需要去加载属性文件读取参数,这样就避免了和数据库读取属性文件的冲突。
Ok准备就绪,运行起来,又报错了,一直提示我所有的哨兵down,但是查看后哨兵都没有down,很奇怪。上网查询资料,也一直没能解决。(为了不拖延项目进度,关于spring配置方法继续再查资料,一定写通)所以就尝试另一种方法写,没有用spring配置来写,选择用代码直接初始化连接池。代码实例就是一开始最初的代码,
错误注意:考虑之前错误提示所有哨兵down,仔细想了一下,可能是我既在配置中设置了连接池初始化和加载了哨兵,又在代码中设置了,起了冲突。(应该是这个,下班回家测试下。)
但是后来考虑到一个问题,因为我现在是把初始化连接池带参数写在redisutil类的代码中,这样的话,如果我没有用到这个业务,没有用到这个redisutil类的话。我的连接池就没有初始化,但是其实一细想,也没关系,不用redis 的话就不会用到连接池了,所以没什么关系,放到spring配置中,是应该算是项目一启动就开启了连接池并初始化了。
总算是在不懈努力下吧,算是跑通了业务,但是对于redis还是有很多东西不是很理解,需要多看看资料了解,缓存是一个很重要的东西,能和数据库一般的存在,当然不是那么简单啦,继续努力吧少年!