Spring data redis故障转移

假设生产环境中有3台redis服务器,3台redis服务器不是以cluster的方式部署,而是以单节点的方式部署在各自的服务器中,3台服务器无任何交互。现在要求第一台服务器发生网路闪断或者服务器宕机,转移到第二台服务器以此类推,如果其他服务器重新提供服务,需要能够重新加入redis服务器列表中。为了实现如上要求,现在将代码和配置文件贴在下面:


1、redis.properties配置文件,用于记录三台redis服务器的ip地址端口号,以及连接数等等,可自行添加

# redis host0
redis.host.0=192.126.4.143
redis.port.0=6379

# redis host1
redis.host.1=192.126.4.144
redis.port.1=6379

# redis host2
redis.host.2=192.126.4.145
redis.port.2=6379

redis.hosts.num=3

2、RedisConfiguration用于将上面配置文件中的key-value注入到配置类中
package com.happygo.redis.failover;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

/**
 * ClassName: RedisConfig <br/>
 * Description: RedisConfig <br/>
 * Date: 2017/9/26 14:37 <br/>
 *
 * @author (1378127237@qq.com)<br/>
 * @version 1.0 <br/>
 */
@Configuration
@PropertySource(value = "file:${app.config.path}/redis.properties")
public class RedisConfiguration {

    /**
     * The Host 0.
     */
    @Value("${redis.host.0}")
    private String host0;

    /**
     * The Host 1.
     */
    @Value("${redis.host.1}")
    private String host1;

    /**
     * The Host 2.
     */
    @Value("${redis.host.2}")
    private String host2;

    /**
     * The Port 0.
     */
    @Value("${redis.port.0}")
    private int port0;

    /**
     * The Port 1.
     */
    @Value("${redis.port.1}")
    private int port1;

    /**
     * The Port 2.
     */
    @Value("${redis.port.2}")
    private int port2;

    /**
     * The Num of redis hosts.
     */
    @Value("${redis.hosts.num}")
    private int numOfRedisHosts;

    /**
     * Getter for property 'numOfRedisHosts'.
     *
     * @return Value for property 'numOfRedisHosts'.
     */
    public int getNumOfRedisHosts() {
        return numOfRedisHosts;
    }

    /**
     * Setter for property 'numOfRedisHosts'.
     *
     * @param numOfRedisHosts Value to set for property 'numOfRedisHosts'.
     */
    public void setNumOfRedisHosts(int numOfRedisHosts) {
        this.numOfRedisHosts = numOfRedisHosts;
    }

    /**
     * Redis 0 connection factory jedis connection factory.
     *
     * @return the jedis connection factory
     */
    @Primary
    @Bean
    public JedisConnectionFactory redis0ConnectionFactory() {
        JedisConnectionFactory factory = new JedisConnectionFactory();
        factory.setHostName(host0);
        factory.setPort(port0);
        return factory;
    }

    /**
     * Redis 0 template redis template.
     *
     * @return the redis template
     */
    @Primary
    @Bean("redis0Template")
    public RedisTemplate<String, String> redis0Template() {
        return new StringRedisTemplate(redis0ConnectionFactory());
    }

    /**
     * Redis 1 connection factory jedis connection factory.
     *
     * @return the jedis connection factory
     */
    @Bean
    public JedisConnectionFactory redis1ConnectionFactory() {
        JedisConnectionFactory factory = new JedisConnectionFactory();
        factory.setHostName(host1);
        factory.setPort(port1);
        return factory;
    }

    /**
     * Redis 1 template redis template.
     *
     * @return the redis template
     */
    @Bean("redis1Template")
    public RedisTemplate<String, String> redis1Template() {
        return new StringRedisTemplate(redis1ConnectionFactory());
    }

    /**
     * Redis 2 connection factory jedis connection factory.
     *
     * @return the jedis connection factory
     */
    @Bean
    public JedisConnectionFactory redis2ConnectionFactory() {
        JedisConnectionFactory factory = new JedisConnectionFactory();
        factory.setHostName(host2);
        factory.setPort(port2);
        return factory;
    }

    /**
     * Redis 2 template redis template.
     *
     * @return the redis template
     */
    @Bean("redis2Template")
    public RedisTemplate<String, String> redis2Template() {
        return new StringRedisTemplate(redis2ConnectionFactory());
    }
}
3、RedisHeartbeatEvent类直接继承Spring的ApplicationEvent类,这是Spring中的应用事件类
package com.happygo.redis.failover;


import org.springframework.context.ApplicationEvent;

/**
 * ClassName: RedisHeartbeatEvent <br/>
 * Description: RedisHeartbeatEvent <br/>
 * Date: 2017/9/29 16:43 <br/>
 *
 * @author (1378127237@qq.com)<br/>
 * @version 1.0 <br/>
 */
public class RedisHeartbeatEvent extends ApplicationEvent {

    /**
     * Instantiates a new Redis heartbeat event.
     *
     * @param source the source
     */
    public RedisHeartbeatEvent(Object source) {
        super(source);
    }
}

4、RedisHeartbeatListener类用于监听redis心跳检测事件(RedisHeartbeatEvent),如果有事件发生,动态连接redis服务器,完成redis服务列表的更新(服务续约和剔除)
package com.happygo.redis.failover;

import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisConnectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentMap;

/**
 * ClassName: RedisHeartbeatListener <br/>
 * Description: RedisHeartbeatListener <br/>
 * Date: 2017/9/29 16:45 <br/>
 *
 * @author (1378127237@qq.com)<br >
 * @version 1.0 <br/>
 */
@Component
@AutoConfigureAfter(RedisTemplateHolder.class)
public class RedisHeartbeatListener {
    /**
     * The constant targetRedisTemplateMap.
     */
    private static final ConcurrentMap<String, RedisTemplate> targetRedisTemplateMap = RedisTemplateHolder
            .getTargetRedisTemplateMap();

    /**
     * On application event.
     *
     * @param redisHeartbeatEvent the redis heartbeat event
     */
    @EventListener
    public void onApplicationEvent(RedisHeartbeatEvent redisHeartbeatEvent) {
        doHeartbeatCheck();
    }

    /**
     * Do heartbeat check.
     */
    private void doHeartbeatCheck() {
        RedisTemplate redisTemplate = null;
        RedisConnectionFactory redisConnectionFactory = null;
        RedisConnection connection = null;
        boolean isRelease = true;
        for (Map.Entry<String, RedisTemplate> entry : targetRedisTemplateMap.entrySet()) {
            String lookupKey = entry.getKey();
            redisTemplate = entry.getValue();
            try {
                redisConnectionFactory = redisTemplate.getConnectionFactory();
                connection = RedisConnectionUtils
                        .getConnection(redisConnectionFactory, false);
                isRelease = true;
                if (lookupKey.contains("die")) {
                    changeRedisTemplateMap(lookupKey, redisTemplate, "die", "live");
                }
            } catch (Exception e) {
                changeRedisTemplateMap(lookupKey, redisTemplate, "live", "die");
                isRelease = false;
            } finally {
                if (isRelease) {
                    RedisConnectionUtils.releaseConnection(connection, redisConnectionFactory);
                }
            }
        }
    }

    /**
     * Change redis template map.
     *
     * @param lookupKey     the lookup key
     * @param redisTemplate the redis template
     * @param target        the target
     * @param replacement   the replacement
     */
    private void changeRedisTemplateMap(String lookupKey, RedisTemplate redisTemplate,
                                        String target, String replacement) {
        targetRedisTemplateMap.remove(lookupKey);
        String newLookupKey = lookupKey.replace(target, replacement);
        targetRedisTemplateMap.putIfAbsent(newLookupKey, redisTemplate);
    }
}
5、RedisHeartbeatScheduler类定时发送心跳检测事件
package com.happygo.redis.failover;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * ClassName: RedisHeartbeatScheduler <br/>
 * Description: RedisHeartbeatScheduler <br/>
 * Date: 2017/9/28 10:04 <br/>
 *
 * @version 1.0 <br/>
 */
@Component
public class RedisHeartbeatScheduler {
    /**
     * The constant LOGGER.
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisHeartbeatScheduler.class);

    /**
     * The Publisher.
     */
    @Autowired
    private ApplicationEventPublisher publisher;

    /**
     * Schedule.
     */
    @Scheduled(cron = "*/3 * * * * ?")
    public void schedule() {
        LOGGER.info(">>>>>>>执行redis心跳检测开始...");
        long startTime = System.currentTimeMillis();
        RedisHeartbeatEvent redisHeartbeatEvent = new RedisHeartbeatEvent("redis heartbeat");
        publisher.publishEvent(redisHeartbeatEvent);
        LOGGER.info(">>>>>>>执行redis心跳检测完毕, waste time: " + (System.currentTimeMillis() - startTime)
                + " millisecond");
    }
}
6、RedisTemplate动态获取持有类
package com.happygo.redis.failover;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * ClassName: RedisTemplateHolder <br/>
 * Description: RedisTemplateHolder <br/>
 * Date: 2017/9/26 17:15 <br/>
 *
 * @author (1378127237@qq.com)<br >
 * @version 1.0 <br/>
 */
@Component
public class RedisTemplateHolder implements ApplicationContextAware {

    /**
     * The constant targetRedisTemplateMap.
     */
    private static final ConcurrentMap<String, RedisTemplate> targetRedisTemplateMap = new ConcurrentHashMap<>();

    /**
     * The Redis conf.
     */
    @Autowired
    private RedisConfiguration redisConf;

    /**
     * Gets target redis template map.
     *
     * @return the target redis template map
     */
    public static ConcurrentMap<String, RedisTemplate> getTargetRedisTemplateMap() {
        return targetRedisTemplateMap;
    }

    /**
     * Sets application context.
     *
     * @param applicationContext the application context
     * @throws BeansException the beans exception
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        int numOfRedisHosts = redisConf.getNumOfRedisHosts();
        String defaultNameOfRedisTemplate = "redis Template";
        for (int i = 0; i < numOfRedisHosts; i++) {
            String beanNameOfRedisTemplate = defaultNameOfRedisTemplate.replace(" ", Integer.toString(i));
            RedisTemplate redisTemplate = (RedisTemplate) applicationContext.getBean(beanNameOfRedisTemplate);
            beanNameOfRedisTemplate = beanNameOfRedisTemplate + ":live";
            targetRedisTemplateMap.putIfAbsent(beanNameOfRedisTemplate, redisTemplate);
        }
        if (CollectionUtils.isEmpty(targetRedisTemplateMap)) {
            throw new IllegalArgumentException("Please check your 'redis.properties' is set redis configuration");
        }
    }

    /**
     * Determine target redis template redis template.
     *
     * @return the redis template
     */
    public RedisTemplate determineTargetRedisTemplate() {
        int numOfRedisHosts = redisConf.getNumOfRedisHosts();
        String defaultNameOfRedisTemplate = "redis Template:live";
        RedisTemplate redisTemplate = null;
        for (int i = 0; i < numOfRedisHosts; i++) {
            String beanNameOfRedisTemplate = defaultNameOfRedisTemplate.replace(" ", Integer.toString(i));
            redisTemplate = targetRedisTemplateMap.get(beanNameOfRedisTemplate);
            if (!ObjectUtils.isEmpty(redisTemplate)) {
                break;
            }
        }
        if (ObjectUtils.isEmpty(redisTemplate)) {
            throw new IllegalArgumentException("Cannot find any available redis hosts connect");
        }
        return redisTemplate;
    }



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值