aka*_*ash 6 java spring spring-data-redis spring-boot
有没有办法防止spring-boot应用程序因外部连接失败而启动失败?我发现其他类似的问题建议使用@Lazy注释来防止bean 初始化,但此解决方案对我使用客户端@Configuration不起作用。Spring Data RedisJedis
此外,像这样的其他解决方案特定于应用程序中使用的依赖项。例如,Spring Cloud具有以下属性来控制快速失败行为 -
spring.cloud.config.fail-fast=true
Run Code Online (Sandbox Code Playgroud)
您可以使用我为我的问题创建的这个项目,通过关闭redis服务器来重现。
下面是我的代码的样子 -
@Lazy
@Configuration
public class RedisConfiguration {
@Value("${spring.redis.sentinel.master}")
private String SENTINEL_MASTER;
@Value("${spring.redis.sentinel.nodes}")
private String SENTINEL_NODES;
@Value("${spring.redis.security.enabled:false}")
private boolean REDIS_SECURITY_ENABLED;
@Value("${spring.redis.security.password:}")
private String REDIS_PASSWORD;
@Lazy
@Bean // somehow this always gets initialized
public RedisConnectionFactory jedisConnectionFactory() {
// create set of sentinel nodes
System.out.println(SENTINEL_NODES);
Set<String> sentinelNodesSet = new HashSet<>(5);
StringTokenizer st = new StringTokenizer(SENTINEL_NODES, ",");
while (st.hasMoreTokens())
sentinelNodesSet.add(st.nextToken());
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration(SENTINEL_MASTER, sentinelNodesSet);
if (REDIS_SECURITY_ENABLED) {
sentinelConfig.setPassword(REDIS_PASSWORD);
}
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(sentinelConfig);
return jedisConnectionFactory;
}
Run Code Online (Sandbox Code Playgroud)
下面是异常跟踪 -
org.springframework.beans.factory.UnsatisfiedDependencyException:创建类路径资源[org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.class]中定义的名为“stringRedisTemplate”的bean时出错:通过方法“stringRedisTemplate”参数表达的依赖关系不满足0; 嵌套异常是org.springframework.beans.factory.BeanCreationException:创建类路径资源[com/springboot/redisintegration/RedisConfiguration.class]中定义的名为“jedisConnectionFactory”的bean时出错:调用init方法失败;嵌套异常是 redis.clients.jedis.exceptions.JedisConnectionException:所有哨兵都已关闭,无法确定 mysentinel master 正在哪里运行...在 org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:797) ~ [spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] 在 org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:538) ~[spring-beans-5.2.8. RELEASE.jar:5.2.8.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1336) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE ] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1176) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] 在 org.springframework.beans.factory .support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:556) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory. java:516) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) ~[ spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] 在 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226) ~[spring-beans-5.2.8.RELEASE .jar:5.2.8.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:322) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE]在 org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] 在 org.springframework.beans.factory。 support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:897) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] 在 org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879) )~[spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE] 在 org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java: [551] 〜[spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE]在org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143)〜[spring-boot -2.3.3.RELEASE.jar:2.3.3.RELEASE] 在 org.springframework.boot.SpringApplication.refresh(SpringApplication.java:758) ~[spring-boot-2.3.3.RELEASE.jar:2.3.3。发布] 在 org.springframework.boot.SpringApplication.refresh(SpringApplication.java:750) ~[spring-boot-2.3.3.RELEASE.jar:2.3.3.RELEASE] 在 org.springframework.boot.SpringApplication.refreshContext( SpringApplication.java:397) ~[spring-boot-2.3.3.RELEASE.jar:2.3.3.RELEASE] 在 org.springframework.boot.SpringApplication.run(SpringApplication.java:315) ~[spring-boot-2.3 .3.RELEASE.jar:2.3.3.RELEASE] 在 org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.3.3.RELEASE.jar:2.3.3.RELEASE]在 org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.3.3.RELEASE.jar:2.3.3.RELEASE] 在 com.springboot.redisintegration.RedisIntegrationApplication.main(RedisIntegrationApplication. java:21) ~[classes/:na] 引起:org.springframework.beans.factory.BeanCreationException:创建在类路径资源 [com/springboot/redisintegration/RedisConfiguration.class] 中定义的名为“jedisConnectionFactory”的 bean 时出错:调用init方法失败;嵌套异常是 redis.clients.jedis.exceptions.JedisConnectionException:所有哨兵都已关闭,无法确定 mysentinel master 正在哪里运行...在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1794) ~ [spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594) ~[spring-beans-5.2.8。 RELEASE.jar:5.2.8.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:516) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE ] 在 org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:324) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] 在 org.springframework。 beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] 位于 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean (AbstractBeanFactory.java:322) ~[spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202) ~[ spring-beans-5.2.8.RELEASE.jar:5.2.8.RELEASE] 在 org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276) ~[spring-beans-5.2.8.RELEASE .jar:5.2.8.RELEASE] 位于 org.springframework.beans.factory.support。
简而言之:
@Lazy注释适用于RedisStandaloneConfiguration但不适用于RedisSentinelConfiguration,不知道为什么?@Lazy注释是有风险的,因为您需要确保所有正在使用的服务Redis也都是延迟加载的。spring.cloud.config.fail-fast=true为 Spring Cloud 提供的解决方案。更新:
我为此功能创建了以下 Jira 问题 -
Spring Cloud Config 在底层使用spring-retryAOP ( spring-boot-starter-aop) 来配置重试机制。
此过程在ConfigServiceBootstrapConfiguration中实现。
代码的相关部分是这样的:
/* @ConditionalOnProperty("spring.cloud.config.fail-fast") */
@ConditionalOnClass({ Retryable.class, Aspect.class, AopAutoConfiguration.class })
@Configuration(proxyBeanMethods = false)
@EnableRetry(proxyTargetClass = true)
@Import(AopAutoConfiguration.class)
@EnableConfigurationProperties(RetryProperties.class)
protected static class RetryConfiguration {
@Bean
@ConditionalOnMissingBean(name = "configServerRetryInterceptor")
public RetryOperationsInterceptor configServerRetryInterceptor(
RetryProperties properties) {
return RetryInterceptorBuilder.stateless()
.backOffOptions(properties.getInitialInterval(),
properties.getMultiplier(), properties.getMaxInterval())
.maxAttempts(properties.getMaxAttempts()).build();
}
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,基本思想是提供一个RetryConfiguration在考虑应用程序失败之前处理一定次数的重试的方法。
Spring Cloud Client 的文档提供了有关用于配置此机制的不同属性的更多信息。您还可以在类的源代码中看到默认值RetryProperties。
请尝试包含两个必需的依赖项spring-retry和spring-boot-starter-aop,以及RetryConfiguration作为主配置的子项,为重试机制的配置属性提供一些合理的默认值,然后看看会发生什么。
您可以将解决方案推向极限,并尝试在大量情况下重新连接,也许可以增加它们之间的节奏,等待服务器可用。
我认为@Lazy注释将不再需要并且可以安全地删除。
编辑
查看错误堆栈跟踪,您认为您还可以尝试禁用 String Boot Redis 自动配置类。
您可以在注释中执行此操作:
@SpringBootApplication(
exclude = { RedisAutoConfiguration.class, RedisRepositoriesAutoConfiguration.class }
)
Run Code Online (Sandbox Code Playgroud)
或者在您的属性文件中:
spring.autoconfigure.exclude= \
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration, \
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration
Run Code Online (Sandbox Code Playgroud)
您还可以使用此属性禁用 Redis 存储库配置:
spring.data.redis.repositories.enabled: false
Run Code Online (Sandbox Code Playgroud)
一旦禁用 Redis 自动配置,您就可以RedisTemplate在您认为合适的时候自由实例化与 Redis 交互所需的内容。
例如,您可以按需初始化它,通过在实际需要时初始化所有必需的工厂来尝试建立与 Redis 的连接。您可以使用初始化 Redis 连接所需的逻辑,并且仅在连接可用时才初始化try,如下所示。catchRedisTemplate
一方面:
spring.autoconfigure.exclude= \
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration, \
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration
Run Code Online (Sandbox Code Playgroud)
在另一:
spring.data.redis.repositories.enabled: false
Run Code Online (Sandbox Code Playgroud)
您可以RedisTemplate按照您认为合适的方式使用它,当然,可以根据需要缓存和重用它。
这些方法可以在为此任务创建的服务或帮助程序类中定义,当然不能在您的配置类中定义。
| 归档时间: |
|
| 查看次数: |
3186 次 |
| 最近记录: |