springboot 启动时校验redis是否连接成功

1.业务场景

当springboot项目启动时,redis中间件可能还没有启动成功,这时候springboot项目会直接挂掉,业务需要等待redis启动成功之后再进行连接redis。

2.项目结构配置

pom文件使用redisson依赖,配置如下:

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.15.0</version>
</dependency>

redis连接配置:

package com.hero.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedisConfig {

    @Value("${env.redis.addr}")
    private String redisAddr;

    @Value("${env.redis.password}")
    private String password;


    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        String[] nodes = redisAddr.split(",");
        for (String node : nodes) {
            config.useClusterServers().addNodeAddress("redis://" + node);
        }
        config.useClusterServers().setPassword(password);
        return Redisson.create(config);
    }


}

3.增加项目启动时redis校验

校验逻辑:redis连接失败则一直重试直到连接成功为止,代码如下:

3.1. 获取 Spring 应用上下文的抽象类
package com.hero.validate;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;

/**
* 获取 Spring 应用上下文的抽象类
*/
public abstract class AbstractValidateItem implements ApplicationContextAware {

    private ConfigurableApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (applicationContext instanceof ConfigurableApplicationContext) {
            this.context = (ConfigurableApplicationContext) applicationContext;
        }

        validate();
    }

    public abstract void validate();
    
    public ConfigurableApplicationContext getContext() {
        return context;
    }


}

3.2 具体校验实现类
package com.hero.validate;

import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.ClusterNode;
import org.redisson.api.ClusterNodesGroup;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
@DependsOn(value = "springContextHolder")
public class ValidateRedis extends AbstractValidateItem {

    @Value("${env.redis.addr}")
    private String redisAddr;

    @Value("${env.redis.password}")
    private String password;

    @Value("${nms.validRedis:1}")
    private String isValid;

    @Override
    public void validate() {
        log.info(">>> ValidateRedis ");
        redisValid();
    }

    private void redisValid() {
        for (int i = 1; i <= 1000000; i++) {
            try {
                Thread.sleep(backoff(i));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                Boolean redisValid;
                log.info(">>> redisValid , isValid: {} ", isValid);
                if ("1".equals(isValid)) {
                    redisValid = getRedisConnection();
                } else {
                    log.info(">>> redisValid 忽略");
                    redisValid = true;
                }

                if (redisValid) {
                    log.info(" check connect redis ok");
                    return;
                } else {
                    log.warn("  retry connect redis addr {} fail", i);
                }
            } catch (Exception e) {
                log.error(">>> redisValid error : {}", e);
                if (i == 1) {
                    e.printStackTrace();
                }
                log.warn("  retry connect redis {} fail", i);
                if (i == 1000000) {
                    log.error(" nms will be restarted for retry connect redis {} fail ,error:{}", i, e);
                    getContext().close();
                }
            }
        }
    }

    public Boolean getRedisConnection() {

        Config config = new Config();
        String[] nodes = redisAddr.split(",");
        for (String node : nodes) {
            config.useClusterServers().addNodeAddress("redis://" + node);
        }
        config.useClusterServers().setPassword(password);

        RedissonClient redissonClient = Redisson.create(config);
        log.info(">>> redissonClient : {} " ,redissonClient);
        if (redissonClient != null) {
            log.info(">>> redissonClient is not null");
            List<Boolean> nodeStates = new ArrayList<>();
            ClusterNodesGroup clusterNodesGroup = redissonClient.getClusterNodesGroup();
            Collection<ClusterNode> connectNodes = clusterNodesGroup.getNodes();
            log.info(">>> connectNodes : {} ", connectNodes);
            for (ClusterNode node : connectNodes) {
                boolean ping = false;
                try {
                    ping = node.ping(5, TimeUnit.SECONDS);
                } catch (Exception e) {
                    log.error(">>> redisValid getRedisConnection ping error : {} ",e );
                }
                log.info(">>> connectNodes ,currentNode : {} , ping : {}", node, ping);
                nodeStates.add(ping);
            }
            log.info(">>> nodeStates : {} ", nodeStates);
            boolean redisPing = nodeStates.contains(true);
            log.info(">>> redisPing : {} ", redisPing);
            redissonClient.shutdown();
            return redisPing;
        }
        return false;
    }


    public static int backoff(int n) {
        if (n > 5) {
            n = 5;
        }
        return 2000 * n;
    }


}

这样,当srpingboot项目启动时会校验reids是否可以成功连接,如果失败则会一直重试直到连接成功。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值