关于protobufs的困惑

时间:2018-04-25 02:35:24

标签: java serialization protocol-buffers

我有一台服务器经常调用微服务(实际上是用python编写的AWS Lambda函数),原始JSON有效负载和响应大小为5-10 MB。这些有效负载被gzip压缩,使其总大小低于lambda的6MB限制。

当前有效负载序列化为JSON,gzip并发送给Lambda。然后对响应进行gunzip压缩,并将JSON反序列化为Java POJO。

通过分析,我们发现这个序列化,gzipping,gunzipping和deserializaing的过程是我们服务器CPU使用率的大部分。寻找使序列化更有效的方法使我成为了protobufs。

将我们的序列化从JSON切换到protobufs肯定会使我们的(de)序列化更高效,并且可能还有一个额外的好处,即不需要gzip来获得6MB以下的有效负载(网络延迟在这里不是一个问题)。 / p>

有问题的POJO看起来像这样(Java):

public class InputObject {

    ... 5-10 metadata fields containing primitives or other simple objects ...

    List<Slots> slots; // usually around 2000
}

public class Slot {
    public double field1;     //20ish fields with a single double
    public double[] field2;   //10ish double arrays of length 5
    public double[][] field3; //1 2x2 matrix of doubles
}

使用JSON gson.toJson(inputObj)这非常容易,你很高兴。 Protobufs看起来像一个完全不同的野兽,要求你使用生成的类,并使用像

这样的东西丢弃你的代码
Blah blah = Blah.newBuilder()
    .setFoo(f)
    .setBar(b)
    .build()

此外,这会导致一个不可变对象,需要更多的环跳更新。将所有与传输层相关的代码放入业务逻辑中似乎是一件坏事。

我看到有些人建议在生成的类周围编写包装器,这样所有的protobuffy-ness都不会泄漏到代码库的其余部分,这似乎是一个好主意。但后来我不确定如何一次性序列化顶级InputObject

也许protobufs不适合这里的工作,但是当你开始考虑提高效率时,它似乎是服务间通信的首选解决方案。

我错过了什么吗?

1 个答案:

答案 0 :(得分:0)

使用您的原型,您可以随时进行序列化。你在java tuto online中有一个例子: https://developers.google.com/protocol-buffers/docs/javatutorial

AddressBook.Builder addressBook = AddressBook.newBuilder();
...
FileOutputStream output = new FileOutputStream(args[0]);
addressBook.build().writeTo(output);

您可能想要做的是将您的proto序列化为ByteArray,然后在Base64中对其进行编码以通过您的包装器进行编码:

String yourPayload = BaseEncoding.base64().encode(blah.toByteArray())

您有额外的库可以帮助您将现有的JSON转换为原型,例如JsonFormat: https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/util/JsonFormat

用法也很简单: 序列化为Json:

JsonFormat.printToString(yourProto)

从原型构建:

JsonFormat.merge(yourJSONstring, yourPrototBuilder);

无需遍历对象的每个元素。

让我知道这是否能回答你的问题!