SpringMVC集成redis cluster集群模式

SpringMVC集成redis cluster集群模式

背景:

最近分配到任务,将原有的redis的哨兵模式升级为集群模式,目前升级工作已正常上线,临近春节,手上的任务不多,所以做一个SpringMVC集成redis cluster集群模式的总结记录,首先先简单介绍一下redis集群的几种模式
1、单机版 不解释
2、Sentinel 哨兵模式
3、Redis Cluster Redis官方集群方案
4、Redis Sharding集群
下面我将详细介绍SpringMVC 如何集成Redis Cluster集群模式,后期如有需要会继续更新哨兵模式。

Redis Cluster 集群模式介绍:

1、cluster相对于哨兵模式是去中心化的,它的每个节点都存储了其它集群的信息,因此每个节点都可以做为集群的中心,容错能力强,具有更高的可用性和在线扩容能力。

2、简单的说,他就是将key通过hash算法,然后进行取模,将不同的key存储到对应的节点上,每个节点又可以有自己的从节点,也有一些局限性:

mget,mset 命令不支持(客户端可能提供了兼容的操作方法,但不建议使用)
pipeline 管道操作去掉,改成单次操作
watch,multi,exec 事务操作过程中,要保证只能对同一个key操作
ok 话不多说,让我们开始进入正题

1、在Maven的pom.xml文件中,增加redis的引用

         <!-- redis -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.8.0.RELEASE</version>
        </dependency>
         <!--jedis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>

注意:
首先spring-data-redis必须是1.7.0以上版本才行!以前用的版本低,启动时报错java.lang.ClassNotFoundException:org.springframework.data.redis.connection.RedisClusterConfiguration。
同时要配置redis.clients,spring-data-redis低版本中是自带的,不需要另外引入,否则报错java.lang.ClassNotFoundException:redis.clients.jedis.JedisPoolConfig
最后reids的版本一定要和jedis的版本相匹配,否则就会启动报错,如何查看自己的redis版本和jedis的版本是否匹配,可以在这个官方的maven仓库中进行查看,网址:maven仓库网址
查看步骤截图如下:
第一步
第二步
第三步

2. 配置redis.properties

#redis
#redis.host = 127.0.0.1
#redis.port = 6379
redis.pass = 集群的密码
redis.timeout = 7200 
redis.database = 9
redis.pool.maxActive = 1000
redis.pool.minIdle = 0
redis.pool.maxIdle = 200
redis.pool.maxWait = 10000
redis.pool.testWhileIdle = true
redis.pool.testOnBorrow = true
redis.pool.testOnReturn = true
redis.pool.minEvictableIdleTimeMillis = 60000
redis.pool.timeBetweenEvictableRunsMillis = 30000
redis.expire.seconds =18000
spring.redis.cluster.nodes=10.12.123.21:9600,10.12.123.21:9601,10.12.123.21:9602,10.12.123.21:9603,10.12.123.21:9604,10.12.123.21:9605

3. 配置spring-redis.xml

<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:p="http://www.springframework.org/schema/p"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
   
   	<!-- 配置Jedis连接池 -->
	<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
		<!-- <property name="maxActive" value="1024" /> -->
		<!-- <property name="maxWait" value="1000" /> -->
		<property name="maxTotal" value="1024" />
		<property name="maxIdle" value="200" />
		<property name="MaxWaitMillis" value="1000" />
		<property name="testOnBorrow" value="true" />
	</bean>

    <!-- redis集群配置 集群模式 -->
	<bean class="org.springframework.data.redis.connection.RedisClusterConfiguration" id="redisClusterConfig">
		<property name="maxRedirects" value="6"></property>
		<property name="clusterNodes">
			<set>
				<bean class="org.springframework.data.redis.connection.RedisNode">
					<constructor-arg name="host" value="10.12.123.21"></constructor-arg>
					<constructor-arg name="port" value="19001"></constructor-arg>
				</bean>
				<bean class="org.springframework.data.redis.connection.RedisNode">
					<constructor-arg name="host" value="10.12.123.21"></constructor-arg>
					<constructor-arg name="port" value="19002"></constructor-arg>
				</bean>
				<bean class="org.springframework.data.redis.connection.RedisNode">
					<constructor-arg name="host" value="10.12.123.21"></constructor-arg>
					<constructor-arg name="port" value="19003"></constructor-arg>
				</bean>
				<bean class="org.springframework.data.redis.connection.RedisNode">
					<constructor-arg name="host" value="10.12.123.21"></constructor-arg>
					<constructor-arg name="port" value="19004"></constructor-arg>
				</bean>
				<bean class="org.springframework.data.redis.connection.RedisNode">
					<constructor-arg name="host" value="10.12.123.21"></constructor-arg>
					<constructor-arg name="port" value="19005"></constructor-arg>
				</bean>
				<bean class="org.springframework.data.redis.connection.RedisNode">
					<constructor-arg name="host" value="10.12.123.21"></constructor-arg>
					<constructor-arg name="port" value="19006"></constructor-arg>
				</bean>
			</set>
		</property>
	</bean>
	<!-- jedis连接工厂 -->
	<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
		<property name="password" value="你的集群密码" />
		<property name="database" value="0" />
		<constructor-arg name="poolConfig" ref="jedisPoolConfig"/>
		<constructor-arg name="clusterConfig" ref="redisClusterConfig"/>
		<property name="timeout" value="5000" />
	</bean>
	
	<!-- spring jedis template -->
	<bean id="template" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory">
		<property name="keySerializer">
			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
		</property>
		<property name="valueSerializer">
			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
		</property>
	</bean>
	
	<!-- redis缓存管理 -->
 	<bean id="redisCacheManager" class="com.your.bangyan.common.cache.RedisCacheManager">
		<property name="template" ref="template"></property>
	</bean> 
</beans>  

把spring-redis.xml导入到主配置文件中

 <import resource="spring-redis.xml"/>

4. 调用Redis

public class RedisCacheManager extends WebApplicationObjectSupport implements InitializingBean{
	private volatile static RedisCacheManager redisManager;
	private JedisCluster jedisCluster;

	private static final Long RELEASE_SUCCESS = 1L;
	/**
	 * 静态方法,来返回一个实例
	 * 如果存在则返回
	 *
	 * @return
	 */
	@SuppressWarnings("resource")
	public static RedisCacheManager getInstance() {
		if (redisManager == null) {
			synchronized (RedisCacheManager.class) {
				if (redisManager == null) {
					ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-redis.xml");
					redisManager = (RedisCacheManager) context.getBean("redisCacheManager");
				}
			}
		}
		return redisManager;
	}


	/**
	 * 说明:redisTemplate获取锁
	 * @param lockId
	 * @param millisecond
	 * @return
	 *
	 */
	public boolean getLock(String lockId, long millisecond) {
		Long success = jedisCluster.setnx(lockId.getBytes(), String.valueOf(millisecond).getBytes());
		if (success==1) {
			return true;
		} else {
			byte[] value = jedisCluster.get(lockId.getBytes());
			if (value!=null && value.length > 0) {
				long expireTime = Long.parseLong(new String(value));
				if (expireTime < System.currentTimeMillis()) {
					// 如果锁已经过期
					byte[] oldValue = jedisCluster.getSet(lockId.getBytes(), String.valueOf(System.currentTimeMillis() + millisecond + 1).getBytes());
					// 防止死锁
					return Long.parseLong(new String(oldValue)) < System.currentTimeMillis();
				}
			}
		}
		return false;
	}

	public boolean setLock(String lockId, String  lockValue) {
		Long success = jedisCluster.setnx(lockId.getBytes(), lockValue.getBytes());
		if (success==1) {
			return true;
		}
		return false;
	}
	/**
	 * 说明:jedis获取锁
	 * @param key
	 * @param value
	 * @param lockExpireTimeOut
	 * @param lockWaitTimeOut
	 * @return
	 *
	 */
	public  boolean  lock(JedisCluster jedisCluster,String key, String value, Long lockExpireTimeOut,Long lockWaitTimeOut) throws Exception {

		Long deadTimeLine = System.currentTimeMillis() + lockWaitTimeOut;
		boolean flag = true;
		for (;;) {
			Long result = jedisCluster.setnx(key, value);
			if(flag){
				jedisCluster.expire(key,lockExpireTimeOut.intValue());
			}
			flag=false;
			if (result==1) {
				return true;
			}

			long lockWaitTimeOutR = deadTimeLine - System.currentTimeMillis();

			if (lockWaitTimeOutR <= 0L) {
				return false;
			}
		}
	};
	/**
	 * 说明:redisTemplate删除锁
	 * @param key
	 *
	 */
	public void delete(String key) {
		jedisCluster.del(key);
	}
	/**
	 * 说明:jedis释放锁
	 * @param key
	 * @param value
	 * @return
	 * @throws Exception
	 *
	 */
	public boolean unlock(String key, String value) throws Exception {

		String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
		logger.error("jedisCluster信息:"+jedisCluster);
		Object result = jedisCluster.evalsha(script, Collections.singletonList(key), Collections.singletonList(value));

		if (RELEASE_SUCCESS.equals(result)) {
			return true;
		}
		return false;

           /* String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

            Object result = jedis.eval(luaScript, Collections.singletonList(key),
                    Collections.singletonList(value));

            if ("1".equals(result)) {
                return true;
            }
        return false;*/
	}


	/**
	 * 获得jedis连接
	 * @return
	 * @throws IOException
	 */
	public JedisCluster getJedisCluster() throws IOException {
		try {
			jedisCluster = CIPRedisUtils.getJedisResource(JedisCluster.class);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return jedisCluster;
	}



	/*
	 * @param timeout 过期时间,当大于0时设置过期时间
	 */
	private void expire(String key, long timeout) {
		if (timeout > 0) {
			jedisCluster.pexpire(key,timeout);
		}
	}

	public void setString(String key, String value, long timeout) {
		jedisCluster.set(key,value);
		expire(key, timeout);
	}

	public void setString(String key, String value) {
		jedisCluster.set(key,value);
	}

	public String getString(String key) {
		return jedisCluster.get(key);
	}
	public void setList(String key, List<String> value, long timeout) {
		for(String str:value){
			jedisCluster.lpush(key, str);
		}
		expire(key, timeout);
	}

	public void setList(String key, List<String> value) {
		for(String str:value){
			jedisCluster.lpush(key, str);
		}
	}

	public List<String> getList(String key) {
		return jedisCluster.lrange(key, 0, -1);
	}



	/**
	 * TODO  未来使用hash存储
	 * @param key
	 * @param value
	 */
	public void setMap(String key, Map<String, Object> value) {
		//hashOperatins.putAll(key, value);
		String valueStr = JSON.toJSONString(value);
		jedisCluster.set(key,valueStr);
	}

	/**
	 * TODO  未来使用hash存储
	 * @param key
	 * @return
	 */
	public Map<String, Object> getMap(String key) {
		//return hashOperatins.entries(key);
		String valueStr = jedisCluster.get(key);
		Map<String, Object> map = (Map<String, Object>)JSON.parseObject(valueStr);
		return map;
	}



	/**
	 * 移除对应key的缓存
	 *
	 * @param key
	 */
	public void removeCache(String key) {
		if (jedisCluster.exists(key)) {
			jedisCluster.del(key);
		}
	}



	/**
	 * 获取当前缓存数量
	 * */
	public long getRedisDBCount() {
		return jedisCluster.dbSize();
	}

	/**
	 * 清空当前缓存
	 * */
	public void clearCurrentRedisDB() {
		jedisCluster.flushDB();
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		try {
			jedisCluster = CIPRedisUtils.getJedisResource(JedisCluster.class);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}

以上结束后就可以在想要使用的地方直接调用对应的方法啦
注意:Redis 3.0以上的才支持集群操作,而且集群之后,不能再分库了!集群设置密码的时候,也要保证所有的节点密码是一样的,不然会跳转失败而找不到数据!但是在程序中设置密码连接的时候却一直失败,所以最好还是使用内网地址作为连接IP,不设置密码了。以上就是整个流程了,虽然看似简单,但是对于一路踩着坑过来的我来说,也是不容易的,如果你有不同的看法欢迎留言沟通,对你有帮助的话加个关注点个赞吧~。

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

also&lucky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值