从 ReactiveRedisOperations 得到一个 key/keys 总是 return 空结果

Getting a key/keys from ReactiveRedisOperations always return empty result

我正在将传统的 Redis 缓存迁移到 Spring Data Reactive Redis。迁移后,我想测试我的新集成是否按预期工作,但我遇到了从特定 Redis Db 获取 key/keys 的问题,即: 我的 redisConfiguration class 看起来像这样:

import java.time.Duration;
import java.util.Properties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.ReactiveRedisOperations;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializationContext.RedisSerializationContextBuilder;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@PropertySource("classpath:redis.properties")
@Slf4j
public class RedisConfiguration {

  private static final String REDIS_PROPERTIES = "redis.properties";
  private final Properties redisProperties = readConfigurationFile(REDIS_PROPERTIES);

  @Value("${redis.host}")
  private String host;

  @Value("${redis.port}")
  private int port;

  @Value("${redis.password}")
  private String password;

  @Value("${redis.timeout}")
  private String timeout;

  @Bean(name = "reactiveRedisConnectionFactory")
  @Primary
  public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory() {
    return new LettuceConnectionFactory();
  }

  @Bean(name ="reactiveRedisTemplate")
  public ReactiveRedisOperations<String, Object> reactiveRedisTemplate(
      @Qualifier(value = "reactiveRedisConnectionFactory") ReactiveRedisConnectionFactory factory) {
    return prepareRedisTemplate(factory);
  }

  @Bean(name = "reactiveRedisUserConnectionFactory")
  public ReactiveRedisConnectionFactory reactiveRedisUserConnectionFactory() {
    RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration();
    String userDb = getProperty(redisProperties, RedisDb.USER_DB);

    setRedisProperties(redisConfiguration, userDb);

    LettuceClientConfiguration clientConfiguration =
        LettuceClientConfiguration.builder()
            .commandTimeout(Duration.ofMillis(Long.parseLong(timeout)))
            .build();

    logRedisConnectionDetails(redisConfiguration, clientConfiguration);

    return new LettuceConnectionFactory(redisConfiguration, clientConfiguration);
  }

  @Bean(name = "reactiveUserRedisTemplate")
  public ReactiveRedisOperations<String, Object> userRedisTemplate(
      @Qualifier(value = "reactiveRedisUserConnectionFactory")
          ReactiveRedisConnectionFactory connectionFactory) {
    return prepareRedisTemplate(connectionFactory);
  }

  @Bean(name = "redisRegistrationTokenConnectionFactory")
  public ReactiveRedisConnectionFactory reactiveRedisRegistrationTokenConnectionFactory() {
    RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration();

    String registrationTokenDb = getProperty(redisProperties, RedisDb.REGISTRATION_TOKEN_DB);
    setRedisProperties(redisConfiguration, registrationTokenDb);

    LettuceClientConfiguration clientConfiguration =
        LettuceClientConfiguration.builder()
            .commandTimeout(Duration.ofMillis(Long.parseLong(timeout)))
            .build();

    logRedisConnectionDetails(redisConfiguration, clientConfiguration);

    return new LettuceConnectionFactory(redisConfiguration, clientConfiguration);
  }

  @Bean(name = "reactiveRegistrationTokenRedisTemplate")
  public ReactiveRedisOperations<String, Object> registrationTokenRedisTemplate(
      @Qualifier(value = "redisRegistrationTokenConnectionFactory")
          ReactiveRedisConnectionFactory connectionFactory) {
    return prepareRedisTemplate(connectionFactory);
  }

  @Bean(name = "reactiveRedisWhitelistingConnectionFactory")
  public ReactiveRedisConnectionFactory redisWhitelistingConnectionFactory() {
    RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration();

    String whitelistingDb = getProperty(redisProperties, RedisDb.WHITELISTING_DB);
    setRedisProperties(redisConfiguration, whitelistingDb);

    LettuceClientConfiguration clientConfiguration =
        LettuceClientConfiguration.builder()
            .commandTimeout(Duration.ofMillis(Long.parseLong(timeout)))
            .build();

    logRedisConnectionDetails(redisConfiguration, clientConfiguration);

    return new LettuceConnectionFactory(redisConfiguration, clientConfiguration);
  }

  @Bean(name = "reactiveWhitelistingRedisTemplate")
  public ReactiveRedisOperations<String, Object> reactiveWhitelistingRedisTemplate(
      @Qualifier(value = "reactiveRedisWhitelistingConnectionFactory")
          ReactiveRedisConnectionFactory connectionFactory) {
    return prepareRedisTemplate(connectionFactory);
  }

  @Bean(name = "redisSerializer")
  public Jackson2JsonRedisSerializer<Object> redisSerializer() {
    return new Jackson2JsonRedisSerializer<>(Object.class);
  }

  private ReactiveRedisOperations<String, Object> prepareRedisTemplate(ReactiveRedisConnectionFactory connectionFactory) {
    Jackson2JsonRedisSerializer<Object> redisSerializer =
        new Jackson2JsonRedisSerializer<>(Object.class);

    RedisSerializationContextBuilder<String, Object> serializationContext =
        RedisSerializationContext.newSerializationContext(new StringRedisSerializer());

    RedisSerializationContext<String, Object> context =
        serializationContext
            .value(redisSerializer)
            .hashKey(redisSerializer)
            .hashValue(redisSerializer)
            .build();
    return new ReactiveRedisTemplate<>(connectionFactory, context);
  }

  private void setRedisProperties(RedisStandaloneConfiguration redisConfiguration, String redisDb) {
    redisConfiguration.setHostName(host);
    redisConfiguration.setPort(port);
    redisConfiguration.setDatabase(Integer.parseInt(redisDb));
    redisConfiguration.setPassword(RedisPassword.of(password));
  }

  private void logRedisConnectionDetails(
      RedisStandaloneConfiguration redisConfiguration,
      LettuceClientConfiguration clientConfiguration) {
    log.info(
        "Connected to Redis host: {}, port: {}, database: {}, commandTimeout: {}",
        redisConfiguration.getHostName(),
        redisConfiguration.getPort(),
        redisConfiguration.getDatabase(),
        clientConfiguration.getCommandTimeout().toSeconds());
  }
}

我的“测试”class 看起来像:

@SpringBootApplication
@Slf4j
public class ApiGatewayApplication implements CommandLineRunner {

  private final ReactiveRedisOperations<String, Object> reactiveWhitelistingRedisTemplate;
  private final ReactiveRedisOperations<String, Object> reactiveUserRedisTemplate;

  public ApiGatewayApplication(
      @Qualifier(value = "reactiveWhitelistingRedisTemplate") ReactiveRedisOperations<String, Object> reactiveWhitelistingRedisTemplate,
      @Qualifier(value = "reactiveUserRedisTemplate") ReactiveRedisOperations<String, Object> reactiveUserRedisTemplate) {
    this.reactiveWhitelistingRedisTemplate = reactiveWhitelistingRedisTemplate;
    this.reactiveUserRedisTemplate = reactiveUserRedisTemplate;
  }

  public static void main(String[] args) {
    SpringApplication.run(ApiGatewayApplication.class, args);
  }

  @Override
  public void run(String... args) {

    log.info("CASE1");
    log.info(
        String.valueOf(
            reactiveWhitelistingRedisTemplate
                .keys("*")
                .flatMap(reactiveWhitelistingRedisTemplate.opsForValue()::get)));
    log.info("CASE2");
    log.info(String.valueOf(reactiveWhitelistingRedisTemplate.scan()));
    log.info("CASE3");
    log.info(String.valueOf(reactiveUserRedisTemplate.keys("*")));
    log.info("CASE4");
    log.info(reactiveWhitelistingRedisTemplate.randomKey() + "RANDOM");
    log.info("CASE5");
    log.info(String.valueOf(reactiveWhitelistingRedisTemplate.opsForValue().get("whitelisting:zenon112")));
  }
}

在获取 key/keys 时,我总是收到空的 Mono/Flux 结果如下:

CASE1
FluxFlatMap
CASE2
FluxMap
CASE3
FluxMap
CASE4
MonoMapRANDOM
CASE5
MonoNext

我确信 Redis 不是空的,Redis 中的示例键是 whitelisting:ranomuser112

老实说,我对自己做错了什么很困惑,对于如何从反应式 Redis 获取特定密钥的建议,我将不胜感激。干杯!

我忽略了 .subscribe() 方法调用。没有它,反应结果将被忽略。问题已解决。