1. 程式人生 > >Protobuffer和json 簡單對比(java語言)

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<size;i++){
			student = User.Student.newBuilder();
			student.setId(101+i);
			student.setName("趙雲");
			student.setDesc("五虎將之一");
			teacher.addStudents(student.build());
		}
		List<byte[]> 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<Student> students = new ArrayList<>();
		for(int i=0;i<size;i++){
			student = new Student();
			student.setId(101+i);
			student.setName("趙雲");
			student.setDesc("五虎將之一");
			students.add(student);
		}
		List<String> 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的測試程式碼
<span style="white-space:pre">	</span>@Test
	public void gJsonSerialization(){
		Teacher teacher = new Teacher();
		teacher.setId(100);
		teacher.setName("劉備");
		Student student = null;
		List<Student> students = new ArrayList<>();
		for(int i=0;i<size;i++){
			student = new Student();
			student.setId(101+i);
			student.setName("趙雲");
			student.setDesc("五虎將之一");
			students.add(student);
		}
		List<String> 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. 以上測試很簡單, 僅供參考.

五, 參考資料