美文网首页互联网科技Spring
Spring Boot Redis 序列化方案的选择!

Spring Boot Redis 序列化方案的选择!

作者: java菲菲 | 来源:发表于2019-10-11 17:33 被阅读0次

    Redis的使用越来越广泛,当遇见性能瓶颈时,我们应该如何去解决呢?

    01 Spring Boot Redis

    Spring Boot Data Redis给我们提供了即插即用的体验,大部分默认配置已经满足了我们的需求,而其中序列化方案选择的是原生的JdkSerializationRedisSerializer

    1.1 RedisTemplate.java

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

    当然,我们也可以选择Spring Boot Data Redis的其他序列化方案进行配置。

    1.2 RedisSerializer的实现

    image.png

    在此基础上,我们可以自定义我们自己的序列化方案。

    02 自定义JSON序列化方案

    2.1 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序列化更小的体积,序列化和反序列化的速度上也更快。

    03 性能对比(基准)

    JDKFastJsonFSTKryo测试结果如下

    原生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
    

    04 总结

    FSTKryo提供了更小的体积和更快的序列化速度,比Fastjson更有性能优势。但是需要提前将需要序列化的对象进行register,这增加了编码难度。而Kryo线程不安全,更需要进行处理,比如通过KryoPool进行池化处理。

    通过更换序列化方案,可以解决Redis IO压力过大的问题,提升性能。

    05 外话

    Dubbo的项目中提供了大量的序列化方案,在IO传输中体积小,速度快,所以在微服务领域比Spring Cloud更具有性能优势。我们在实现序列化是可以参考以下Dubbo的源码进行便携,毕竟千锤百炼的代码很有借鉴价值。

    比如,DubboFST的创建时,会对需要序列化的对象进行 registerClass, 这会显著的增强性能。而在使用Kryo时,不仅register序列化的对象,还需要针对基本类型进行register

    FST 本身已经对基本类型进行注册了,所以FST在易用性上比Kryo更有优势。

    5.1 FstFactory.java

        public FstFactory() {
            SerializableClassRegistry.getRegisteredClasses().keySet().forEach(conf::registerClass);
        }
    

    5.2 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;
        }
    

    相关文章

      网友评论

        本文标题:Spring Boot Redis 序列化方案的选择!

        本文链接:https://www.haomeiwen.com/subject/kbdxmctx.html