FST快速序列化和反序列化, Redis使用FST

FST

快速序列化和反序列化

FST: github

参考

参考

介绍

FST的使用有两种方式,一种是快捷方式,另一种需要使用ObjectOutput和ObjectInput。

快捷方式

static void quickStart() {
    FSTConfiguration conf = FSTConfiguration.createAndroidDefaultConfiguration();
    FstObject object = new FstObject();
    object.setName("huaijin");
    object.setAge(30);
    System.out.println("serialization, " + object);
    
    byte[] bytes = conf.asByteArray(object);
    
    FstObject newObject = (FstObject) conf.asObject(bytes);
    
    System.out.println("deSerialization, " + newObject);
}

直接使用FSTConfiguration提供的序列化和反序列化接口。FSTConfiguration也提供了注册对象的Class接口,如果不注册,默认会将对象的Class Name写入。

这个提供了易用高效的API方式,不使用ByteArrayOutputStreams而直接得到byte[]。

ObjectOutput和ObjectInput

static FSTConfiguration conf = FSTConfiguration.createAndroidDefaultConfiguration();

static void writeObject(OutputStream outputStream, FstObject fstObject) throws IOException {
    FSTObjectOutput out = conf.getObjectOutput(outputStream);
    out.writeObject(fstObject);
    out.close();
}

static FstObject readObject(InputStream inputStream) throws Exception {
    FSTObjectInput input = conf.getObjectInput(inputStream);
    FstObject fstObject = (FstObject) input.readObject(FstObject.class);
    input.close();
    return fstObject;
}

能更细腻控制序列化的写入写出

配置

pom

<!-- 用于序列化和反序列化-->
<dependency>
    <groupId>de.ruedigermoeller</groupId>
    <artifactId>fst</artifactId>
    <version>2.57</version>
</dependency>

FSTSerializer-fst单例

import org.nustaq.serialization.FSTConfiguration;
import org.nustaq.serialization.FSTDecoder;
import org.nustaq.serialization.FSTEncoder;
import org.nustaq.serialization.coders.FSTStreamDecoder;
import org.nustaq.serialization.coders.FSTStreamEncoder;

import java.io.IOException;
import java.lang.reflect.Field;
/**
 * 使用fts进行序列化
 */
public class FSTSerializer {

    static class FSTDefaultStreamCoderFactory implements FSTConfiguration.StreamCoderFactory {
        // 单例的生成工厂,
        // 并且让FSTStreamDecoder.chBufS FSTStreamDecoder.ascStringCache为空

        Field chBufField;
        Field ascStringCacheField;

        {
            try {
                chBufField = FSTStreamDecoder.class.getDeclaredField("chBufS");
                ascStringCacheField = FSTStreamDecoder.class.getDeclaredField("ascStringCache");
            } catch (Exception e) {
                throw new IllegalStateException(e);
            }
            ascStringCacheField.setAccessible(true);
            chBufField.setAccessible(true);
        }

        private FSTConfiguration fstConfiguration;

        FSTDefaultStreamCoderFactory(FSTConfiguration fstConfiguration) {
            this.fstConfiguration = fstConfiguration;
        }

        @Override
        public FSTEncoder createStreamEncoder() {
            return new FSTStreamEncoder(fstConfiguration);
        }

        @Override
        public FSTDecoder createStreamDecoder() {
            return new FSTStreamDecoder(fstConfiguration) {
                @Override
                public String readStringUTF() throws IOException {
                    try {
                        String res = super.readStringUTF();
                        chBufField.set(this, null);// xField.set(FSTStreamDecoder obj, Object value) 把FSTStreamDecoder对象的xField设置为value
                        return res;
                    } catch (Exception e) {
                        throw new IOException(e);
                    }
                }

                @Override
                public String readStringAsc() throws IOException {
                    try {
                        String res = super.readStringAsc();
                        ascStringCacheField.set(this, null);
                        return res;
                    } catch (Exception e) {
                        throw new IOException(e);
                    }
                }
            };
        }

        static ThreadLocal input = new ThreadLocal();
        static ThreadLocal output = new ThreadLocal();

        @Override
        public ThreadLocal getInput() {
            return input;
        }

        @Override
        public ThreadLocal getOutput() {
            return output;
        }

    }

    private static class InstanceHolder { // 生成单例
        private static final FSTConfiguration INSTANCE = FSTConfiguration.createDefaultConfiguration();
        static {
            INSTANCE.setStreamCoderFactory(new FSTDefaultStreamCoderFactory(INSTANCE));
        }
    }

    public FSTConfiguration getConfig() {// 返回单例
        return InstanceHolder.INSTANCE;
    } 
}

为什么解码之前要清空缓存?

装载: Fst反序列化失败_weixin_33708432的博客-CSDN博客

fst在做反序列化时 由于该数据原先序列化的时候类(可以称之为模板)是有10个字段,但是当开发增加了2个字段后 由于既存的数据中已经均是10个字段的数据(实例)但是事实上新的版本中已经变成12个字段了。
当反序列化时由于根据对应的class信息(没有指定version annotation)导致反序列化报错。
要求必须指定version,如果有需要设置默认值等等可以使用VersionConflictListener那么解决方案也很简单,当增加字段的时候直接将缓存清空即可(避免增加version字段)

FstRedisSerializer-redis使用fst单例

import com.yami.shop.common.serializer.FSTSerializer;
import lombok.SneakyThrows;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.lang.Nullable;
/**
 * 使用fst 进行reids的序列化
 */
public class FstRedisSerializer implements RedisSerializer<Object> {

    private static final byte[] EMPTY_ARRAY = new byte[0];

    @Override
    @SneakyThrows
    public byte[] serialize(Object o) {
        if (o == null) {
            return EMPTY_ARRAY;
        }
        return new FSTSerializer().getConfig().asByteArray(o);
    }

    @Override
    @SneakyThrows
    public Object deserialize(byte[] bytes) {
        if (isEmpty(bytes)) {
            return null;
        }
        return new FSTSerializer().getConfig().asObject(bytes);
    }

    private static boolean isEmpty(@Nullable byte[] data) {
        return (data == null || data.length == 0);
    }
}

FstCodec-redisson使用fst单例

import org.nustaq.serialization.FSTConfiguration;
import org.nustaq.serialization.FSTObjectInput;
import org.nustaq.serialization.FSTObjectOutput;
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 io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
/**
 * 被redisson使用
 * Efficient and speedy serialization codec fully
 * compatible with JDK Serialization codec.
 *
 * https://github.com/RuedigerMoeller/fast-serialization
 *
 * @author Nikita Koksharov
 *
 */
public class FstCodec extends BaseCodec {

    private final FSTConfiguration config;

    public FstCodec() {
        config = new FSTSerializer().getConfig();
    }

    private final Decoder<Object> decoder = new Decoder<Object>() {
        @Override
        public Object decode(ByteBuf buf, State state) throws IOException {
            ByteBufInputStream in = new ByteBufInputStream(buf);
            FSTObjectInput inputStream = config.getObjectInput(in);
            try {
                return inputStream.readObject();
            } catch (IOException e) {
                throw e;
            } catch (Exception e) {
                throw new IOException(e);
            }
        }
    };

    private final Encoder encoder = new Encoder() {

        @Override
        public ByteBuf encode(Object in) throws IOException {
            ByteBuf out = ByteBufAllocator.DEFAULT.buffer();
            ByteBufOutputStream os = new ByteBufOutputStream(out);
            FSTObjectOutput oos = config.getObjectOutput(os);
            try {
                oos.writeObject(in);
                oos.flush();
                return os.buffer();
            } catch (IOException e) {
                out.release();
                throw e;
            } catch (Exception e) {
                out.release();
                throw new IOException(e);
            }
        }
    };

    @Override
    public Decoder<Object> getValueDecoder() {
        return decoder;
    }

    @Override
    public Encoder getValueEncoder() {
        return encoder;
    }

    @Override
    public ClassLoader getClassLoader() {
        if (config.getClassLoader() != null) {
            return config.getClassLoader();
        }

        return super.getClassLoader();
    }

}
上一篇:数组


下一篇:(转)Python 对象协议