如果连接状态发生变化,如何观察并重新加载bean

问题描述 投票:0回答:1

我有一个支持 Redis 缓存的 Spring Boot 应用程序

在运行时有没有办法检测redis服务器是否关闭/启动?

并在此基础上重新加载连接应用程序和redis的Bean来重新加载

例如,如果连接正常,则在 redis 在线时使用

redisCacheManagerOnline
bean,在 redis 离线时使用
redisCacheManagerOffline
bean

示例代码

`@Bean
fun redisCacheManagerOnline(
    jedisConnectionFactory: JedisConnectionFactory,
    redisTemplate: RedisTemplate<String, Any>
): CacheManager {
    val redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
        .disableCachingNullValues()
        .entryTtl(Duration.ofDays(1))
        .serializeValuesWith(
            RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.valueSerializer)
        )
    return RedisCacheManager
        .RedisCacheManagerBuilder.fromConnectionFactory(jedisConnectionFactory)
        .cacheDefaults(redisCacheConfiguration)
        .build()
}

@Bean
fun redisCacheManagerOffline(): CacheManager {
    return NoOpCacheManager()
}`

有没有办法在应用程序运行时执行此操作?

我尝试了

redisCacheManagerOnline
尝试捕获并返回
NoOpCacheManager()
如果有任何错误,但它在应用程序运行时不起作用并且我关闭了 redis 服务器

spring spring-boot redis jedis
1个回答
0
投票

要实现运行时根据Redis服务器的可用性动态切换redisCacheManagerOnline和redisCacheManagerOffline,需要引入一种机制:

定期检查Redis服务器的状态。 在两个 bean 之间动态切换。 第 1 步:创建 Redis 健康检查器 您可以创建一个计划任务,定期 ping Redis 服务器以检查其可用性。

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import java.util.concurrent.atomic.AtomicBoolean

@Component
class RedisHealthChecker @Autowired constructor(
    private val redisConnectionFactory: RedisConnectionFactory,
    private val redisTemplate: RedisTemplate<String, Any>
) {

    private val redisAvailable = AtomicBoolean(true)

    fun isRedisAvailable(): Boolean {
        return redisAvailable.get()
    }
enter code here

    @Scheduled(fixedDelay = 5000)
    fun checkRedisConnection() {
        try {
            redisConnectionFactory.connection.ping()
            redisAvailable.set(true)
        } catch (e: Exception) {
            redisAvailable.set(false)
        }
    }
}

第 2 步:动态加载适当的缓存管理器 Bean 您可以创建一个工厂或提供程序,根据 Redis 服务器的可用性提供适当的 CacheManager。该工厂将使用 RedisHealthChecker 来确定使用哪个 CacheManager。

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.cache.RedisCacheManager
import org.springframework.data.redis.cache.NoOpCacheManager
import org.springframework.cache.CacheManager

@Configuration
class CacheManagerProvider @Autowired constructor(
    private val redisHealthChecker: RedisHealthChecker,
    private val jedisConnectionFactory: JedisConnectionFactory,
    private val redisTemplate: RedisTemplate<String, Any>
) {

    @Bean
    fun cacheManager(): CacheManager {
        return if (redisHealthChecker.isRedisAvailable()) {
            redisCacheManagerOnline(jedisConnectionFactory, redisTemplate)
        } else {
            redisCacheManagerOffline()
        }
    }

    private fun redisCacheManagerOnline(
        jedisConnectionFactory: JedisConnectionFactory,
        redisTemplate: RedisTemplate<String, Any>
    ): CacheManager {
        val redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
            .disableCachingNullValues()
            .entryTtl(Duration.ofDays(1))
            .serializeValuesWith(
                RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.valueSerializer)
            )
        return RedisCacheManager
            .RedisCacheManagerBuilder.fromConnectionFactory(jedisConnectionFactory)
            .cacheDefaults(redisCacheConfiguration)
            .build()
    }

    private fun redisCacheManagerOffline(): CacheManager {
        return NoOpCacheManager()
    }
}

第3步:使Bean自动重新加载 为了确保缓存管理器在运行时动态切换,您可以利用 Spring 的 @RefreshScope 或创建自定义机制来重新加载 bean。但是,@RefreshScope 通常适用于配置更改,而不是运行时可用性检查。

相反,请考虑使用 ApplicationContext 来重新注册 bean 的自定义解决方案:

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import org.springframework.stereotype.Component

@Component
class DynamicCacheManagerReloader @Autowired constructor(
    private val cacheManagerProvider: CacheManagerProvider,
    private val applicationContext: ApplicationContext
) {

    fun reloadCacheManager() {
        val cacheManager = cacheManagerProvider.cacheManager()
        val beanFactory = applicationContext.autowireCapableBeanFactory
        beanFactory.autowireBean(cacheManager)
        beanFactory.initializeBean(cacheManager, "cacheManager")
    }
}

第四步:Redis状态变化时触发Reload 您可以修改 RedisHealthChecker 以在 Redis 服务器状态发生变化时触发缓存管理器重新加载:

@Component
class RedisHealthChecker @Autowired constructor(
    private val redisConnectionFactory: RedisConnectionFactory,
    private val redisTemplate: RedisTemplate<String, Any>,
    private val dynamicCacheManagerReloader: DynamicCacheManagerReloader
) {

    private val redisAvailable = AtomicBoolean(true)

    fun isRedisAvailable(): Boolean {
        return redisAvailable.get()
    }

    @Scheduled(fixedDelay = 5000)
    fun checkRedisConnection() {
        try {
            redisConnectionFactory.connection.ping()
            if (!redisAvailable.get()) {
                redisAvailable.set(true)
                dynamicCacheManagerReloader.reloadCacheManager()
            }
        } catch (e: Exception) {
            if (redisAvailable.get()) {
                redisAvailable.set(false)
                dynamicCacheManagerReloader.reloadCacheManager()
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.