Redis(七) HA集群(基于Sentinel)集成Spring、master写slave读

Redis( ) HA 集群(基于 Sentinel )集成 Spring master slave

说明:适合测试且了解sentinel模式,线上项目慎用。

 

一、配置情况说明:

    Master: 端口 6381

    Slave 端口 63796380

    Sentinel 端口 26379

 

二、集成spring-data-redis

   2.1配置文件:

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/context
         http://www.springframework.org/schema/context/spring-context.xsd">

	<context:component-scan base-package="com.tablemiao.redis.dao"></context:component-scan>
	<context:property-placeholder location="classpath:applicationContext.properties" />
	
	<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
		<property name="maxIdle" value="${redis.maxIdle}" />
		<property name="maxTotal" value="${redis.maxActive}" />
		<property name="maxWaitMillis" value="${redis.maxWait}" />
		<property name="testOnBorrow" value="${redis.testOnBorrow}" />		
	</bean>
		
  <bean id="redisSentinelConfiguration"
		class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
		<property name="master">
			 <bean class="org.springframework.data.redis.connection.RedisNode">
				<property name="name" value="mymaster" />
			</bean> 
		</property>
		<property name="sentinels">
			<set>
				 <bean class="org.springframework.data.redis.connection.RedisNode">
					<constructor-arg name="host" value="192.168.1.118"></constructor-arg>
					<constructor-arg name="port" value="26379"></constructor-arg>
				</bean> 
			</set>		
		</property>
	</bean>

	<bean id="jedisConnectionFactory"
		class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">	
		<property name="poolConfig" ref="poolConfig"/> 
		 <constructor-arg ref="redisSentinelConfiguration"></constructor-arg> 
	 	<property name="usePool" value="false"/>
	    <property name="hostName" value="${redis.hostName}" />
		<property name="port" value="${redis.port}" /> 
	</bean>

	<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
		<property name="connectionFactory" ref="jedisConnectionFactory" />
	</bean>


</beans>

 

网上看了很多配法,解释的都不完整,有些根本不能运行,共同学习。

poolConfig 连接池配置  可以忽略,因为<property name="usePool" value="false"/>

配了这句,如果启用,读取配置就会出错,始终没有解决,可以直接删除掉。

redisSentinelConfiguration 哨兵的配置,hostportsentinel的监听端口和host(有人说是redis服务进程的端口和host,不知道怎么玩的,说的不清不楚,一直出错,查看过这个类,也不该这么配),本人是一个sentinel.conf文件中监听三个redis进程(就是Redis()中的配法),所以只有一个,多个可以在set中添加。

jedisConnectionFactory 配置连接工厂、默认端口为master的端口,以方便后续测试。

 

2.2加载上诉配置文件 直接拿到注入的redisTemplate  进行数据操作,读写无异常。

 

2.3断开master进程、主从切换,后台切换完成,重新加入开始master,此时master变为slave详见Redis()

2.4 再次通过redisTemplate 进行样例操作,发现,此时读取可以,但是写数据不行。原因为,代码中的连接地址依旧为开始master连接的地址和端口。

 

2.5 问人找文档无果。仔细查看redis的连接模式,想到如下法子,测试可行,性能没测试过。             

 

  2.5.1通过redisTemplate.getConnectionFactory().getSentinelConnection().masters();可以获取当前的master连接地址和端口。

  2.5.2获取配置文件中当前连接地址和端口比对,如果一致,说明当前连接为master,可以进行读写,直接利用注入的redisTemplate进行操作即可,如果不一致,利用当前master的地址和端口在代码里重新建立连接,进行写操作。


2.6比对的逻辑代码(整体代码详见cc-redis-three

 

//获取当前的master
  	public String getMaster() {
		Iterator<RedisServer> masters = redisTemplate.getConnectionFactory()
				.getSentinelConnection().masters().iterator();
		while(masters.hasNext()){
			return  masters.next().asString();
		}
		return "get_master_error";	
	}
 
//连接的逻辑比对。 如果一致,就返回redisTemplate,否则返回jedis连接
public Object getMasterRedisTemplate(){
		String proptiesHostAndPort = hostName+":"+port;
		String mHost = getMaster().split(":")[0];
		int mPort = Integer.parseInt(getMaster().split(":")[1]);
		//如果当前连接为master,直接返回,用作写操作
		if(proptiesHostAndPort.equals(getMaster())){
			return redisTemplate;
		}else{
			//获取master 的连接方式  再次建立连接,进行写操作
			
			/*
			    本想通过这种方式,返回一个RedisTemplate  结果发现获取不到连接,希望大神弄好了,一起学习
			JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
			jedisConnectionFactory.setHostName(hostName);
			jedisConnectionFactory.setPort(port);
			RedisTemplate<String, Object> re= new RedisTemplate<String, Object>();
			re.setConnectionFactory(jedisConnectionFactory);
			//这种也不行
			JedisConnection jedis = jedisConnectionFactory.getConnection();
			*/
			
			//直接jedis 操作
			JedisPoolConfig jpl = new JedisPoolConfig();
			jpl.setMaxTotal(50000);
			jpl.setMaxIdle(5);
			jpl.setMaxWaitMillis(1000*60*5);
			jpl.setTestOnBorrow(true);
			JedisPool jp = new JedisPool(jpl,mHost,mPort);

			return jp.getResource();
		}
	}

 

2.7 获取到2.6中的连接,分别利用其进行操作,读,写代码

  写,如果返回是的RedisTemplate,说明是当前是master的连接,直接写数据即可,否则

创建一个masterjedis连接 进行写数据

 

public boolean insertString(String key, String value) {
		try {
			//先拿连接判断,是否当前连接为master,底下insert相同处理即可
			if(getMasterRedisTemplate() instanceof RedisTemplate){
				redisTemplate.opsForValue().set(key, value);
			}else{
				Jedis jedis = (Jedis)getMasterRedisTemplate();
				jedis.set(key, value);
			}
			return true;
		} catch (Exception e) {
			logger.info("新增错误:{}", e.getMessage());
			return false;
		}

	}
读,一样的判断即可
public Object select(String key){
		try {
			if(getMasterRedisTemplate() instanceof RedisTemplate){
				 DataType type = redisTemplate.type(key);
				 if(DataType.NONE == type){
					 logger.info("key不存在");
					 return null;
				 }else if(DataType.STRING == type){
					 return super.redisTemplate.opsForValue().get(key);
				 }else if(DataType.LIST == type){
					 return super.redisTemplate.opsForList().range(key, 0, -1);
				 }else if(DataType.HASH == type){
					 return super.redisTemplate.opsForHash().entries(key);
				 }else
					 return null;
			}else{
				Jedis jedis = (Jedis)getMasterRedisTemplate();
				if("string".equals(jedis.type(key))){
					return jedis.get(key);
				}else{
					 return null;
				}	
			}
		} catch (Exception e) {
			logger.info("查询错误:{}", e.getMessage());
			return null;
		}
	}

若是想要达到master slave读,在读的时候判断即可,如果是当前的master连接,那创建一个slavejedis进行读取。

 

三、测试

 

@org.junit.Test
	public void test() throws Exception{
		
		logger.info("=========start===========");
		logger.info("当前master列表:{}",customDao.getMaster());
		//logger.info("当前连接的hostName:{}",customDao.isMaster());
		customDao.insertString("sentinel:key:1", "sentinel_value_集群值测试1");
		logger.info("读取集群配置的值:{}",customDao.select("sentinel:key:1"));
		logger.info("=========end===========");
	}

结果如下,当前连接是的slave,也能读写

 

 

 

 

 

 

 

你可能感兴趣的:(redis,redis主从复制,redis主从切换,sentinel集群)