Spring Boot Redis 序列化方案的選擇

Redis的使用愈來愈普遍,當碰見性能瓶頸時,咱們應該如何去解決呢?java

文章對應的項目見 spring-boot-skillgit

Redis序列化方案

Spring Boot Redis

Spring Boot Data Redis給咱們提供了即插即用的體驗,大部分默認配置已經知足了咱們的需求,而其中序列化方案選擇的是原生的JdkSerializationRedisSerializerredis

RedisTemplate.java
if (defaultSerializer == null) {

	defaultSerializer = new JdkSerializationRedisSerializer(
			classLoader != null ? classLoader : this.getClass().getClassLoader());
}

複製代碼

固然,咱們也能夠選擇Spring Boot Data Redis的其餘序列化方案進行配置。spring

RedisSerializer的實現

在此基礎上,咱們能夠自定義咱們本身的序列化方案。json

自定義JSON序列化方案

FastJsonRedisSerializer.java
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
    private FastJsonConfig fastJsonConfig = new FastJsonConfig();
    private Class<T> type;

    public FastJsonRedisSerializer(Class<T> type) {
        this.type = type;
    }

    public FastJsonConfig getFastJsonConfig() {
        return fastJsonConfig;
    }

    public void setFastJsonConfig(FastJsonConfig fastJsonConfig) {
        this.fastJsonConfig = fastJsonConfig;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        try {
            return JSON.toJSONBytes(
                    fastJsonConfig.getCharset(),
                    t,
                    fastJsonConfig.getSerializeConfig(),
                    fastJsonConfig.getSerializeFilters(),
                    fastJsonConfig.getDateFormat(),
                    JSON.DEFAULT_GENERATE_FEATURE,
                    fastJsonConfig.getSerializerFeatures()
            );
        } catch (Exception ex) {
            throw new SerializationException("Could not serialize: " + ex.getMessage(), ex);
        }
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        try {
            return (T) JSON.parseObject(
                    bytes,
                    fastJsonConfig.getCharset(),
                    type,
                    fastJsonConfig.getParserConfig(),
                    fastJsonConfig.getParseProcess(),
                    JSON.DEFAULT_PARSER_FEATURE,
                    fastJsonConfig.getFeatures()
            );
        } catch (Exception ex) {
            throw new SerializationException("Could not deserialize: " + ex.getMessage(), ex);
        }
    }
}
複製代碼

固然,這個是基於fastjson的序列化方案,不只提供了相比於JDK序列化更小的體積,序列化和反序列化的速度上也更快。安全

FST和Kryo序列化方案

這裏就粘貼相關代碼了,詳情可見 redis-serializer-linebash

性能對比(基準)

JDKFastJsonFSTKryo測試結果以下,測試項目可見 redis-serializer-lineide

原生JDK序列化方案[序列化100000次]耗時:2160 ms, 大小 44000000
原生JDK序列化方案[序列化100000次]耗時:1406 ms, 大小 44000000
FastJson序列化方案[序列化100000次]耗時:679 ms, 大小 18800000
FastJson序列化方案[序列化100000次]耗時:289 ms, 大小 18800000
FST序列化方案[序列化100000次]耗時:273 ms, 大小 10400000
FST序列化方案[序列化100000次]耗時:130 ms, 大小 10400000
Kryo序列化方案[序列化100000次]耗時:498 ms, 大小 14000000
Kryo序列化方案[序列化100000次]耗時:215 ms, 大小 14000000
複製代碼

總結

FSTKryo提供了更小的體積和更快的序列化速度,比Fastjson更有性能優點。可是須要提早將須要序列化的對象進行register,這增長了編碼難度。而Kryo線程不安全,更須要進行處理,好比經過KryoPool進行池化處理。spring-boot

經過更換序列化方案,能夠解決Redis IO壓力過大的問題,提高性能。微服務

外話

Dubbo的項目中提供了大量的序列化方案,在IO傳輸中體積小,速度快,因此在微服務領域比Spring Cloud更具備性能優點。咱們在實現序列化是能夠參考如下Dubbo的源碼進行編碼,畢竟千錘百煉的代碼頗有借鑑價值。

好比,DubboFST的建立時,會對須要序列化的對象進行 registerClass, 這會顯著的加強性能。而在使用Kryo時,不只register序列化的對象,還須要針對基本類型進行register

FST 自己已經對基本類型進行註冊了,因此FST在易用性上比Kryo更有優點,也提供了@Version對POJO的新增字段進行版本管理。

歡迎關注我呀,我會不按期更新開發中的小技巧,小手段。Git地址:gitee.com/SoftMeng/sp…

Dubbo中的序列化舉例

FstFactory.java
public FstFactory() {
        SerializableClassRegistry.getRegisteredClasses().keySet().forEach(conf::registerClass);
    }
複製代碼
Kryo.java
public Kryo create() {
        if (!kryoCreated) {
            kryoCreated = true;
        }

        Kryo kryo = new CompatibleKryo();

        // TODO
// kryo.setReferences(false);
        kryo.setRegistrationRequired(registrationRequired);

        kryo.addDefaultSerializer(Throwable.class, new JavaSerializer());
        kryo.register(Arrays.asList("").getClass(), new ArraysAsListSerializer());
        kryo.register(GregorianCalendar.class, new GregorianCalendarSerializer());
        kryo.register(InvocationHandler.class, new JdkProxySerializer());
        kryo.register(BigDecimal.class, new DefaultSerializers.BigDecimalSerializer());
        kryo.register(BigInteger.class, new DefaultSerializers.BigIntegerSerializer());
        kryo.register(Pattern.class, new RegexSerializer());
        kryo.register(BitSet.class, new BitSetSerializer());
        kryo.register(URI.class, new URISerializer());
        kryo.register(UUID.class, new UUIDSerializer());
        UnmodifiableCollectionsSerializer.registerSerializers(kryo);
        SynchronizedCollectionsSerializer.registerSerializers(kryo);

        // now just added some very common classes
        // TODO optimization
        kryo.register(HashMap.class);
        kryo.register(ArrayList.class);
        kryo.register(LinkedList.class);
        kryo.register(HashSet.class);
        kryo.register(TreeSet.class);
        kryo.register(Hashtable.class);
        kryo.register(Date.class);
        kryo.register(Calendar.class);
        kryo.register(ConcurrentHashMap.class);
        kryo.register(SimpleDateFormat.class);
        kryo.register(GregorianCalendar.class);
        kryo.register(Vector.class);
        kryo.register(BitSet.class);
        kryo.register(StringBuffer.class);
        kryo.register(StringBuilder.class);
        kryo.register(Object.class);
        kryo.register(Object[].class);
        kryo.register(String[].class);
        kryo.register(byte[].class);
        kryo.register(char[].class);
        kryo.register(int[].class);
        kryo.register(float[].class);
        kryo.register(double[].class);

        for (Class clazz : registrations) {
            kryo.register(clazz);
        }

        SerializableClassRegistry.getRegisteredClasses().forEach((clazz, ser) -> {
            if (ser == null) {
                kryo.register(clazz);
            } else {
                kryo.register(clazz, (Serializer) ser);
            }
        });

        return kryo;
    }
複製代碼
相關文章
相關標籤/搜索