前提条件
- 统一接口
- 多用户、多Protobuf描述文件
- 实时解析
方案
protobuf 文件示例:
//指定protobuf语法版本
syntax = "proto3";
//包名
option java_package = "com.test.protobuf.demo.protobufBean";
//源文件类名
option java_outer_classname = "ProtobufPerson";
// class Person
message Person {
string name = 1;
int32 id = 2;
string email = 3;
Dog dog = 4;
}
message Dog {
string dogName = 4;
int32 id = 5;
string email = 6;
}
一、利用描述文件
利用 protobuf 文件,生成 descriptor 文件
protoc Test.proto --descriptor_set_out=Test.description
java 代码:
ProtobufPerson.Person.Builder personBuilder = ProtobufPerson.Person.newBuilder();
personBuilder.setId(10);
personBuilder.setName("Tom");
personBuilder.setEmail("123@123.com");
personBuilder.setDog(ProtobufPerson.Dog.newBuilder().setDogName("456").setId(15).setEmail("456@456").build());
ProtobufPerson.Person reqPerson = personBuilder.build();
System.out.println(reqPerson.toString());
byte[] dataByte = personBuilder.build().toByteArray();
System.out.println(new String(dataByte));
// 原路解析
ProtobufPerson.Person respPerson = ProtobufPerson.Person.parseFrom(dataByte);
System.out.println(respPerson.toString());
Descriptors.Descriptor pbDescriptor = null;
DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet
.parseFrom(new FileInputStream("/Users/xxx/protobuf-demo/src/proto/Test.description"));
System.out.println(descriptorSet.getFileList().size());
for (DescriptorProtos.FileDescriptorProto fdp : descriptorSet.getFileList()) {
Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fdp, new Descriptors.FileDescriptor[] {});
for (Descriptors.Descriptor descriptor : fileDescriptor.getMessageTypes()) {
// 需要 知道描述文件里,你需要用那个; 接口传或者 protobuf文件只有一个 message,不然就无法对应
if (descriptor.getName().equals("Person")) {
System.out.println("Movie descriptor found");
pbDescriptor = descriptor;
break;
}
}
}
if (pbDescriptor == null) {
System.out.println("No matched descriptor");
return;
}
DynamicMessage.Builder pbBuilder = DynamicMessage.newBuilder(pbDescriptor);
Message pbMessage = pbBuilder.mergeFrom(dataByte).build();
缺点: 需要加载 descriptor 文件,并且需要接口传描述文件里的实体 名称
二、利用统一的protobuf消息体
方案一的麻烦点在于,服务端需要自己有一份protobuf的 descriptor 文件。
消息体protobuf文件示例:
syntax = "proto3";
option java_package="com.test.protobuf.demo.protobufBean";
import "google/protobuf/descriptor.proto";
message SelfDescribingMessage {
// Set of FileDescriptorProtos which describe the type and its dependencies.
google.protobuf.FileDescriptorSet descriptor_set = 1;
string msg_name=2;
// The message and its type, encoded as an Any message.
bytes message = 3;
}
java 代码:
ProtobufPerson.Person.Builder personBuilder = ProtobufPerson.Person.newBuilder();
personBuilder.setId(10);
personBuilder.setName("Tom");
personBuilder.setEmail("123@123.com");
personBuilder.setDog(ProtobufPerson.Dog.newBuilder().setDogName("456").setId(15).setEmail("456@456").build());
ProtobufPerson.Person reqPerson = personBuilder.build();
DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet
.parseFrom(new FileInputStream("/Users/xxx/tctWorkspace/protobuf-demo/src/proto/Test.description"));
Selfmd.SelfDescribingMessage.Builder selfmdBuilder = Selfmd.SelfDescribingMessage.newBuilder();
selfmdBuilder.setDescriptorSet(descriptorSet);
selfmdBuilder.setMsgName(ProtobufPerson.Person.getDescriptor().getFullName());
selfmdBuilder.setMessage(reqPerson.toByteString());
Selfmd.SelfDescribingMessage build = selfmdBuilder.build();
byte[] byteArray = build.toByteArray();
// 消费者
long currentTime = System.currentTimeMillis();
Selfmd.SelfDescribingMessage parseFrom = Selfmd.SelfDescribingMessage.parseFrom(byteArray);
DescriptorProtos.FileDescriptorSet descriptorSet2 = parseFrom.getDescriptorSet();
ByteString message = parseFrom.getMessage();
String msgName = parseFrom.getMsgName();
Descriptors.Descriptor pbDescritpor = null;
for (DescriptorProtos.FileDescriptorProto fdp : descriptorSet2.getFileList()) {
Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fdp,
new Descriptors.FileDescriptor[] {});
for (Descriptors.Descriptor descriptor : fileDescriptor.getMessageTypes()) {
if (descriptor.getName().equals(msgName)) {
System.out.println("descriptor found");
pbDescritpor = descriptor;
break;
}
}
}
if (pbDescritpor == null) {
System.out.println("No matched descriptor");
return;
}
DynamicMessage dmsg = DynamicMessage.parseFrom(pbDescritpor, message);
JsonFormat jsonFormat = new JsonFormat();
String dataStr = jsonFormat.printToString(dmsg);
System.out.println(dataStr);
关于解析成 protobuf的message对象后,可以用google的转换 jar包,把它转换成json,完全不需要别的操作。
<!-- https://mvnrepository.com/artifact/com.googlecode.protobuf-java-format/protobuf-java-format -->
<dependency>
<groupId>com.googlecode.protobuf-java-format</groupId>
<artifactId>protobuf-java-format</artifactId>
<version>1.4</version>
</dependency>
方案二,比较适合做一些平台型的。使用方案一的话,不如每次添加新的protobuf的java文件,然后重启。
0