我有一台服务器经常调用微服务(实际上是用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不适合这里的工作,但是当你开始考虑提高效率时,它似乎是服务间通信的首选解决方案。
我错过了什么吗?
答案 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);
无需遍历对象的每个元素。
让我知道这是否能回答你的问题!