Полезная нагрузка protobuf больше, чем JSON?
У меня есть объект, представляющий собой список объектов 'Level', и я тестирую передачу их с помощью Spring Boot Rest Controller двумя способами:
с JSON в Rest Controller я использую что-то вроде:
@RequestMapping(value = "/api/v1/layers/{layername}", method = RequestMethod.GET, produces = "application/json") public @ResponseBody List<Level> query(@PathVariable String layername, @RequestParam("northEastLat") Float northEastLat, @RequestParam("northEastLng") Float northEastLng, @RequestParam("northWestLat") Float northWestLat, @RequestParam("northWestLng") Float northWestLng, @RequestParam("southEastLat") Float southEastLat, @RequestParam("southEastLng") Float southEastLng, @RequestParam("southWestLat") Float southWestLat, @RequestParam("southWestLng") Float southWestLng ) { List<Level> poligons=levelService.obtainLevels(layername,southWestLng,southWestLat,northWestLng,northWestLat,northEastLng,northEastLat,southEastLng,southEastLat); int i=1; for (Level p : poligons) { System.out.println("poligon" + i++ + " is:" + p.toString()); } return poligons; }
В формате Protostuff Protobuf я использую что-то вроде:
@RequestMapping(value = "/api/v1/layers/{layername}", method = RequestMethod.GET,produces = "text/plain") public String query(@PathVariable String layername, @RequestParam("northEastLat") Float northEastLat, @RequestParam("northEastLng") Float northEastLng, @RequestParam("northWestLat") Float northWestLat, @RequestParam("northWestLng") Float northWestLng, @RequestParam("southEastLat") Float southEastLat, @RequestParam("southEastLng") Float southEastLng, @RequestParam("southWestLat") Float southWestLat, @RequestParam("southWestLng") Float southWestLng ) { List<Level> poligons=levelService.obtainLevels(layername,southWestLng,southWestLat,northWestLng,northWestLat,northEastLng,northEastLat,southEastLng,southEastLat); LevelList list = new LevelList(poligons); byte[] bytes; int i=1; for (Level p : poligons) { System.out.println("poligon" + i++ + " is:" + p.toString()); } Schema<LevelList> schema = RuntimeSchema.getSchema(LevelList.class); LinkedBuffer buffer = LinkedBuffer.allocate(); try { bytes = ProtostuffIOUtil.toByteArray(list, schema, buffer); } finally { buffer.clear(); } return new String(bytes); }
Формат объекта уровня: [{"wkb_geometry":"{" тип ":" Полигон "," координаты ": [[[[24.446822,45.34997],[24.706508,45.352485]]]}","id":199,"уровень":"3","тип": нулевая}
Объект уровня:
@Entity(name = "Level")
@Table(name="Level2G")
@SecondaryTables({
@SecondaryTable(name="Level3G"),
@SecondaryTable(name="Level4G")
})
public class Level implements Serializable {
private static final long serialVersionUID = 1L;
// @Column(name = "wkb_geometry",columnDefinition="Geometry")
//@Type(type = "org.hibernate.spatial.GeometryType")
@Column(name="wkb_geometry")
private /*Geometry */ String wkb_geometry;
@Id
@Column(name="id")
private Integer id;
@Column(name="level")
private String level;
@Transient
private String type;
public Level() {
}
public Level(String wkb_geometry, Integer id, String level) {
this.wkb_geometry = wkb_geometry;
this.id = id;
this.level = level;
this.type = "Feature";
}
public Level(String wkb_geometry, Integer id, String level, String type) {
this.wkb_geometry = wkb_geometry;
this.id = id;
this.level = level;
this.type = type;
}
public Object getWkb_geometry() {
return wkb_geometry;
}
public void setWkb_geometry(String wkb_geometry) {
this.wkb_geometry = wkb_geometry;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "Level{" +
"wkb_geometry=" + wkb_geometry +
", id=" + id +
", level='" + level + '\'' +
", type='" + type + '\'' +
'}';
}
}
Объект LevelList - это просто список объектов уровня
Проблема в том, что с Protostuff я получаю большую полезную нагрузку (26 КБ) по сравнению с JSON (3,7 КБ). Зачем?
Также для второго варианта я также попытался установить "application/octet-stream", чтобы он возвращал байты напрямую, но результат все тот же. Также я сравнил скорость для JSON и protobuf; Protobuf имеет лучшую производительность даже с большей полезной нагрузкой. Есть идеи почему?
2 ответа
Protostuff и Protobuf - это не одно и то же. Protostuff - это библиотека-оболочка, которая может использовать множество различных форматов сериализации. Он также поддерживает генерацию схемы во время выполнения, которую вы используете. Эта схема выполнения требует отправки дополнительных метаданных вместе с сообщением, чтобы сообщить получателю о схеме сообщения. Я предполагаю, что большое сообщение, которое вы видите, в основном из этих данных схемы времени выполнения.
В стандартном Protobuf схема не отправляется вместе с сообщением, поскольку предполагается, что отправитель и получатель уже согласовали схему, предоставленную .proto
файл скомпилирован в обе программы. Если вы используете Protobuf со стандартным .proto
файл, вы обнаружите, что сообщения, которые он производит, гораздо меньше, чем JSON.
У вас есть хотя бы одна проблема в вашем тесте.
Это преобразование из байтового массива в String недопустимо:
bytes = ProtostuffIOUtil.toByteArray(list, schema, buffer);
return new String(bytes);
Этот конструктор String будет пытаться проанализировать байтовый массив как строку UTF-8 (наиболее вероятно; зависит от ваших настроек локали), но данные по определению не являются допустимой строкой UTF-8.
Если вы хотите сделать лучшее сравнение размеров, вы должны написать тест в следующей форме:
LevelList source = testData();
byte[] jsonData = generateJson(source);
byte[] protobufData = generateProtobuf(source);
System.out.println("JSON=" + jsonData.size() + " Protobuf=" + protobufData.size());
Главное здесь - сделать ваш тест воспроизводимым, чтобы другие люди могли его повторить.