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();
    }

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM