Java 带有Redis Sentinel的Spring引导缓存始终连接到主节点

Java 带有Redis Sentinel的Spring引导缓存始终连接到主节点,java,spring,redis,redis-sentinel,Java,Spring,Redis,Redis Sentinel,我有一个SpringBoot(2.3.1.RELEASE)应用程序,它使用RedisSentinel进行缓存。 这是我对Sentinel连接的配置: @Bean public LettuceConnectionFactory redisConnectionFactory() { RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration() .master(redisPrope

我有一个SpringBoot(2.3.1.RELEASE)应用程序,它使用RedisSentinel进行缓存。 这是我对Sentinel连接的配置:

@Bean
public LettuceConnectionFactory redisConnectionFactory() {
   RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
            .master(redisProperties.getSentinel().getMaster());
    redisProperties.getSentinel().getNodes().forEach(s -> sentinelConfig.sentinel(s, redisProperties.getPort()));
    sentinelConfig.setPassword(RedisPassword.of(redisProperties.getPassword()));
    return new LettuceConnectionFactory(sentinelConfig);
}
这是我的缓存管理器配置:

@Bean
public RedisCacheManager cacheManager() {
    Map<String, RedisCacheConfiguration> cacheConfigs = new HashMap<>();
    cacheConfigs.put("cache1", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(ttlMinutes)));
    cacheConfigs.put("cache2", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(ttlMinutes)));

    return RedisCacheManager.builder(redisConnectionFactory())
            .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(ttlMinutes)))
            .withInitialCacheConfigurations(cacheConfigs)
            .transactionAware()
            .build();
}
@Bean
公共RedisCacheManager cacheManager(){
Map cacheConfigs=new HashMap();
cacheConfigs.put(“cache1”,RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(ttlMinutes)));
cacheConfigs.put(“cache2”,RedisCacheConfiguration.defaultCacheConfig().entryTtl(持续时间:分钟(ttlMinutes)));
返回RedisCacheManager.builder(redisConnectionFactory())
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(持续时间:分钟(ttlMinutes)))
.withInitialCacheConfigurations(缓存配置)
.TransactionWare()
.build();
}
从缓存的角度来看,一切都很好

但是,如果我在
io.莴苣.core.protocol.CommandHandler
中打开调试日志,我会发现它总是连接到同一个节点(主节点)。我可以通过查看节点上的日志来确认这一点

无论我在网上看到什么,这似乎都是正确的配置

这就引出了我的问题:

  • 是否可以将Spring缓存抽象配置为仅将主节点用于写入,将从节点用于读取?

这一期望是否成立?或者这就是Sentinel应该使用的方式(所有请求都转到master)?

是的,可以这样做。

    @Bean
    public RedisConnectionFactory lettuceConnectionFactory(RedisProperties redisProperties, LettucePoolingClientConfiguration lettucePoolingClientConfiguration) {
        final RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration().master(redisProperties.getMaster());
        redisSentinelConfiguration.setDatabase(redisProperties.getDbIndex());
        addSentinels(redisProperties, redisSentinelConfiguration);
        return new LettuceConnectionFactory(redisSentinelConfiguration, lettucePoolingClientConfiguration);
    }

    private void addSentinels(RedisProperties redisProperties, RedisSentinelConfiguration redisSentinelConfiguration) {
        redisProperties.getNodes()
                .forEach(node -> {
                    final String[] splitted = node.split(NODE_SPLITTER);
                    final String host = splitted[0];
                    final int port = Integer.parseInt(splitted[1]);
                    redisSentinelConfiguration.addSentinel(RedisNode.newRedisNode()
                            .listeningAt(host, port)
                            .build());
                });
    }

    @Bean
    public LettucePoolingClientConfiguration lettucePoolingClientConfiguration(ClientOptions clientOptions, ClientResources clientResources, RedisProperties redisProperties) {
        return LettucePoolingClientConfiguration.builder()
                .readFrom(ReadFrom.ANY_REPLICA)
                .poolConfig(genericObjectPoolConfig(redisProperties))
                .clientOptions(clientOptions)
                .clientResources(clientResources)
                .build();
    }

    @Bean
    public GenericObjectPoolConfig genericObjectPoolConfig(RedisProperties redisProperties) {
        final GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMaxIdle(redisProperties.getPoolMaxIdle());
        config.setMinIdle(redisProperties.getPoolMinIdle());
        config.setMaxTotal(redisProperties.getPoolMaxTotal());
        config.setBlockWhenExhausted(false);
        config.setMaxWaitMillis(redisProperties.getPoolMaxWaitMillis());
        return config;
    }

    @Bean
    public ClientOptions clientOptions(RedisProperties redisProperties) {
        return ClientOptions.builder()
                .autoReconnect(true)
                .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
                .timeoutOptions(TimeoutOptions.builder().fixedTimeout(Duration.ofSeconds(redisProperties.getCommandTimedOutSec())).build())
                .build();
    }

    @Bean(destroyMethod = "shutdown")
    public ClientResources clientResources() {
        return DefaultClientResources.create();
    }
来自

据说Spring Data Redis提供了Redis主/副本设置 哪一个不是 不仅允许数据安全地存储在更多节点上,而且还允许 使用从副本读取数据,同时将写入推送到主副本 生菜

为此,必须更新配置类中的
redisConnectionFactory()
方法:

 @Bean
 public LettuceConnectionFactory redisConnectionFactory() {
   LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
            .readFrom(ReadFrom.REPLICA_PREFERRED)
            .build();
   RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()
            .master(redisProperties.getSentinel().getMaster());
   redisProperties.getSentinel().getNodes().forEach(s -> sentinelConfig.sentinel(s, redisProperties.getPort()));
   sentinelConfig.setPassword(RedisPassword.of(redisProperties.getPassword()));
   return new LettuceConnectionFactory(sentinelConfig, clientConfig);
}

如果您想从所有从机(副本)写入主设备并仅读取,可以使用以下配置

这些配置包括连接池、向主节点写入和从所有具有循环负载平衡的从节点读取。

    @Bean
    public RedisConnectionFactory lettuceConnectionFactory(RedisProperties redisProperties, LettucePoolingClientConfiguration lettucePoolingClientConfiguration) {
        final RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration().master(redisProperties.getMaster());
        redisSentinelConfiguration.setDatabase(redisProperties.getDbIndex());
        addSentinels(redisProperties, redisSentinelConfiguration);
        return new LettuceConnectionFactory(redisSentinelConfiguration, lettucePoolingClientConfiguration);
    }

    private void addSentinels(RedisProperties redisProperties, RedisSentinelConfiguration redisSentinelConfiguration) {
        redisProperties.getNodes()
                .forEach(node -> {
                    final String[] splitted = node.split(NODE_SPLITTER);
                    final String host = splitted[0];
                    final int port = Integer.parseInt(splitted[1]);
                    redisSentinelConfiguration.addSentinel(RedisNode.newRedisNode()
                            .listeningAt(host, port)
                            .build());
                });
    }

    @Bean
    public LettucePoolingClientConfiguration lettucePoolingClientConfiguration(ClientOptions clientOptions, ClientResources clientResources, RedisProperties redisProperties) {
        return LettucePoolingClientConfiguration.builder()
                .readFrom(ReadFrom.ANY_REPLICA)
                .poolConfig(genericObjectPoolConfig(redisProperties))
                .clientOptions(clientOptions)
                .clientResources(clientResources)
                .build();
    }

    @Bean
    public GenericObjectPoolConfig genericObjectPoolConfig(RedisProperties redisProperties) {
        final GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMaxIdle(redisProperties.getPoolMaxIdle());
        config.setMinIdle(redisProperties.getPoolMinIdle());
        config.setMaxTotal(redisProperties.getPoolMaxTotal());
        config.setBlockWhenExhausted(false);
        config.setMaxWaitMillis(redisProperties.getPoolMaxWaitMillis());
        return config;
    }

    @Bean
    public ClientOptions clientOptions(RedisProperties redisProperties) {
        return ClientOptions.builder()
                .autoReconnect(true)
                .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
                .timeoutOptions(TimeoutOptions.builder().fixedTimeout(Duration.ofSeconds(redisProperties.getCommandTimedOutSec())).build())
                .build();
    }

    @Bean(destroyMethod = "shutdown")
    public ClientResources clientResources() {
        return DefaultClientResources.create();
    }
您应该为负载平衡读取模式导入新的核心版本(ReadFrom.ANY_REPLICA),它将随spring boot 2.4.0一起提供

pom.xml

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
            <version>6.0.1.RELEASE</version>
    </dependency>

org.springframework.boot
spring启动程序数据redis
生菜
莴苣核
生菜
莴苣核
6.0.1.1发布