序列化方案使用示例

235 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

示例代码的公共接口

该篇文章接上篇 序列化实际场景对比

public interface KvSerializer {
    /**
     * 将对象序列化为 byte 数组
     *
     * @param object 序列化的对象
     * @return
     */
    byte[] serialize(Object object) throws Throwable;

    /**
     * 将 byte 数组序列化为指定类型
     *
     * @param bytes byte 数组
     * @param clazz 类型
     * @param <T>   泛型 T,和 clazz 类型一致
     * @return
     */
    <T> T deserialize(byte[] bytes, Class<T> clazz) throws Throwable;
}

序列化使用代码示例

Kryo

官方文档:
中文:blog.csdn.net/fanjunjaden…
英文:github.com/EsotericSof…

  • Kryo序列化机制比默认的Java序列化机制速度要快,序列化后的数据要更小,大概是Java序列化机制的1/10。所以Kryo序列化优化以后,可以让网络传输的数据变少,在集群中耗费的内存资源大大减少
  • 使用kryo默认的序列化方式fieldSerializer, 对需要序列化的对象采取默认的操作。开启reference,关闭register
  • Kryo在类注册且reference关闭的情况下,序列化速度和大小明显 优于hessian和java,接近于protostuff。开启reference后将序列化速度将明显变慢,但仍旧优于hessian
  • 目前只有java实现,不能用于在多个系统、多种语言间的数据交换
  • 依赖
<dependency> 
    <groupId>com.esotericsoftware</groupId> 
    <artifactId>kryo</artifactId> 
    <version>4.0.2</version> 
</dependency> 
  • Kryo 序列化示例代码
public class KryoKvSerializer implements KvSerializer {
    private Logger log = LoggerFactory.getLogger(KryoKvSerializer.class);

    private static final ThreadLocal<Kryo> KRYO_THREAD_LOCAL = new ThreadLocal<Kryo>() {
        @Override
        protected Kryo initialValue() {
            Kryo kryo = new Kryo();
            /**
             * 不要轻易改变这里的配置,更改之后,序列化的格式就会发生变化,
             * 上线的同时就必须清除 Redis 里的所有缓存,
             * 否则那些缓存再回来反序列化的时候,就会报错
             */
            //支持对象循环引用(否则会栈溢出)
            kryo.setReferences(true); //默认值就是 true,添加此行的目的是为了提醒维护者,不要改变这个配置

            //不强制要求注册类(注册行为无法保证多个 JVM 内同一个类的注册编号相同;而且业务系统中大量的 Class 也难以一一注册)
            kryo.setRegistrationRequired(false); //默认值就是 false,添加此行的目的是为了提醒维护者,不要改变这个配置

            //Fix the NPE bug when deserializing Collections.
            ((Kryo.DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy())
                    .setFallbackInstantiatorStrategy(new StdInstantiatorStrategy());
            return kryo;
        }
    };


    private static KryoFactory factory = new KryoFactory() {
        @Override
        public Kryo create() {
            Kryo kryo = new Kryo();
            /**
             * 不要轻易改变这里的配置!更改之后,序列化的格式就会发生变化,
             * 上线的同时就必须清除 Redis 里的所有缓存,
             * 否则那些缓存再回来反序列化的时候,就会报错
             */
            //支持对象循环引用(否则会栈溢出)
            kryo.setReferences(true); //默认值就是 true,添加此行的目的是为了提醒维护者,不要改变这个配置

            //不强制要求注册类(注册行为无法保证多个 JVM 内同一个类的注册编号相同;而且业务系统中大量的 Class 也难以一一注册)
            kryo.setRegistrationRequired(false); //默认值就是 false,添加此行的目的是为了提醒维护者,不要改变这个配置

            //Fix the NPE bug when deserializing Collections.
            ((Kryo.DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy())
                    .setFallbackInstantiatorStrategy(new StdInstantiatorStrategy());
            return kryo;
        }
    };


    private static KryoPool pool = new KryoPool.Builder(factory).softReferences().build();

    @Override
    public byte[] serialize(Object object) throws Throwable {
        if (object == null) {
            return null;
        }
        Kryo kryo = KRYO_THREAD_LOCAL.get();
        byte[] bytes = null;
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            Output output = new Output(byteArrayOutputStream);
            kryo.writeClassAndObject(output, object);
            output.flush();
            output.close();
            bytes = byteArrayOutputStream.toByteArray();
        } catch (Exception e) {
            log.error("Kryo 序列化对象异常,异常信息为: {}", e);
            throw e;
        } finally {
            return bytes;
        }
    }

    @Override
    public <T> T deserialize(byte[] bytes, Class<T> clazz) throws Throwable {
        if (bytes == null) {
            return null;
        }
        Kryo kryo = pool.borrow();
        T object = null;
        try {
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            Input input = new Input(byteArrayInputStream);
            object = (T) kryo.readClassAndObject(input);
            input.close();
        } catch (Exception e) {
            log.error("Kryo 反序列化对象异常,异常信息为: {}", e);
            throw e;
        } finally {
            pool.release(kryo);
            return object;
        }
    }
}

protostuff

是google在原来的protobuffer是的优化产品。使用起来也比较简单易用,目前效率也是最好的一种序列化工具

  • 依赖
 <dependency> 
    <groupId>io.protostuff</groupId> 
    <artifactId>protostuff-core</artifactId> 
    <version>1.4.0</version> 
</dependency> 
<dependency> 
    <groupId>io.protostuff</groupId> 
    <artifactId>protostuff-runtime</artifactId> 
    <version>1.4.0</version> 
</dependency>
  • Protostuff 序列化示例代码
public class ProtostuffKvSerializer implements KvSerializer {
    private Logger log = LoggerFactory.getLogger(ProtostuffKvSerializer.class);

    private static Objenesis objenesis = new ObjenesisStd(true);
    private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap();

    private static <T> Schema<T> getSchema(Class<T> cls) {
        Schema<T> schema = (Schema)cachedSchema.get(cls);
        if (schema == null) {
            schema = RuntimeSchema.createFrom(cls);
            if (schema != null) {
                cachedSchema.put(cls, schema);
            }
        }

        return (Schema)schema;
    }

    @Override
    public byte[] serialize(Object obj) {
        Class cls = obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(512);
        byte[] bytes;
        try {
            Schema schema = getSchema(cls);
            bytes = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            log.error("Protostuff 序列化对象异常,异常信息为: {}", e);
            throw e;
        } finally {
            buffer.clear();
        }
        return bytes;
    }

    @Override
    public <T> Object deserialize(byte[] bytes, Class<T> clazz) {
        try {
            T message = objenesis.newInstance(clazz);
            Schema<T> schema = getSchema(clazz);
            ProtostuffIOUtil.mergeFrom(bytes, message, schema);
            return message;
        } catch (Exception e) {
            log.error("Protostuff 反序列化对象异常,异常信息为: {}", e);
            throw e;
        }
    }
}

JDK

  • JDK 序列化示例代码
public class JdkKvSerializer implements KvSerializer {
    private Logger log = LoggerFactory.getLogger(JdkKvSerializer.class);

    @Override
    public byte[] serialize(Object object) throws Throwable {
        if (object == null) {
            return null;
        }
        byte[] bytes = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(object);
            oos.flush();
            bytes = bos.toByteArray();
            oos.close();
            bos.close();
        } catch (IOException e) {
            log.error("JDK 序列化对象异常,异常信息为: {}", e);
            throw e;
        }
        return bytes;
    }

    @Override
    public <T> T deserialize(byte[] bytes, Class<T> clazz) throws Throwable {
        if (bytes == null) {
            return null;
        }
        Object obj = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bis);
            obj = ois.readObject();
            ois.close();
            bis.close();
        } catch (Exception e) {
            log.error("JDK 反序列化对象异常,异常信息为: {}", e);
            throw e;
        }
        return (T) obj;
    }
}

Hessian(Hessian1)

  • 依赖
<dependency>
    <groupId>com.caucho</groupId>
    <artifactId>hessian</artifactId>
    <version>4.0.62</version>
</dependency>
  • Hessian 序列化示例代码
public class Hessian1KvSerializer implements KvSerializer {
    private Logger log = LoggerFactory.getLogger(Hessian1KvSerializer.class);

    public byte[] serialize(Object obj) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        HessianOutput ho = new HessianOutput(os);

        byte[] bytes;
        try {
            ho.writeObject(obj);
            ho.flush();
            byte[] result = os.toByteArray();
            bytes = result;
        } catch (IOException e) {
            log.error("Hessian1 序列化对象异常,异常信息为: {}", e);
            throw e;
        } finally {
            try {
                ho.close();
                os.close();
            } catch (IOException e) {
                log.error("Hessian1 序列化对象异常,异常信息为: {}", e);
                throw e;
            }
        }

        return bytes;
    }

    public <T> Object deserialize(byte[] bytes, Class<T> clazz) {
        ByteArrayInputStream is = new ByteArrayInputStream(bytes);
        HessianInput hi = new HessianInput(is);

        Object obj;
        try {
            Object result = hi.readObject();
            obj = result;
        } catch (IOException e) {
            log.error("Hessian1 反序列化对象异常,异常信息为: {}", e);
            throw e;
        } finally {
            try {
                hi.close();
                is.close();
            } catch (Exception e) {
                log.error("Hessian1 反序列化对象异常,异常信息为: {}", e);
                throw e;
            }
        }

        return obj;
    }
}

Hessian(Hessian2)

  • 依赖
<dependency>
    <groupId>com.caucho</groupId>
    <artifactId>hessian</artifactId>
    <version>4.0.62</version>
</dependency>
  • Hessian 序列化示例代码
public class Hessian2KvSerializer implements KvSerializer {
    private Logger log = LoggerFactory.getLogger(Hessian2KvSerializer.class);

    public byte[] serialize(Object obj) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        Hessian2Output ho = new Hessian2Output(os);

        byte[] bytes;
        try {
            ho.writeObject(obj);
            ho.flush();
            byte[] result = os.toByteArray();
            bytes = result;
        } catch (Exception e) {
            log.error("Hessian2 序列化对象异常,异常信息为: {}", e);
            throw e;
        } finally {
            try {
                ho.close();
                os.close();
            } catch (IOException e) {
                log.error("Hessian2 序列化对象异常,异常信息为: {}", e);
                throw e;
            }
        }

        return bytes;
    }

    public <T> Object deserialize(byte[] bytes, Class<T> clazz) {
        ByteArrayInputStream is = new ByteArrayInputStream(bytes);
        Hessian2Input hi = new Hessian2Input(is);

        Object obj;
        try {
            Object result = hi.readObject();
            obj = result;
        } catch (IOException e) {
            log.error("Hessian2 反序列化对象异常,异常信息为: {}", e);
            throw e;
        } finally {
            try {
                hi.close();
                is.close();
            } catch (IOException e) {
                log.error("Hessian2 反序列化对象异常,异常信息为: {}", e);
                throw e;
            }
        }

        return obj;
    }
}

Jackson

  • 依赖
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.9.1</version>
</dependency>
  • Jackson 序列化示例代码
public class JacksonKvSerializer implements KvSerializer {
    private Logger log = LoggerFactory.getLogger(Hessian2KvSerializer.class);
    private static final ObjectMapper objectMapper = new ObjectMapper();

    public JacksonSerializer() {
    }

    public <T> byte[] serialize(T obj) {
        try {
            return objectMapper.writeValueAsBytes(obj);
        } catch (Exception e) {
            log.error("Jackson 序列化对象异常,异常信息为: {}", e);
            throw e;
        }
    }

    public <T> Object deserialize(byte[] bytes, Class<T> clazz) {
        try {
            return objectMapper.readValue(bytes, clazz);
        } catch (Exception e) {
            log.error("Jackson 反序列化对象异常,异常信息为: {}", e);
            throw e;
        }
    }
}