文章目录
1. 安装 Protocol Buffer 编译器
- 官网下载操作系统对应的编译器:https://github.com/protocolbuffers/protobuf/releases
- windows系统,解压后配置环境变量
- 确定环境变量是否生效
2. 定义 Protobuf 模型
在项目中创建.proto 文件
syntax = "proto3";
import "google/protobuf/timestamp.proto";
package com.rainbowred;
option java_multiple_files = false;
option java_package = "com.rainbowred.commons.entities.proto";
option java_outer_classname = "UserProto";
message User {
int64 id = 1;
string name = 2;
int32 age = 3;
string email = 4;
google.protobuf.Timestamp createAt = 5;
}
3. 生成 Protobuf Java 类
- 控制台执行命令
protoc --proto_path=D:\JavaSource\micro-services\commons-lib\commons-entities\src\main\resources\protobuf --java_out=D:\tmp\protobuf D:\JavaSource\micro-services\commons-lib\commons-entities\src\main\resources\protobuf\user.proto
- 生成的代码
4. 添加Maven依赖
<protobuf-version>3.25.1</protobuf-version>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf-version}</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>${protobuf-version}</version>
</dependency>
5. 新增JSON和Protobuf互转的工具类
package com.rainbowred.commons.tools.utils;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import com.google.protobuf.util.JsonFormat;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Method;
/**
* Protobuf工具类
*
* @author wangyitao
* @date 2024/1/4.
*/
public class ProtobufUtil {
/**
* Protobuf转JSON
*
* @param message
* @return
*/
public static String toJson(Message message) {
if (message == null) {
return "";
}
try {
return JsonFormat.printer().print(message);
} catch (InvalidProtocolBufferException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
/**
* JSON转Protobuf
*
* @param json
* @param clazz
* @param <T>
* @return
*/
public static <T extends Message> T toBean(String json, Class<T> clazz) {
if (StringUtils.isBlank(json)) {
return null;
}
try {
final Method method = clazz.getMethod("newBuilder");
final Message.Builder builder = (Message.Builder) method.invoke(null);
JsonFormat.parser().merge(json, builder);
return (T) builder.build();
} catch (Exception e) {
throw new RuntimeException("ProtobufUtils toMessage happen error, class: " + clazz + ", json: " + json, e);
}
}
}
6. 功能测试
6.1 Protobuf转JSON
@Test
public void protobufToJsonOnce() throws Exception {
Timestamp timestamp = Timestamp.newBuilder().setSeconds(1704356248L).build();
UserProto.User user = UserProto.User.newBuilder()
.setId(1234L)
.setName("Test User")
.setAge(30)
.setEmail("test@example.com")
.setCreateAt(timestamp)
.build();
String jsonStr = ProtobufUtil.toJson(user);
log.info(jsonStr);
}
执行结果:
6.2 JSON转Protobuf
@Test
public void jsonToProtobufOnce() throws Exception {
String jsonStr = "{\"age\":30,\"createAt\":\"2024-01-04T02:17:28Z\",\"email\":\"test@example.com\",\"id\":1234,\"name\":\"Test User\"}";
UserProto.User user = ProtobufUtil.toBean(jsonStr, UserProto.User.class);
log.info(user.toString());
}
执行结果:
7. 性能测试代码
为了对比java对象转json的性能,先对json库进行性能测试
7.1 JAVA对象转JSON
@Test
@PerfTest(invocations = 1000000, threads = 16)
public void entityToJson() {
User userEntity = new User();
userEntity.setId(1234L);
userEntity.setName("Test User");
userEntity.setAge(30);
userEntity.setEmail("test@example.com");
userEntity.setCreateAt(LocalDateTime.now());
JsonUtil.toJson(userEntity);
}
测试结果:
结果分析:16线程总共执行100w次,总耗时1.718秒,单次最大耗时359毫秒,平均每次耗时约0.019毫秒
7.2 JSON转JAVA对象
@Test
@PerfTest(invocations = 1000000, threads = 16)
public void jsonToEntity() {
String jsonStr = "{\"age\":30,\"createAt\":\"2024-01-04T15:23:35.568027800\",\"email\":\"test@example.com\",\"id\":1234,\"name\":\"Test User\"}";
User userEntity = JsonUtil.toBean(jsonStr, User.class);
}
测试结果:
结果分析:16线程总共执行100w次,总耗时2.009秒,单次最大耗时425毫秒,平均每次耗时约0.024毫秒
7.3 Protobuf转JSON
@Test
@PerfTest(invocations = 1000000, threads = 16)
public void protobufToJson() throws Exception {
Timestamp timestamp = Timestamp.newBuilder().setSeconds(1704356248L).build();
UserProto.User user = UserProto.User.newBuilder()
.setId(1234L)
.setName("Test User")
.setAge(30)
.setEmail("test@example.com")
.setCreateAt(timestamp)
.build();
ProtobufUtil.toJson(user);
}
测试结果:
结果分析:16线程总共执行100w次,总耗时2.708秒,单次最大耗时837毫秒,平均每次耗时约0.034毫秒
7.4 JSON转Protobuf
@Test
@PerfTest(invocations = 1000000, threads = 16)
public void jsonToProtobuf() throws Exception {
String jsonStr = "{\"age\":30,\"createAt\":\"2024-01-04T02:17:28Z\",\"email\":\"test@example.com\",\"id\":1234,\"name\":\"Test User\"}";
UserProto.User user = ProtobufUtil.toBean(jsonStr, UserProto.User.class);
}
测试结果:
结果分析:16线程总共执行100w次,总耗时2.782秒,单次最大耗时639毫秒,平均每次耗时约0.037毫秒
8. 总结
在进行 proto(Protocol Buffers)和 JSON 库的性能对比时,我们观察到,两者在单次解析过程中的平均时间均低于1毫秒。无论是采用 Protocol Buffers 还是 JSON 作为数据序列化和反序列化的机制,它们都具备高效处理能力,非常适合用于数据密集型的通信框架。这种高效的数据处理能力确保了在网络通信中,即使是在数据量较大或请求频率较高的情况下,这两种技术也能提供稳定且迅速的数据流转换,从而满足现代应用程序对于性能的严格要求。因此,无论是基于轻量级的 JSON 格式还是基于更高效但需要特定解析方式的 Protocol Buffers,它们都是构建高效且可靠通信框架的可行选择。
9. Grpc
9.1 下载和安装protoc-gen-grpc-java插件
github地址:https://grpc.io/docs/languages/java/quickstart/
exe文件地址:https://test-release-02.oss-cn-hangzhou.aliyuncs.com/protoc-gen-grpc-java-1.58.0-windows-x86_64.exe
9.2 定义 Protobuf 模型
service
syntax = "proto3";
import "room.proto";
import "user.proto";
package com.rainbowred;
option java_multiple_files = true;
option java_package = "com.rainbowred.commons.rpc.service.grpc";
option java_outer_classname = "MessageServiceProto";
service MessageService {
rpc getRoomList(UserIdRequest) returns (RoomList) {}
}
实体类room
syntax = "proto3";
package com.rainbowred;
option java_multiple_files = false;
option java_package = "com.rainbowred.commons.entities.protobuf";
option java_outer_classname = "RoomProto";
message Room {
string roomId = 1;
int64 rid = 2;
string header = 3;
string title = 4;
string lastMessage = 5;
int32 unreadCount = 6;
}
message RoomList {
repeated Room rooms = 1;
}
实体类UserIdRequest
syntax = "proto3";
package com.rainbowred;
option java_multiple_files = false;
option java_package = "com.rainbowred.commons.entities.protobuf";
option java_outer_classname = "UserProto";
message UserIdRequest {
string userId = 1;
}
9.3 生成Grpc Protobuf Java 类
protoc --proto_path=D:\JavaSource\micro-services\commons-lib\commons-entities\src\main\resources\protobuf --java_out=D:\tmp\protobuf --grpc-java_out=D:\tmp\protobuf --plugin=protoc-gen-grpc-java="D:\Program Files\protoc\protoc-gen-grpc\protoc-gen-grpc-java-1.58.0-windows-x86_64.exe" D:\JavaSource\micro-services\commons-lib\commons-entities\src\main\resources\protobuf\message_service.proto