2020年對(duì)我來說是忙碌的一年涛癌,2021年也毫無懸念的需要繼續(xù)忙碌。因?yàn)楣緩?020年從零開始構(gòu)建云原生平臺(tái)斗这,從原有隊(duì)伍中抽調(diào)了一部分人再加上新招聘的組成了目前的團(tuán)隊(duì)承接這項(xiàng)寄托或者影響著公司未來的產(chǎn)品陆错。既然是云原生,微服務(wù)是必不可少的。緩存曲尸、隊(duì)列自然也是必備套件紊册。在redis存儲(chǔ)及隊(duì)列消息發(fā)送時(shí)都面臨序列化框架的選擇比肄。
序列化框架對(duì)于上層應(yīng)用來說是透明的。應(yīng)用的開發(fā)同學(xué)無需關(guān)注或者甚至有些都?jí)焊恢佬蛄谢拇嬖谀叶福鳛榧夹g(shù)中臺(tái)的同學(xué)芳绩,我們還是會(huì)面臨序列化框架選擇的問題。也許你會(huì)嘲笑我撞反,“為什么要糾結(jié)那么一丁點(diǎn)的性能差異妥色,難道Json序列化的可讀性不香嗎?”遏片。但我覺得凡事都應(yīng)該論個(gè)究竟嘹害,如果Json序列化那么香撮竿,為什么還會(huì)有這么多序列化框架的存在? 技術(shù)框架的性能也直接影響到所有應(yīng)用的性能笔呀,所以在選擇框架的時(shí)候除了本身框架的穩(wěn)定性幢踏、維護(hù)性、擴(kuò)展性之外许师,性能表現(xiàn)也會(huì)作為選擇評(píng)比的重要參考指標(biāo)房蝉。
Java生態(tài)的序列化框架有不少。除了Jdk序列化微渠、Jackson搭幻、被安全問題飽受爭議的FastJson之外,像Hessian敛助、Kryo粗卜、Fst、Protostuff等序列化框架也被廣泛應(yīng)用纳击。而針對(duì)這些框架本身的性能表現(xiàn)如何续扔,還是要實(shí)測一把才能有個(gè)準(zhǔn)確的對(duì)比。下面就針對(duì)Kryo焕数、Fst纱昧、Protostuff進(jìn)行下序列化、反序列化的性能測試堡赔。
先看一下要被序列化的對(duì)象识脆。我定義了User、Group對(duì)象善已,應(yīng)該也是我們在構(gòu)建應(yīng)用時(shí)設(shè)計(jì)的最簡單的對(duì)象了灼捂。
@Data
public class User implements Serializable {
????private Stringid;
? ? private String name;
? ? private Integer age;
? ? private String desc;
? ? private Group group;
}
User對(duì)象有個(gè)關(guān)聯(lián)屬性group,表示用戶所屬的分組或者部門换团。
@Data
public class Groupimplements Serializable {
private Stringid;
? ? private Stringname;
? ? private Stringdescription;
}
分別定義了三個(gè)序列化的類悉稠。
Kryo序列化類:
public class JanzKryoSerializerimplements Serializer{
????private Kryokryo;
????private volatile static Serializerserializer;
????private JanzKryoSerializer(){
????????kryo =new Kryo();
????????kryo.register(BigDecimal.class, new DefaultSerializers.BigDecimalSerializer());
????????kryo.register(BigInteger.class, new DefaultSerializers.BigIntegerSerializer());
? ? ? ? kryo.register(Double.class, new DefaultSerializers.DoubleSerializer());
????????kryo.register(Float.class, new DefaultSerializers.FloatSerializer());
????????kryo.register(Byte.class, new DefaultSerializers.ByteSerializer());
? ? ????kryo.register(Integer.class, new DefaultSerializers.IntSerializer());
????????kryo.register(Long.class, new DefaultSerializers.LongSerializer());
? ? ? ? kryo.register(String.class, new DefaultSerializers.StringSerializer());
? ? ? ? kryo.register(StringBuffer.class, new DefaultSerializers.StringBufferSerializer());
? ? ? ? kryo.register(Date.class, new DefaultSerializers.DateSerializer());
? ? ? ? // kryo.setRegistrationRequired(true);
? }
????public static SerializergetInstance(){
????????if(serializer ==null){
????????????synchronized(JanzKryoSerializer.class){
????????????????serializer =new JanzKryoSerializer();
? ? ? ? ???? }
????????}
????????return serializer;
????? }
????@Override
????public byte[]serialize(T obj) {
????????Output output =null;
????????try {
? ? ? ? ? ? ?ByteArrayOutputStream baos =new ByteArrayOutputStream();
? ? ? ? ? ? ? output =new Output(baos);
? ? ? ? ? ? ? kryo.writeClassAndObject(output, obj);
? ? ? ? ? ? ? output.flush();
? ? ? ? ? ? ? return baos.toByteArray();
? ? ? ? ? }catch(Exception e){
? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? }finally {
????????????????if(output !=null){
????????????????????output.close();
? ? ? ? ? ? ? ? }
????????}
????????return null;
???? }
????@SuppressWarnings("unchecked")
????@Override
????? public T deserialize(byte[] bits, Class clazz) {
????????????if(bits ==null || bits.length ==0){
????????????????return null;
? ? ? ????????}
????????????Input ois =null;
? ? ? ? ? ? try {
????????????????ByteArrayInputStream bais =new ByteArrayInputStream(bits);
? ? ? ? ? ? ? ? ois =new Input(bais);
? ? ? ? ????????? return (T)kryo.readClassAndObject(ois);
? ? ? ? ? ? ?}catch(Exception e){
????????????????e.printStackTrace();
? ? ? ? ? ? ? }finally {
????????????????if(ois !=null){
????????????????????ois.close();
? ? ? ? ? ? ? ? ?}
????????????}
????????????return null;
? }
Fst序列化類:
class JanzFstSerializer implements Serializer{
? ? private FSTConfiguration configuration;
? ? private volatile static Serializer serializer;
? ? private JanzFstSerializer() {
? ? ? ? configuration = FSTConfiguration.createDefaultConfiguration();
? ? }
? ? public static Serializer getInstance() {
? ? ? ? if (serializer == null) {
? ? ? ? ? ? synchronized (JanzFstSerializer.class) {
? ? ? ? ? ? ? ? serializer = new JanzFstSerializer();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return serializer;
? ? }
? ? @Override
? ? public byte[] serialize(Object obj) {
? ? ? ? return configuration.asByteArray(obj);
? ? }
? ? @Override
? ? public <T> T deserialize(byte[] bits, Class<T> clazz) {
? ? ? ? if (bits == null) {
? ? ? ? ? ? return null;
? ? ? ? }
? ? ? ? return (T)configuration.asObject(bits);
? ? }
}
Protostuff序列化類:
public class JanzStuffSerializer implements Serializer {
private volatile static Serializer serializer;
private JanzStuffSerializer() {
}
public static Serializer getInstance() {
if (serializer == null) {
synchronized (JanzStuffSerializer.class) {
serializer = new JanzStuffSerializer();
}
}
return serializer;
}
@Override
public <T> byte[] serialize(T obj) {
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) obj.getClass();
? ? ? ? LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
? ? ? ? try {
? ? ? ? ? ? Schema<T> schema = getSchema(clazz);
? ? ? ? ? ? return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? throw new IllegalStateException(e.getMessage(), e);
? ? ? ? } finally {
? ? ? ? ? ? buffer.clear();
? ? ? ? }
}
@Override
public <T> T deserialize(byte[] data, Class<T> clazz) {
try {
? ? ? ? ? ? T obj = clazz.newInstance();
? ? ? ? ? ? Schema<T> schema = getSchema(clazz);
? ? ? ? ? ? ProtostuffIOUtil.mergeFrom(data, obj, schema);
? ? ? ? ? ? return obj;
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? throw new IllegalStateException(e.getMessage(), e);
? ? ? ? }
}
private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>();
? ? private static <T> Schema<T> getSchema(Class<T> clazz) {
? ? ? ? @SuppressWarnings("unchecked")
? ? ? ? Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);
? ? ? ? if (schema == null) {
? ? ? ? ? ? schema = RuntimeSchema.getSchema(clazz);
? ? ? ? ? ? if (schema != null) {
? ? ? ? ? ? ? ? cachedSchema.put(clazz, schema);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return schema;
? ? }
}
基于定義的三種序列化類,編寫測試方法:
public static void main(String[] args) {
? ? ? ? int count = 100000;
? ? ? ? StopWatch stopWatch = new StopWatch();
? ? ? ? Group group = new Group();
? ? ? ? group.setId("12123123123NBNBNBNBNBNB");
? ? ? ? group.setName("北京致遠(yuǎn)互聯(lián)股份有限公司");
? ? ? ? group.setDescription("XXNB廠商艘包, 坐標(biāo):北京市海淀區(qū)XX中心");
? ? ? ? List<User> userList = new ArrayList<>();
? ? ? ? for (int i = 0; i < count; i++) {
? ? ? ? ? ? User user = new User();
? ? ? ? ? ? user.setId("230028340823048203840823NBNBNBNBNBNB-" + i);
? ? ? ? ? ? user.setAge(i);
? ? ? ? ? ? user.setName("我是來自火星的超人-" + i);
? ? ? ? ? ? user.setGroup(group);
? ? ? ? ? ? user.setDesc("歡迎地球人到火星參觀的猛,順便把我捎回去,我是火星人" + i);
? ? ? ? ? ? userList.add(user);
? ? ? ? }
? ? ? ? JanzKryoSerializer.getInstance();
? ? ? ? JanzFstSerializer.getInstance();
? ? ? ? JanzStuffSerializer.getInstance();
? ? ? ? stopWatch.start("kryo");
? ? ? ? int size = 0;
? ? ? ? for(User user : userList){
? ? ? ? ? ? byte[] bytes = JanzKryoSerializer.getInstance().serialize(user);
? ? ? ? ? ? size += bytes.length;
? ? ? ? ? ? JanzKryoSerializer.getInstance().deserialize(bytes, User.class);
? ? ? ? }
? ? ? ? stopWatch.stop();
? ? ? ? System.out.println("kryo size:" + size);
? ? ? ? size = 0;
? ? ? ? stopWatch.start("fst");
? ? ? ? for(User user : userList){
? ? ? ? ? ? byte[] bytes = JanzFstSerializer.getInstance().serialize(user);
? ? ? ? ? ? size += bytes.length;
? ? ? ? ? ? JanzFstSerializer.getInstance().deserialize(bytes, User.class);
? ? ? ? }
? ? ? ? stopWatch.stop();
? ? ? ? System.out.println("fst size:" + size);
? ? ? ? size = 0;
? ? ? ? stopWatch.start("stuff");
? ? ? ? for(User user : userList){
? ? ? ? ? ? byte[] bytes = JanzStuffSerializer.getInstance().serialize(user);
? ? ? ? ? ? size += bytes.length;
? ? ? ? ? ? JanzStuffSerializer.getInstance().deserialize(bytes, User.class);
? ? ? ? }
? ? ? ? stopWatch.stop();
? ? ? ? System.out.println("stuff size:" + size);
? ? ? ? System.out.println(stopWatch.prettyPrint());
? ? }
基于這樣的測試輸出結(jié)果如下:
kryo size:33058414
fst size:30800878
stuff size:27750158
StopWatch '': running time = 2388100212 ns
---------------------------------------------
ns? ? ? ? %? ? Task name
---------------------------------------------
1097995591? 046%? kryo
826917225? 035%? fst
463187396? 019%? stuff
從測試對(duì)比的結(jié)果來看想虎,protostuff的性能優(yōu)勢還是很明顯的卦尊。在沒有其他條件影響的情況下,我們會(huì)采用protostuff作為默認(rèn)的序列化器舌厨。
在當(dāng)前的Java生態(tài)中岂却,做任何事情都有很多的選擇。本著求真的態(tài)度,我們不妨跑個(gè)demo淌友,做個(gè)簡單壓測煌恢,除了熟悉框架的基本使用之外也跟具體的了解下組件的內(nèi)部實(shí)現(xiàn)原理和實(shí)際表現(xiàn)骇陈。這樣讓我們在做選擇的時(shí)候顯得不會(huì)茫然失措震庭。
實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)!