Я использую клиент spring-data-redis(2.1.5.RELEASE) и jedis(2.10.2) для подключения к моему экземпляру azure redis из разных служб, работающих как приложение spring-boot.
Две службы имеют одинаковые методы кэширования и указывают на один и тот же кеш, реализуя следующую конфигурацию. Проблема, с которой я сталкиваюсь, заключается в том, что когда одна служба пытается прочитать кэшированное значение, созданное другой службой, возникает исключение де-серализации.
Исключение:
org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is org.springframework.core.NestedIOException: Failed to deserialize object type; nested exception is java.lang.ClassNotFoundException
Примечание: Я использую Redis только для кэширования данных, считанных из моей базы данных.
public RedisCacheWriter redisCacheWriter(RedisConnectionFactory connectionFactory) {
return RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
}
@Bean
public RedisCacheManager cacheManager() {
Map<String, RedisCacheConfiguration> cacheNamesConfigurationMap = new HashMap<>();
cacheNamesConfigurationMap.put("employers", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(90000)));
cacheNamesConfigurationMap.put("employees", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(90000)));
RedisCacheManager manager = new RedisCacheManager(redisCacheWriter(), RedisCacheConfiguration.defaultCacheConfig(), cacheNamesConfigurationMap);
manager.setTransactionAware(true);
manager.afterPropertiesSet();
return manager;
}
public RedisCacheWriter redisCacheWriter(RedisConnectionFactory connectionFactory) {
return RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
}
@Bean
public RedisCacheManager cacheManager() {
Map<String, RedisCacheConfiguration> cacheNamesConfigurationMap = new HashMap<>();
cacheNamesConfigurationMap.put("employees", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(90000)));
RedisCacheManager manager = new RedisCacheManager(redisCacheWriter(), RedisCacheConfiguration.defaultCacheConfig(), cacheNamesConfigurationMap);
manager.setTransactionAware(true);
manager.afterPropertiesSet();
return manager;
}
@Cacheable(value = "employees", key = "#employeesId")
public Employee getEmployee(String employeesId) {
//methods
}
public class Employee implements Serializable {
private String id;
private String name;
}
@MuhammadInshal Я добавил исключение, с которым столкнулся.
@Venkat — это два отдельных приложения или одно и то же приложение развернуто дважды?
@Jerome эти два являются отдельными приложениями, и если одно и то же приложение будет развернуто дважды, служба сможет читать кэшированные данные (которые создаются этой службой)
Нашел проблему. Имел Employee.java в разных пакетах
@Venkat, ты нашел какое-нибудь решение для этого?
@Roul Как уже упоминалось. Классы сотрудников сериализуются с именем пакета. Таким образом, в разных микросервисах класс Employee должен добавляться по одному и тому же пути к пакету. Например. : src/java/com/company/domain/Employee




Я бы посоветовал вам убедиться, что классы Employee идентичны, а затем добавить в классы поле serialVersionUID. Очистите кэш Redis и повторите попытку.
Это из Javadocs в интерфейсе java.io.Serializable:
If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification. However, it is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected
InvalidClassExceptions during deserialization. Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value. It is also strongly advised that explicit serialVersionUID declarations use theprivatemodifier where possible, since such declarations apply only to the immediately declaring class--serialVersionUID fields are not useful as inherited members.
Я добавил serialVersionUID, как вы предложили, но все равно сталкиваюсь с тем же ClassNotFoundException
Либо убедитесь, что возвращенный (де(сериализованный)) объект находится в том же пакете, либо запретите регистрацию класса в данных. Это происходит потому, что имя класса с полным именем пакета устанавливается в данные (в моем случае JSON). Вот конфигурация, которую я использовал
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.cache.RedisCacheManagerBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext.SerializationPair;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
@Configuration
@RequiredArgsConstructor
public class RedisConfig {
@Bean
RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
return builder -> {
Map<String, RedisCacheConfiguration> configurationMap = new HashMap<>();
configurationMap.put("whatever-cache-name",
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(3600))
.serializeValuesWith(
SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<>(Employee.class))
)
);
builder.withInitialCacheConfigurations(configurationMap);
};
}
}
Только
Просто используйте вместо этого другой сериализатор
@AutoWired
ObjectMapper objectMapper;
@Bean
RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer() {
return builder -> {
Map<String, RedisCacheConfiguration> configurationMap = new HashMap<>();
configurationMap.put("whatever-cache-name",
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(3600))
.serializeValuesWith(
SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper))
)
);
builder.withInitialCacheConfigurations(configurationMap);
};
}
Обратите внимание, что по умолчанию GenericJackson2JsonRedisSerializer создает новый ObjectMapper, если вы не укажете его в конструкторе, и реализация по умолчанию добавит полное имя класса в данные, и поэтому я использовал тот, который предоставлен Spring, поскольку он был правильно настроен достаточно в тот случай.
Можете ли вы опубликовать точное исключение, которое вы получаете?