spring引导缓存总是连接到主节点

ep6jt1vc  于 2021-06-09  发布在  Redis
关注(0)|答案(2)|浏览(417)

我有一个springboot(2.3.1.release)应用程序,它使用redis sentinel进行缓存。这是我对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();
}

从缓存的Angular 来看,一切正常。
但是,如果在 io.lettuce.core.protocol.CommandHandler ,我看到它总是连接到同一个节点(主节点)。我可以通过查看节点上的日志来确认这一点。
无论我在网上看到什么,这似乎都是正确的配置。
这就引出了我的问题:
是否可以将spring缓存抽象配置为仅将主节点用于写入,将从节点用于读取?
这个期望是否成立?或者这就是sentinel应该使用的方式(所有请求都转到master)?

ee7vknir

ee7vknir1#

如果您想从所有从属服务器(副本)写入主服务器和只读服务器,可以使用以下配置。
这些配置包括连接池、向主节点的写入和从所有具有循环负载平衡的从节点的读取。

@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)导入新的核心版本,它将随SpringBoot2.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>
ao218c7q

ao218c7q2#

是的,可以做到。
来自spring数据redis docs-10.4.4。写入主机,从副本读取:
据说springdataredis提供了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);
}

相关问题