redission自定义hessian序列化

一。技术改造背景

由于之前的比较陈旧的技术,后面发起了技术改造,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.cauchogroupId>
    <artifactId>hessianartifactId>
    <version>4.0.66version>
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://github.com/redisson/redisson/wiki/4.-data-serialization

你可能感兴趣的:(java,redisson,redisson,自定义序列化,hessian)