一。技术改造背景
由于之前的比较陈旧的技术,后面发起了技术改造,redis整体改后使用redisson框架。
二。问题
改造完成后,使用方反馈 缓存获取异常 异常信息如下
Caused by: java.io.CharConversionException: Unexpected EOF in the middle of a 4-byte UTF-32 char: got 1, needed 4, at char #1, byte #5)
at com.fasterxml.jackson.core.io.UTF32Reader.reportUnexpectedEOF(UTF32Reader.java:187)
at com.fasterxml.jackson.core.io.UTF32Reader.loadMore(UTF32Reader.java:248)
at com.fasterxml.jackson.core.io.UTF32Reader.read(UTF32Reader.java:126)
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._loadMore(ReaderBasedJsonParser.java:276)
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._matchToken2(ReaderBasedJsonParser.java:2727)
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._matchToken(ReaderBasedJsonParser.java:2707)
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._handleOddValue(ReaderBasedJsonParser.java:1986)
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:802)
at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4761)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4667)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3666)
at org.redisson.codec.JsonJacksonCodec$2.decode(JsonJacksonCodec.java:99)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:393)
at org.redisson.client.handler.CommandDecoder.decodeCommand(CommandDecoder.java:205)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:144)
at org.redisson.client.handler.CommandDecoder.decode(CommandDecoder.java:120)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:519)
at io.netty.handler.codec.ReplayingDecoder.callDecode(ReplayingDecoder.java:366)
三。问题定位
从日志上来看 解密失败了,回头看未改造的redis 存储的序列化方式是 hessian 而改造后的 redis存储的序列化方式是JsonJacksonCodec 导致反序列化报错
四。问题解决
具体问题定位到了后 解决办法就已经有了。
- 修改redisson的序列化方式 保持和旧的序列化方式相同
- 做缓存迁移或者缓存全量失效(理论来讲不太现实)
那这个很明显,选择修改redisson的序列化方式 保持和旧的序列化方式相同。
redison目前支持的 序列化方式
Codec class name | Description |
---|---|
org.redisson.codec.Kryo5Codec |
Kryo 5 binary codec (Android compatible) Default codec |
org.redisson.codec.KryoCodec |
Kryo 4 binary codec |
org.redisson.codec.JsonJacksonCodec |
Jackson JSON codec. Stores type information in @class field (Android compatible) |
org.redisson.codec.TypedJsonJacksonCodec |
Jackson JSON codec which doesn’t store type id (@class field) during encoding and doesn’t require it for decoding |
org.redisson.codec.AvroJacksonCodec |
Avro binary json codec |
org.redisson.codec.SmileJacksonCodec |
Smile binary json codec |
org.redisson.codec.CborJacksonCodec |
CBOR binary json codec |
org.redisson.codec.MsgPackJacksonCodec |
MsgPack binary json codec |
org.redisson.codec.IonJacksonCodec |
Amazon Ion codec |
org.redisson.codec.SerializationCodec |
JDK Serialization binary codec (Android compatible) |
org.redisson.codec.LZ4Codec |
LZ4 compression codec. Uses Kryo5Codec for serialization by default |
org.redisson.codec.SnappyCodecV2 |
Snappy compression codec based on snappy-java project. Uses Kryo5Codec for serialization by default |
org.redisson.codec.MarshallingCodec |
JBoss Marshalling binary codec Deprecated! |
org.redisson.client.codec.StringCodec |
String codec |
org.redisson.client.codec.LongCodec |
Long codec |
org.redisson.client.codec.ByteArrayCodec |
Byte array codec |
org.redisson.codec.CompositeCodec |
Allows to mix different codecs as one |
很意外并没有 我们需要的hessian的序列化 只能手写! redisson 默认的序列化方式为Kryo5Codec
首先引入依赖
<dependency>
<groupId>com.caucho</groupId>
<artifactId>hessian</artifactId>
<version>4.0.66</version>
</dependency>
其次继承 BaseCodec 实现我们的 HessianCoder 即可 完整的实现如下
import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import lombok.extern.slf4j.Slf4j;
import org.redisson.client.codec.BaseCodec;
import org.redisson.client.handler.State;
import org.redisson.client.protocol.Decoder;
import org.redisson.client.protocol.Encoder;
import java.io.IOException;
/**
* 自定义实现 Hessian 序列化 兼容原有的序列化方式
*
* @author leon
* @date 2023-08-10 15:14:03
*/
@Slf4j
public class HessianCoder extends BaseCodec {
private final Encoder encoder = new Encoder() {
@Override
public ByteBuf encode(Object in) throws IOException {
try (ByteBufOutputStream os = new ByteBufOutputStream(ByteBufAllocator.DEFAULT.buffer())) {
HessianOutput ho = new HessianOutput(os);
ho.writeObject(in);
return os.buffer();
} catch (Exception e) {
log.error("Hessian序列化异常: {}", e.getMessage(), e);
throw new IOException(e);
}
}
};
private final Decoder<Object> decoder = new Decoder<Object>() {
@Override
public Object decode(ByteBuf buf, State state) throws IOException {
try (ByteBufInputStream inputStream = new ByteBufInputStream(buf)) {
HessianInput hi = new HessianInput(inputStream);
return hi.readObject();
} catch (Exception e) {
log.error("Hessian反序列化异常: {}", e.getMessage(), e);
throw new IOException(e);
}
}
};
@Override
public Decoder<Object> getValueDecoder() {
return decoder;
}
@Override
public Encoder getValueEncoder() {
return encoder;
}
}
最后修改我们的redisson配置 将序列化方式替换为HessianCoder 即可
在进行测试获取缓存 也没报错了。文章来源:https://uudwc.com/A/9vDDj
参考官方文档:https://github.com/redisson/redisson/wiki/4.-data-serialization文章来源地址https://uudwc.com/A/9vDDj