java - Protobuf

1. 安装 Protocol Buffer 编译器

  1. 官网下载操作系统对应的编译器:https://github.com/protocolbuffers/protobuf/releases
  2. windows系统,解压后配置环境变量

  1. 确定环境变量是否生效

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 类

  1. 控制台执行命令
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
  1. 生成的代码

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值