Redis集群

问题说明

  1. 分片作用
    a. 可以动态的实现内存扩容
    b. 可以将数据分散保存.
  2. 哨兵作用
    可以实现redis高可用(ha)

Redis集群实现

说明:采用redis集群配置,在集群内部实现通信和选举,同时实现分片的扩容和redis高可用性.

脑裂

说明:因为选举机制投票平票的结果导致出现多台主机.这种现象称之为脑裂.
解决:让节点个数增加(保证超半数即可)

集群搭建步骤

划分集群规模

规模:3主6从
端口:7000-7008共9台

搭建步骤

  1. 复制redis.conf文件到7000下
cp redis.conf cluster/7000
  1. 关闭aof使用rdb模式 593行
  2. 取消IP绑定
  3. 关闭保护模式
  4. 修改端口号
  5. 开启后台启动
  6. 修改PID位置 150行
  7. 修改持久化文件路径
  8. 修改内存策略
  9. 开启集群
  10. 开启集群配置
  11. 修改集群推选时间

复制7000文件夹

[root@localhostcluster]# cp -r 7000 7001
[root@localhostcluster]# cp -r 7000 7002
[root@localhostcluster]# cp -r 7000 7003
[root@localhostcluster]# cp -r 7000 7004
[root@localhostcluster]# cp -r 7000 7005
[root@localhostcluster]# cp -r 7000 7006
[root@localhostcluster]# cp -r 7000 7007
[root@localhostcluster]# cp -r 7000 7008

修改多个文件端口

分别修改7001-7008文件

Redis sentinel脑裂 redis 脑裂问题 解决_redis

[root@localhostcluster]# vim 7001/redis.conf
[root@localhostcluster]# vim 7002/redis.conf
[root@localhostcluster]# vim 7003/redis.conf
[root@localhostcluster]# vim 7004/redis.conf
[root@localhostcluster]# vim 7005/redis.conf
[root@localhostcluster]# vim 7006/redis.conf
[root@localhostcluster]# vim 7007/redis.conf
[root@localhostcluster]# vim 7008/redis.conf

编辑Redis启动脚本

说明:vim编辑start.sh文件.之后shstart.sh执行脚本

Redis sentinel脑裂 redis 脑裂问题 解决_Redis sentinel脑裂_02

通过ruby创建redis集群

说明:使用ruby管理redis集群.

Redis sentinel脑裂 redis 脑裂问题 解决_vim_03


Redis sentinel脑裂 redis 脑裂问题 解决_Redis sentinel脑裂_04


创建redis集群在redis根目录下运行

./src/redis-trib.rb create --replicas 2 192.168.126.166:7000 192.168.126.166:7001 192.168.126.166:7002 192.168.126.166:7003 192.168.126.166:7004 192.168.126.166:7005 192.168.126.166:7006 192.168.126.166:7007 192.168.126.166:7008

集群划分

Redis sentinel脑裂 redis 脑裂问题 解决_配置文件_05


输入yes

Redis sentinel脑裂 redis 脑裂问题 解决_Redis sentinel脑裂_06


Redis sentinel脑裂 redis 脑裂问题 解决_redis_07

集群测试

说明:将redis中的主机关闭后,检测高可用是否能够实现.并且当重新启动主机后,能否实现自动挂载.

Redis sentinel脑裂 redis 脑裂问题 解决_redis_08

集群宕机条件

Redis sentinel脑裂 redis 脑裂问题 解决_配置文件_09

Spring中工厂模式

工厂模式作用

核心思想:实例化对象
需求:

  1. 多例对象的创建
  2. 有些对象例如接口/抽象类不能直接实例化接口—代理抽象类----创建子类.
  3. 需要对对象进行二次封装.

静态工厂

说明:静态工厂中要求必须有静态方法.
调用方式:类名.静态方法
Spring配置方式:

<!--配置静态工厂  -->
	<bean id="calendarA" class="com.jt.manage.factory.StaticFactory" 
	factory-method="getInstance"/>

工厂模式编辑:

public class StaticFactory {
	public static Calendar getInstance(){		
		return Calendar.getInstance();
	}
}

实例化工厂

说明:实例化工厂要求必须先创建工厂对象,之后通过工厂方法调用获取对象
配置文件:

<!--实例化工厂  -->
	<bean id="instanceFactory" class="com.jt.manage.factory.InstanceFactory"/>
	<bean id="calendarB"
	factory-bean="instanceFactory"
	factory-method="getInstance"/>

工厂定义:

public class InstanceFactory {
	public Calendar getInstance(){	
		return Calendar.getInstance();
	}
}

Spring工厂

说明:该模式有spring内部调用,不需要做多余的配置但是需要实现特定的接口FactoryBean

工厂定义:

public class SpringFactory implements FactoryBean<Calendar>{

	@Override
	public CalendargetObject() throws Exception {
		returnCalendar.getInstance();
	}

	@Override
	public Class<?> getObjectType() {
		return Calendar.class;
	}

	@Override
	public boolean isSingleton() {	
		returnfalse;
	}
}

配置文件定义:

<!--配置spring工厂  -->
	<bean id="calendarC" class="com.jt.manage.factory.SpringFactory"/>

工具类测试:

@Test
public void testFactory(){
	//1.实例化Spring容器
	ApplicationContext context = 
			newClassPathXmlApplicationContext("spring/factory.xml");
	Calendar calendarA = (Calendar) context.getBean("calendarA");
	System.out.println(calendarA.getTime());
	
	Calendar calendarB = (Calendar) context.getBean("calendarB");
	System.out.println(calendarB.getTime());
	
	Calendar calendarC = (Calendar) context.getBean("calendarB");
	System.out.println(calendarC.getTime());
}

Spring整合集群

入门案例

@Test
	public void testCluster(){
		Set<HostAndPort>nodes = new HashSet<>();
		nodes.add(newHostAndPort("192.168.126.166",7000));
		nodes.add(newHostAndPort("192.168.126.166",7001));
		nodes.add(newHostAndPort("192.168.126.166",7002));
		nodes.add(newHostAndPort("192.168.126.166",7003));
		nodes.add(newHostAndPort("192.168.126.166",7004));
		nodes.add(newHostAndPort("192.168.126.166",7005));
		nodes.add(newHostAndPort("192.168.126.166",7006));
		nodes.add(newHostAndPort("192.168.126.166",7007));
		nodes.add(newHostAndPort("192.168.126.166",7008));
		JedisCluster jedisCluster = new JedisCluster(nodes);
		jedisCluster.set("aa", "小提莫哪里逃");
		System.out.println(jedisCluster.get("aa"));
	}

Spring整合集群

编辑properties配置文件

#最小空闲数
redis.minIdle=100
#最大空闲数
redis.maxIdle=300
#最大连接数
redis.maxTotal=1000
#客户端超时时间单位是毫秒
redis.timeout=5000
#最大建立连接等待时间
redis.maxWait=1000
#是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
redis.testOnBorrow=true

#redis cluster
redis.cluster0=192.168.126.166:7000
redis.cluster1=192.168.126.166:7001
redis.cluster2=192.168.126.166:7002
redis.cluster3=192.168.126.166:7003
redis.cluster4=192.168.126.166:7004
redis.cluster5=192.168.126.166:7005
redis.cluster6=192.168.126.166:7006
redis.cluster7=192.168.126.166:7007
redis.cluster8=192.168.126.166:7008

修改Spring配置文件

<!--jedis配置-->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!--最大空闲数-->
<property name="maxIdle" value="${redis.maxIdle}"/>
<!--最大建立连接等待时间-->
<property name="maxWaitMillis" value="${redis.maxWait}"/>
<!--是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个-->
<property name="testOnBorrow" value="${redis.testOnBorrow}"/>
<property name="maxTotal" value="${redis.maxTotal}"/>
<property name="minIdle" value="${redis.minIdle}"/>
</bean>

<!--通过工厂模式获取数据  -->
<bean id="jedisCluster" class="com.jt.common.factory.JedisClusterFactory">
	<!--引入配置文件源文件  -->
	<property name="propertySource">
		<value>classpath:/property/redis.properties</value>
	</property>
	<!--引入池配置文件  -->
	<property name="poolConfig" ref="poolConfig"/>
	<!--添加配置前缀-->
	<property name="redisNodePrefix" value="redis.cluster"/>
</bean>

编辑工厂模式

public class JedisClusterFactory implements FactoryBean<JedisCluster>{
	
	//引入pro配置文件
	private Resource propertySource;
	private JedisPoolConfig poolConfig;
	private String redisNodePrefix;
	
	//获取Set集合信息
	public Set<HostAndPort>getNodes(){
		Set<HostAndPort>nodes = new HashSet<>();
		
		//操作pro对象
		Properties pro = new Properties();
		try {
			pro.load(propertySource.getInputStream());
			
			//遍历获取nodes节点数据
			for (Object key :pro.keySet()) {
				//将key变为String类型目的判断字符串方便
				String strKey = (String) key;
				
				//判断哪个是redis节点数据
				if(strKey.startsWith(redisNodePrefix)){
					//IP:端口
					String value = pro.getProperty(strKey);
					String[] args = value.split(":");
					HostAndPort hostAndPort = 
					new HostAndPort(args[0],Integer.parseInt(args[1]));
					nodes.add(hostAndPort);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		returnnodes;
	}
	
	@Override
	public JedisCluster getObject() throws Exception {
		Set<HostAndPort> nodes = getNodes();
		return new JedisCluster(nodes, poolConfig);
	}

	@Override
	public Class<?> getObjectType() {
		return JedisCluster.class;
	}

	@Override
	public boolean isSingleton() {
		return false;
	}

	public Resource getPropertySource() {
		return propertySource;
	}

	public void setPropertySource(Resource propertySource) {
		this.propertySource = propertySource;
	}

	public JedisPoolConfig getPoolConfig() {
		returnpoolConfig;
	}

	public void setPoolConfig(JedisPoolConfigpoolConfig) {
		this.poolConfig = poolConfig;
	}

	public String getRedisNodePrefix() {
		return redisNodePrefix;
	}

	public void setRedisNodePrefix(String redisNodePrefix) {
		this.redisNodePrefix = redisNodePrefix;
	}	
}