Protocol Buffers 3

由于接口同时支持application/x-protobufapplication/json两种content-type,所以protobuf-java提供的JSON序列化方案性能也是我们比较关注的。

最近注意到Protocol Buffers 3(下称proto3)中对JSON的处理调整为利用Gson实现,尝试升级到proto3,这里简单总结下中间遇到的一些问题。

proto3语法规则

  • 描述文件头部增加语法声明syntax = "proto3" ,这样protoc会正确编译生成代码。

  • 移除required描述关键字,字段默认是optional

  • 移除default选项,proto2可以使用default为某一字段指定默认值,但proto3字段的默认值只能根据字段类型由系统决定。

  • enum类型第一个字段的字段编号tags必须为0。

  • 添加新的字段选项json_name。默认在proto3的JSON格式中字段名将被转换为lowerCamelCase,这个选项被用于自定义字段名。

JsonFormat注意点

反序列化
1
2
3
4
5
6
7
8
HttpInputMessage inputMessage;
InputStreamReader reader;
if (MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
    reader = new InputStreamReader(inputMessage.getBody(), charset);
    JsonFormat.parser()
            .ignoringUnknownFields()            //忽略未知字段,避免反序列化抛异常
            .merge(reader, ex);
}
序列化
1
2
3
4
5
6
7
8
9
10
11
HttpOutputMessage outputMessage;
OutputStreamWriter outputStreamWriter;
if (MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
    outputStreamWriter = new OutputStreamWriter(outputMessage.getBody(), charset);
    JsonFormat.printer()
            .includingDefaultValueFields()      //默认值也要序列化,兼容接口定义,int类型默认值0也要输出
            .preservingProtoFieldNames()        //保留原有字段名,兼容接口定义
            .omittingInsignificantWhitespace()  //忽略空格和换行
            .appendTo(message, outputStreamWriter);
    outputStreamWriter.flush();
}

实践

  • 接口升级不要修改已有字段的tags,通常采用追加模式,reserved 关键字用来声明保留字段。

  • 可以合理利用map类型。

  • 了解gRPC