Protobuffer和json 简单对比(java语言)

一,应用场景

广告行业多平台之间, 会用http协议以post的方式传递很多上下文信息. 大平台(兼有pc和mobile),比如阿里/百度/腾讯目前都以protobuffer作为数据交换格式. 我所知道的纯移动流量平台mongo和小米,采用的json. 不能明白他们做选择时的考量是什么, 所以对两者做了简单的对比测试(当然,不一定能解决自己的困惑).

二, 案例设计

1, 测试proto协议

message Teacher{
    required int32 id = 1;
    required string name = 2;
    //学生列表
    repeated  Student students = 3;
}
message Student {  
    required int32 id = 1;  
    required string name = 2;  
    optional string desc = 3;  
}
2, 最近基本的java bean 类Teacher和Student, 属性和proto里的数据完全一致.

3, 针对以上数据格式, 做三组测试:

    a) 混合数据: 如上图, 属性中既有数字, 又有字符串.

    b) 纯数字测试: 把上图中的字符属性注释掉. 普通java bean也做对应处理.

    c) 纯字符测试: 把上图中的数字属性注释掉.

三, 测试编码

1, proto数据格式的序列化,反序列化以及序列化后文件大小. 代码为"混合数据"的代码. 三组测试只有属性不一样, 做相应的注释就好.

	@Test
	public void protoSerialization() {
		User.Teacher.Builder teacher = User.Teacher.newBuilder();
		teacher.setId(100);
		teacher.setName("刘备");
		User.Student.Builder student;
		for(int i=0;i listB = new ArrayList<>();
		long start = System.currentTimeMillis();
		for(User.Student.Builder s:teacher.getStudentsBuilderList()){
			listB.add(s.build().toByteArray());
		}
		System.out.println("protobuffer序列化耗时:"+(System.currentTimeMillis() - start));
		start = System.currentTimeMillis();
		for(byte[] b:listB){
			try {
				User.Student.parseFrom(b);
			} catch (InvalidProtocolBufferException e) {
				e.printStackTrace();
			}
		}
		System.out.println("protobuffer反序列化耗时:"+(System.currentTimeMillis() - start));				
		try {
			FileOutputStream fileOutputStream = new FileOutputStream("E:\\temp\\teacher-proto.txt");
			fileOutputStream.write(teacher.build().toByteArray());
//			teacher.build().writeTo(fileOutputStream);
			fileOutputStream.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
2. 阿里巴巴的fastjson工具包操作json的测试代码. 由于使用静态方法操作, 所以在junit的setUp()方法中预先加载了JSON类.

	@Test
	public void fastJsonSerialization(){
		Teacher teacher = new Teacher();
		teacher.setId(100);
		teacher.setName("刘备");
		Student student = null;
		List students = new ArrayList<>();
		for(int i=0;i jsonStr = new ArrayList<>();
		long start = System.currentTimeMillis();
		for(Student s:students){
			jsonStr.add(JSON.toJSONString(s));

		}
		System.out.println("fastJson序列化耗时:"+(System.currentTimeMillis() - start));
		start = System.currentTimeMillis();
		for(String str:jsonStr){
			JSON.parseObject(str, Student.class);
		}
		System.out.println("fastJson反序列化耗时:"+(System.currentTimeMillis() - start));		
		teacher.setStudents(students);
		String result = JSON.toJSONString(teacher);
		try {
			FileWriter file = new FileWriter("E:\\temp\\teacher-fast-json.txt");
			file.write(result);
			file.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
3. google 的json工具包操作json的测试代码

	@Test
	public void gJsonSerialization(){
		Teacher teacher = new Teacher();
		teacher.setId(100);
		teacher.setName("刘备");
		Student student = null;
		List students = new ArrayList<>();
		for(int i=0;i jsonStr = new ArrayList<>();
		Gson g = new Gson();
		long start = System.currentTimeMillis();
		for(Student s:students){
			jsonStr.add(g.toJson(s));
		}
		System.out.println("gson序列化耗时:"+(System.currentTimeMillis() - start));
		start = System.currentTimeMillis();
		for(String str:jsonStr){
			g.fromJson(str, Student.class);
		}
		System.out.println("gson反序列化耗时:"+(System.currentTimeMillis() - start));
		teacher.setStudents(students);
		String result = new Gson().toJson(teacher);
		try {
			FileWriter file = new FileWriter("E:\\temp\\teacher-gson.txt");
			file.write(result);
			file.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}


三,测试结果

1, 数字字符混合测试

混合测试结果
数据格式 序列化耗时(ms) 反序列化耗时(ms) 文件大小
protobuffer 61 20 440k
fastjson工具处理json 64 60 781k
gson处理json 112 115 781k

2, 纯数字测试

纯数字测试
数据格式 序列化耗时(ms) 反序列化耗时(ms) 文件大小
protobuffer 30 24 74k
fastjson工具处理json 43 48 180k
gson处理json 63 106 180k

3, 纯字符

纯字符测试
数据格式 序列化耗时(ms) 反序列化耗时(ms) 文件大小
protobuffer 57 19 396k
fastjson工具处理json 45 62 630
gson处理json 96 95 630

四, 总结

1. 测试代码每次运行虽有细微差异, 但总的来说protobuff先性能和序列化后文件大小方面表现还不错, 尤其是文件大小, 比较适合我的应用场景. 跨网传输数据就能节约不少成本. 当然, json也有自己的优势, 不是protobuff能完全取代的, 具体得看应用场景.

2. pb对数字类型的编码压缩很大, 在做proto协议涉及时可以适当考虑这一点. 当然会牺牲一些cpu时间.

3. 听说"pb对嵌套数组类型会消耗大量内存", 未经测试验证.

4. 以上测试很简单, 仅供参考.

五, 参考资料

1. Protobuffer和json深度对比.

2. 序列化的几种方式



 

你可能感兴趣的:(webservice,java)