Полезная нагрузка protobuf больше, чем JSON?

У меня есть объект, представляющий собой список объектов 'Level', и я тестирую передачу их с помощью Spring Boot Rest Controller двумя способами:

  1. с 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;
    }
    
  2. В формате 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());

Главное здесь - сделать ваш тест воспроизводимым, чтобы другие люди могли его повторить.

Другие вопросы по тегам