解决引入Redisson可能会出现项目启动失败的问题

 更新时间:2024年06月12日 10:06:37   作者:疯狂打码中~  
这篇文章主要介绍了解决引入Redisson可能会出现项目启动失败的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

问题1

需要注意Redisson版本和spring-boot版本一致,我使用的是spring-boot 2.1.3 对应的Redisson 3.9.1不然会报错

java.lang.NoClassDefFoundError: org/springframework/data/redis/connection/RedisStreamCommands

    at org.redisson.spring.data.connection.RedissonConnectionFactory.getConnection(RedissonConnectionFactory.java:111)
    at org.springframework.data.redis.core.RedisConnectionUtils.doGetConnection(RedisConnectionUtils.java:132)
    at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:95)
    at org.springframework.data.redis.core.RedisConnectionUtils.getConnection(RedisConnectionUtils.java:82)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:211)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184)
    at org.springframework.data.redis.core.RedisTemplate.hasKey(RedisTemplate.java:769)

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

问题2

Redisson自己会启动一个Redisson连接池,尝试连接redis,项目启动的时候就会连接,这时候如果k8s初始化的pod节点网络不通可能会出现问题,因为redis连接不上(说是我们的是海外服务器的原因,网络不稳定),项目报错,导致起不起来,然后pod会一直重启,因为运维层次不了解他们设置的k8s原理,需要在短时间解决,提供了下面的方案解决,后来也没有再出现这个问题,启动报错

1)自己重新空实现了一个RedissonClient

/**
 * @ClassName RedissonClientTemporary
 * @Decription 只是在初始化时候使用一下,之后就会被替换
 */
public class RedissonClientTemporary implements RedissonClient{

}

2)然后将这个空实现注入到spring容器

为了给其它使用到RedissonClient,作为属性初始化时候不报错

@Configuration
public class RedissonConfig {

    /**
     * 配置一个临时的对象到spring容器中,不使用
     * @return 一个RedissonClient的实现
     */
    @Bean
    public RedissonClient redissonClient() {
        RedissonClient redissonClient = new RedissonClientTemporary();
        return redissonClient;
    }

}

​​​​​3)项目启动完成使用一个监听事件

放入Redisson替换RedissonClient的实现,然后初始化一下,这里可能还是会连接报错但是不影响,因为已经放入了spring容器,如果这里连接失败,他也就不会再次尝试连接了,直到你再次使用它时候才会再次调用创建连接(那会redis就已经可用,其实redis还不可用,我们使用降级策略也可以,那会使用本地缓存或者数据库等)

/**
 * @ClassName ApplicationLoadRedissonListener
 * @Decription 项目启动完成,增加一个监听器,替换spring容器里面的redissonClient的对象,进行切换成redisson
 */
@Component
public class ApplicationLoadRedissonListener implements ApplicationListener<ApplicationReadyEvent> {

    private static final Logger log = LoggerFactory.getLogger(ApplicationLoadRedissonListener.class);

    @Autowired
    ConfigurableApplicationContext configurableApplicationContext;
    @Autowired
    private RedisProperties redisProperties;
    @Value("${spring.redis.redisson.singleServerConfig.subscriptionsPerConnection}")
    private Integer subscriptionsPerConnection;
    @Value("${spring.redis.redisson.singleServerConfig.connectionPoolSize}")
    private Integer connectionPoolSize;
    @Value("${spring.redis.redisson.singleServerConfig.connectionMinimumIdleSize}")
    private Integer connectionMinimumIdleSize;
    @Value("${spring.redis.redisson.singleServerConfig.subscriptionConnectionPoolSize}")
    private Integer subscriptionConnectionPoolSize;
    @Value("${spring.redis.redisson.singleServerConfig.subscriptionConnectionMinimumIdleSize}")
    private Integer subscriptionConnectionMinimumIdleSize;


    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        try{
            //加载一些基本的redis基础配置
            Config config = new Config();
            String address = "redis://" + redisProperties.getHost() + ":" + redisProperties.getPort();
            SingleServerConfig serverConfig = config.useSingleServer();
            serverConfig.setAddress(address);
            serverConfig.setDatabase(redisProperties.getDatabase());
            if (!StringUtils.isEmpty(redisProperties.getPassword())) {
                serverConfig.setPassword(redisProperties.getPassword());
            }
            serverConfig.setTimeout((int)redisProperties.getTimeout().toMillis());

            //加载redisson一些特殊配置
            serverConfig.setConnectionPoolSize(connectionPoolSize);
            serverConfig.setConnectionMinimumIdleSize(connectionMinimumIdleSize);
            serverConfig.setSubscriptionConnectionMinimumIdleSize(subscriptionConnectionMinimumIdleSize);
            serverConfig.setSubscriptionConnectionPoolSize(subscriptionConnectionPoolSize);
            serverConfig.setSubscriptionsPerConnection(subscriptionsPerConnection);

            log.info("加载 redisson配置信息 {}", JsonUtil.of(serverConfig));
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(Redisson.class);
            beanDefinitionBuilder.addConstructorArgValue(config);

            String redissonClientName = RedissonClient.class.getSimpleName().substring(0,1).toLowerCase() + RedissonClient.class.getSimpleName().substring(1);
            Object redissonClient = configurableApplicationContext.getBean(redissonClientName);
            log.info("初次放入的redissonClient实现对象:{}", redissonClient.getClass().getName());;

            //创建一个Redisson对象
            BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) configurableApplicationContext;
            beanDefinitionRegistry.registerBeanDefinition(redissonClientName, beanDefinitionBuilder.getBeanDefinition());

            //这里相当于初始化加载使用
            redissonClient = configurableApplicationContext.getBean(redissonClientName);
            log.info("最终放入的redissonClient实现对象:{}", redissonClient.getClass().getName());
        }catch (Exception e){
            log.info("ApplicationLoadRedissonListener/onApplicationEvent/RedissonClient/Exception:[{}]", e.getMessage());
        }
    }

}
# redisson 连接配置
# 单个连接最大订阅数量
spring.redis.redisson.singleServerConfig.subscriptionsPerConnection=5
# 连接池大小
spring.redis.redisson.singleServerConfig.connectionPoolSize=8
# 最小空闲连接数
spring.redis.redisson.singleServerConfig.connectionMinimumIdleSize=4
# 发布和订阅连接池大小
spring.redis.redisson.singleServerConfig.subscriptionConnectionPoolSize=8
# 发布和订阅连接的最小空闲连接数
spring.redis.redisson.singleServerConfig.subscriptionConnectionMinimumIdleSize=1

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringMVC基于配置的异常处理器

    SpringMVC基于配置的异常处理器

    这篇文章主要为大家介绍了SpringMVC基于配置的异常处理器,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • IntelliJ IDEA JRebel 安装使用图文教程(热部署插件)

    IntelliJ IDEA JRebel 安装使用图文教程(热部署插件)

    IDEA 全称 IntelliJ IDEA,是java语言开发的集成环境,IntelliJ在业界被公认为最好的java开发工具之一。这篇文章主要介绍了IntelliJ IDEA 热部署插件JRebel 安装使用图文教程,需要的朋友可以参考下
    2018-03-03
  • SpringCloud Feign隔离与降级详细分析

    SpringCloud Feign隔离与降级详细分析

    Feign是Netflix公司开发的一个声明式的REST调用客户端; Ribbon负载均衡、 Hystrⅸ服务熔断是我们Spring Cloud中进行微服务开发非常基础的组件,在使用的过程中我们也发现它们一般都是同时出现的,而且配置也都非常相似
    2022-11-11
  • Java针对ArrayList自定义排序的2种实现方法

    Java针对ArrayList自定义排序的2种实现方法

    这篇文章主要介绍了Java针对ArrayList自定义排序的2种实现方法,结合实例形式总结分析了Java操作ArrayList自定义排序的原理与相关实现技巧,需要的朋友可以参考下
    2018-01-01
  • MyBatis 延迟加载深度解密:从使用方式到底层动态代理原理解析

    MyBatis 延迟加载深度解密:从使用方式到底层动态代理原理解析

    本文详细解析MyBatis延迟加载机制,涵盖定义、配置方式、底层原理、执行流程及常见坑点,助你掌握延迟加载优化数据库查询与内存使用技巧,应对面试与实际项目挑战,感兴趣的朋友一起看看吧
    2026-06-06
  • Spring中的@Configuration详解

    Spring中的@Configuration详解

    这篇文章主要介绍了Spring中的@Configuration详解,Spring 提供了丰富的特性和功能,包括依赖注入、面向切面编程、事务管理、数据访问、Web应用程序开发等,需要的朋友可以参考下
    2023-08-08
  • Java OCR tesseract 图像智能文字字符识别技术实例代码

    Java OCR tesseract 图像智能文字字符识别技术实例代码

    这篇文章主要介绍了Java OCR tesseract 图像智能文字字符识别技术实例代码,非常具有实用价值,需要的朋友可以参考下
    2017-06-06
  • 解决IDEA Gradle构建报错''Cause: zip END header not found''

    解决IDEA Gradle构建报错''Cause: zip END header not found''

    这篇文章主要介绍了解决IDEA Gradle构建报错"Cause: zip END header not found"的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • Java使用dom4j实现对xml简单的增删改查操作示例

    Java使用dom4j实现对xml简单的增删改查操作示例

    这篇文章主要介绍了Java使用dom4j实现对xml简单的增删改查操作,结合实例形式详细分析了Java使用dom4j实现对xml简单的增删改查基本操作技巧与相关注意事项,需要的朋友可以参考下
    2020-05-05
  • shiro并发人数登录控制的实现代码

    shiro并发人数登录控制的实现代码

    在做项目中遇到这样的需求要求每个账户同时只能有一个人登录或几个人同时登录,如果是同时登录的多人,要么不让后者登录,要么踢出前者登录,怎么实现这样的功能呢?下面小编给大家带来了shiro并发人数登录控制的实现代码,一起看看吧
    2017-09-09

最新评论