Отслеживание проблем сериализации повторного выпуска

Java

задний план

Изначально проект использовал jedis для подключения redis, но учитывая необходимость использования блокировки redis, его заменили на удобный и быстрыйredisson, но после использования redisson будет сообщено об ошибке декодирования Конкретная информация следующая:

2019-05-15 13:39:59.973 [redisson-netty-2-3] ERROR o.r.c.h.CommandDecoder [decodeCommand:203]     - Unable to decode data. channel: [id: 0x477c5ced, L:/192.168.4.94:57423 - R:10.10.10.43/10.10.10.43:6379], reply: ReplayingDecoderByteBuf(ridx=102, widx=102), command: (GET), params: [Geek:xxxxx:xxxx]
java.io.IOException: java.lang.NullPointerException
    at org.nustaq.serialization.FSTObjectInput.readObject(FSTObjectInput.java:247)
    at org.redisson.codec.FstCodec$1.decode(FstCodec.java:228)
    at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:368)
    at org.redisson.client.handler.CommandDecoder.decodeCommand(CommandDecoder.java:200)
    at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:140)
    at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:115)
    at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:502)
    at io.netty.handler.codec.ReplayingDecoder.callDecode(ReplayingDecoder.java:366)
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:278)

тестовый код

RBucket ste = redissonClient.getBucket("Geek:add:ddd");
Object re = ste.get();

Учитывая проблему, которая может быть вызвана сериализацией, выяснитеNullPointer 3.10.6, установите кодек наStringCodec,Прямо сейчас

redissonClient.getConfig().setCodec(new StringCodec());

Но это не решило проблему, redisson по-прежнему использует дефолтныйFstCodec, с помощью мощной функции подсказки вы можете видеть, что getBucket принимает параметр кодека

idea.png

Измените код на

RBucket ste = redissonClient.getBucket("Geek:add:ddd", new StringCodec());
String re = ste.get();

Идеальное решение

вопрос

Почему прямая установка конфига redisson не действует?​Пошагово проверяйте исходный кодRedissonObject#RedissonObject

public RedissonObject(CommandAsyncExecutor commandExecutor, String name) {
        this(commandExecutor.getConnectionManager().getCodec(), commandExecutor, name);
}

Видно, что redisson по умолчанию отConnectionManagerполучатьcodecспособ, продолжать видеть, чтобыSingleConnectionManagerНапример,SingleConnectionManagerдаMasterSlaveConnectionManagerПодкласс , конкретное отношение диаграммы классов

connectionManage.png

config.java

public Config(Config oldConf) {
        setExecutor(oldConf.getExecutor());
        if (oldConf.getCodec() == null) {
            // use it by default
            oldConf.setCodec(new FstCodec());
        }
......
    }

То есть, когда обнаруживается, что исходный кодек пуст, он устанавливается вFstCodec

Взгляните на ключевую часть кода конфигурации Redisson.java.

protected Redisson(Config config) {
  this.config = config;
  Config configCopy = new Config(config);

  connectionManager = ConfigSupport.createConnectionManager(configCopy);
  evictionScheduler = new EvictionScheduler(connectionManager.getCommandExecutor());
  writeBehindService = new WriteBehindService(connectionManager.getCommandExecutor());
}

public static RedissonClient create(Config config) {
  Redisson redisson = new Redisson(config);
  if (config.isReferenceEnabled()) {
    redisson.enableRedissonReferenceSupport();
  }
  return redisson;
}

Видно, что конфигурация передается при инициализации redisson.

потому что я используюredisson-spring-boot-starter, взгляните на то, как инициализируется стартер Стартер redisson по умолчанию использует конфигурацию spring-data-redis.

@Bean(destroyMethod = "shutdown")
    @ConditionalOnMissingBean(RedissonClient.class)
    public RedissonClient redisson() throws IOException {
        Config config = null;
       ....

        if (redissonProperties.getConfig() != null) {
            ....
        } else {
            config = new Config();
            String prefix = "redis://";
            Method method = ReflectionUtils.findMethod(RedisProperties.class, "isSsl");
            if (method != null && (Boolean)ReflectionUtils.invokeMethod(method, redisProperties)) {
                prefix = "rediss://";
            }

            config.useSingleServer()
                .setAddress(prefix + redisProperties.getHost() + ":" + redisProperties.getPort())
                .setConnectTimeout(timeout)
                .setDatabase(redisProperties.getDatabase())
                .setPassword(redisProperties.getPassword());
        }

   return Redisson.create(config);

Возвращаясь к вопросу в начале, почему установка кодека redisson не вступает в силу напрямую? Из приведенного выше анализа мы можем узнать, что кодек унифицированных настроек redisson в основном передается через инициализацию.ConnectionManagerзаставить кодек вступить в силу и передатьredissonClient.getConfig().setCodec(...)путь не меняетсяConnectionManagerкодировка в .

В заключение:

  1. Если вы хотите настроить кодек, вам нужно самостоятельно инициализировать redissonClient [вызвать Redisson.create(config)] или переписать redisson-starter
  2. Когда степень настройки невелика, кодек по умолчанию можно использовать напрямую или в тело метода можно передать определенный кодек.

Reference

Эта статья написанаЗаблудший старый фермерсоздавать, использоватьCC BY 4.0 CNсоглашение к лицензии. Ее можно свободно воспроизводить и цитировать, но с обязательной подписью автора и указанием источника статьи.