跨语言序列化-protobuf/thrift/avro性能测试

时间:2022-10-15 09:37:33

1. 编写Schema


1.1 student.proto

[java]  view plain  copy
  1. package protobuf;   
  2. option java_package = "com.topsec.trd";   
  3. option java_outer_classname = "StudentProto";   
  4. message Student  {   
  5.   required string name = 1;  
  6.   required int32 age = 2;  
  7.   required int32 sex = 3;  
  8.   optional string alias = 4;  
  9.   repeated string interest=5;  
  10.     
  11. }  

1.2 studnet.avsc

[java]  view plain  copy
  1. namespace java com.topsec.trd  
  2. struct StudentThrift {    
  3.   1: string name,  
  4.   2: i32 age,  
  5.   3: i32 sex,  
  6.   4: optional string aliass,  
  7.   5: list<string> interest  
  8. }  

1.3 studnet.avsc

[java]  view plain  copy
  1. {"namespace""com.topsec.trd",  
  2.  "type""record",  
  3.  "name""StudentAvro",  
  4.  "fields": [  
  5.      {"name""name""type""string"},  
  6.      {"name""alias",  "type": ["string""null"]},  
  7.      {"name""age",  "type": ["int""null"]},  
  8.      {"name""sex""type": ["string""null"]},  
  9.      {"name""interet""type": {"type""array""items""string"}}  
  10.  ]  
  11. }  

2. 生成bean

生成protobuf bean

protoc --java_out=. student.proto

生成thrift bean

thrift-0.10.0.exe -gen java student.thrift

生成avro bean

java -jar lib/avro-tools-1.7.7.jar compile schema src/main/resource/student.avsc src/main/java

3. 编写测试代码

[java]  view plain  copy
  1. public class ProtoBuf {  
  2.     private final static int TIMES = 10000000;  
  3.     public static void main(String[] args) throws IOException {  
  4.          long start = System.currentTimeMillis();  
  5.          for(int i = 0; i < TIMES; i++) {  
  6.              deserialize();  
  7.          }  
  8.          long end = System.currentTimeMillis();  
  9.          System.out.println("ProtoBuf total time \t" + (end -start));  
  10.     }  
  11.       
  12.     public static byte [] serializeAsBytes() {  
  13.         return makeStudent().build().toByteArray();   
  14.     }  
  15.       
  16.     public static InputStream serializeAsStream() throws IOException {  
  17.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  18.         makeStudent().build().writeDelimitedTo(baos);  
  19.         return new ByteArrayInputStream(baos.toByteArray());  
  20.     }  
  21.       
  22.     private static Builder makeStudent() {  
  23.         StudentProto.Student.Builder student = StudentProto.Student.newBuilder();  
  24.         student.setName("小明");  
  25.         student.setSex(0);  
  26.         student.setAge(18);  
  27.         List<String> interests = new ArrayList<String>();  
  28.         interests.add("吃饭");  
  29.         interests.add("睡觉");  
  30.         interests.add("打豆豆");  
  31.         student.addAllInterest(interests);  
  32.         return student;  
  33.     }  
  34.       
  35.     public static void deserialize() throws IOException {  
  36.         byte [] bytes = serializeAsBytes();  
  37.         StudentProto.Student student = StudentProto.Student.parseFrom(bytes);  
  38.         System.out.println(student.getName());  
  39.     }  
  40. }  


[java]  view plain  copy
  1. public class Thrift {  
  2.     private final static int TIMES = 10000000;  
  3.     private final static TSerializer SERIALIZER = new TSerializer(new TBinaryProtocol.Factory());  
  4.     private final static TDeserializer DESERIALIZER = new TDeserializer(new TBinaryProtocol.Factory());  
  5.       
  6.     public static void main(String[] args) throws TException {  
  7.          long start = System.currentTimeMillis();  
  8.          for(int i = 0; i < TIMES; i++) {  
  9.              deserialize();  
  10.          }  
  11.          long end = System.currentTimeMillis();  
  12.          System.out.println("Thrift total time \t" + (end -start));  
  13.     }  
  14.       
  15.     public static byte [] serialize() throws TException {  
  16.         StudentThrift stu = new StudentThrift();  
  17.         stu.setName("小明");  
  18.         stu.setSex(0);  
  19.         stu.setAge(18);  
  20.         List<String> interests = new ArrayList<String>();  
  21.         interests.add("吃饭");  
  22.         interests.add("睡觉");  
  23.         interests.add("打豆豆");  
  24.         stu.setInterest(interests);  
  25.           
  26.         return SERIALIZER.serialize(stu);  
  27.     }  
  28.       
  29.     public static void deserialize() throws TException {  
  30.         byte [] bytes = serialize();  
  31.         StudentThrift stu = new StudentThrift();  
  32.         DESERIALIZER.deserialize(stu, bytes);  
  33. //      System.out.println(stu);  
  34.     }  
  35.       
  36. }  


[java]  view plain  copy
  1. public class AVRO {  
  2.     private final static int TIMES = 10000000;  
  3.      public static void main(String[] args) throws IOException {  
  4.          long start = System.currentTimeMillis();  
  5.          deserializeAsBytes();  
  6.          long end = System.currentTimeMillis();  
  7.          System.out.println("Avro total time \t" + (end -start));  
  8.      }  
  9.        
  10.      public static byte [] serializeAsBytes() throws IOException {  
  11.          StudentAvro student = new StudentAvro();  
  12.          student.setName("小明");  
  13.          student.setSex("女");  
  14.          student.setAge(18);  
  15.          List<CharSequence> interests = new ArrayList<CharSequence>();  
  16.          interests.add("吃饭");  
  17.          interests.add("睡觉");  
  18.          interests.add("打豆豆");  
  19.          student.setInteret(interests);  
  20.          ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  21.          DatumWriter<StudentAvro>  userDatumWriter = new  SpecificDatumWriter<StudentAvro>(StudentAvro.class);  
  22.          DataFileWriter<StudentAvro>  dataFileWriter = new  DataFileWriter<StudentAvro>(userDatumWriter);  
  23.          dataFileWriter.create(student.getSchema(), baos);  
  24.          dataFileWriter.append(student);  
  25.          dataFileWriter.close();  
  26.          return baos.toByteArray();  
  27.      }  
  28.        
  29.      public static byte [] serializeAsBytes(int times) throws IOException {  
  30.          ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  31.          DatumWriter<StudentAvro>  userDatumWriter = new  SpecificDatumWriter<StudentAvro>(StudentAvro.class);  
  32.          DataFileWriter<StudentAvro>  dataFileWriter = new  DataFileWriter<StudentAvro>(userDatumWriter);  
  33.          dataFileWriter.create(new StudentAvro().getSchema(), baos);  
  34.          for(int i = 0; i < times; i++) {  
  35.              StudentAvro student = new StudentAvro();  
  36.              student.setName("小明");  
  37.              student.setSex("女");  
  38.              student.setAge(18);  
  39.              List<CharSequence> interests = new ArrayList<CharSequence>();  
  40.              interests.add("吃饭");  
  41.              interests.add("睡觉");  
  42.              interests.add("打豆豆");  
  43.              student.setInteret(interests);  
  44.               
  45.              dataFileWriter.append(student);  
  46.          }  
  47.          dataFileWriter.close();  
  48.          return baos.toByteArray();  
  49.      }  
  50.        
  51.      public static void deserializeAsBytes() throws IOException {  
  52.          SeekableByteArrayInput sbai = new SeekableByteArrayInput(serializeAsBytes(TIMES));  
  53.          DatumReader<StudentAvro> datumReader = new SpecificDatumReader<StudentAvro>(StudentAvro.class);  
  54.          DataFileReader<StudentAvro> dataFileReader = new DataFileReader<StudentAvro>(sbai, datumReader);  
  55.          StudentAvro user = null;  
  56.          while (dataFileReader.hasNext()) {  
  57.              user = dataFileReader.next(user);  
  58.              System.out.println(user.getName());  
  59.          }   
  60.          dataFileReader.close();  
  61.      }  
  62.        
  63. }  

4. 测试结果

private final static int TIMES = 100000;
ProtoBuf total time  282
Thrift total time  229
Avro total time  694

private final static int TIMES = 1000000;
ProtoBuf total time  988
Thrift total time  1248
Avro total time 2079

private final static int TIMES = 10000000;
ProtoBuf total time  7368
Thrift total time  10675
Avro total time  15025

4.1 小结

项/技术 avro                   thrift                 protobuf       
速度 中等
序列化到1个stream 是            否                否   


5.工程下载地址

(包含测试代码及三种schema和生成bean的工具等)


6.protobuf分析


6.1 protobuf特点

(a)占用空间小
一条消息数据,用protobuf序列化后的大小是json的10分之一,xml格式的20分之一,是二进制序列化的10分之一(极端情况下,会大于等于直接序列化),总体看来ProtoBuf的优势还是很明显的。
(b)解析速度快
解析速度快,主要归功于protobuf对message 没有动态解析,没有了动态解析的处理序列化速度自然快了。就比如xml ,获取文件之后,还需要解析标签、节点、字段,每一个都需要遍历,而protobuf不需要,直接将field装入流。
(c)兼容性好
fieldNumber 为每个field定义一个编号,其一保证不重复,其二保证其在流中的位置。如若当前数据流中有某个字段,而解析方没有相关的解析代码,解析放会直接skip 吊这个field,而且读数据的position也会后移,保证后续读取不出问题。
[java]  view plain  copy
  1. private StudentProto.Student request(int age) {  
  2.         StudentProto.Student.Builder builder = StudentProto.Student.newBuilder();  
  3.         builder.setName("小明");  
  4.         builder.setSex(0);  
  5.         builder.setAge(age);  
  6.         return builder.build();  
  7.     }  
protobuf计算tag源码
[java]  view plain  copy
  1. static final int TAG_TYPE_BITS = 3;  
  2. /** Makes a tag value given a field number and wire type. */  
  3.  static int makeTag(final int fieldNumber, final int wireType) {  
  4.    return (fieldNumber << TAG_TYPE_BITS) | wireType;  
  5.  }  
数字类型存储:tag+value,
字符串存储    :leg+value,leg是字符串的长度

分析字节流:
跨语言序列化-protobuf/thrift/avro性能测试