番禺建网站公司,网页建设的公司,中国十大国企公司排名,使用wordpress需要懂什么语言一、为什么需要序列化#xff1f;
数据跨平台/语言交互#xff1a; 不同编程语言#xff08;如 Java、Python、Go#xff09;的数据结构不兼容#xff0c;序列化提供统一的数据表示。例如#xff1a;Java 的 HashMap 和 Python 的 dict 需转换为通用格式#xff08;如 …一、为什么需要序列化
数据跨平台/语言交互 不同编程语言如 Java、Python、Go的数据结构不兼容序列化提供统一的数据表示。例如Java 的 HashMap 和 Python 的 dict 需转换为通用格式如 JSON、Protobuf才能通信。 网络传输优化 原始内存中的对象包含指针、元数据等冗余信息无法直接传输。序列化后数据体积更小减少带宽占用提升传输效率。 持久化存储 将对象转换为字节流或文本保存到文件或数据库中便于后续读取和恢复。 数据版本兼容性 通过序列化协议如 Protobuf支持字段的增删改避免因数据结构变化导致系统崩溃。
二、Protocol Buffers
1、Protobuf 简介 官网地址https://protobuf.dev/overview/GitHub 项目地址https://github.com/protocolbuffers/protobuf Protocol Buffers简称 Protobuf是 Google 开发的一种高效、跨平台的数据序列化协议专为结构化数据的存储和通信设计。
它通过简洁的接口定义语言IDL描述数据结构并生成高效的序列化代码广泛应用于微服务通信如 gRPC、大数据存储等场景。
Protocol Buffers 的核心优势
高效性二进制编码体积比 XML/JSON 小 3-10 倍序列化速度快 5-100 倍跨语言支持支持 Java、C、Python、Go 等主流语言代码自动生成强类型约束通过 .proto 文件明确定义数据结构减少运行时错误向后兼容性通过字段编号Tag管理版本演进新旧版本可共存可扩展性支持新增字段、嵌套消息、枚举、Map 等复杂结构
2、Protobuf 语法详解
2.1 基本结构
在 .proto 文件中定义数据结构和接口示例hello.proto
syntax proto3; // 指定使用 proto3 语法option java_packagecom.example; // 生成文件所在路径及包名。service serviceName { // 定义服务在这个服务中需要有一个方法这个方法可以接受客户端的参数再返回服务端的响应。 rpc GetUser (UserRequest) returns (UserResponse);
}message UserRequest { // 定义消息类型命名规范使⽤驼峰命名法⾸字⺟⼤写。string name 1; // 字段类型 名称 标签号不可重复int32 age 2;repeated string hobbies 3; // repeated 表示返回多个数据可理解为Java List
}.proto 文件命名应该使⽤全⼩写字⺟命名多个字⺟之间⽤ _ 连接。 例如lower_snake_case.proto书写 .proto ⽂件代码时应使⽤ 2 个空格的缩进。向⽂件添加注释可使⽤ // 或者 /* … */
在利用 Protobuf 进行网络数据传输时确保通信双方拥有一致的 .proto 文件至关重要。缺少了相应的 .proto 文件通信任何一方都无法生成必要的工具函数代码进而无法解析接收到的消息数据。
与 JSON 这种文本格式不同后者即便在没有 JSON.parse 反序列化函数的情况下人们仍能大致推断出消息内容。相比之下Protobuf 序列化后的数据是二进制字节流它并不适合人类阅读且必须通过特定的反序列化函数才能正确解读数据。
Protobuf 的这种设计在提高数据安全性方面具有优势因为缺少 .proto 文件就无法解读数据内容。然而这也意味着在通信双方之间需要维护一致的 .proto 文件随着项目的扩展这可能会带来额外的维护成本。
2.2 字段规则
字段标签号Tag唯一标识字段1-15 占 1 字节16-2047 占 2 字节标量类型string, int32, bool, bytes, double 等复合类型 repeated数组或列表oneof多个字段中只能同时设置一个map键值对如 mapstring, int32 嵌套消息消息内定义其他消息
2.3 版本控制策略
新增字段使用新标签号旧代码会忽略未知字段弃用字段标记 reserved 防止误用reserved 2, 15 to 20; // 保留标签号
reserved email; // 保留字段名3、Protobuf 序列化机制
3.1 二进制编码原理
TLV 结构每个字段按 Tag-Length-Value 编码无冗余字段名 Tag字段标签号 数据类型如 Varint, 64-bit, Length-delimitedValue根据类型压缩存储如 Varint 对整数进行变长编码 示例int32 age 2; → 标签号 2 对应二进制 0x10值 25 编码为 0x19
3.2 性能对比
格式编码方式可读性体积解析速度XML文本高大慢JSON文本高较大较慢Protobuf二进制低小极快
4、protoc 编译器
protoc 是 Protocol Buffers 的核心编译器用于将 .proto 文件编译为不同语言的代码如 Java、C、Python 等。
1安装编译器 protoc
官网安装文档https://protobuf.dev/installation/
在 https://github.com/protocolbuffers/protobuf/releases 上下载对应的版本然后配置环境变量
# 配置环境变量
cat ~/.zshrc EOF
# Protocol Buffers
export PATH$PATH:/Users/zs/App/env/protoc-25/bin
EOF# 验证
protoc --version2IDEA配置protobuf插件
IntelliJ IDEA ⇒ Settings ⇒ Plugins ⇒ MarketPlace输入 Protocol Buffers点击 Install 3基础命令结构
protoc [OPTIONS] PROTO_FILESPROTO_FILES # 待编译的 .proto 文件路径如 src/main/proto/hello.protoOPTIONS # 控制代码生成和编译行为的参数--lang_outOUT_DIR # 指定生成代码的语言和输出目录如 --java_out、--python_out--java_outsrc/main/java--pluginEXECUTABLE # 指定自定义插件如 gRPC 插件 --pluginprotoc-gen-grpc-java/path/to/plugin--grpc-lang_out # 生成 gRPC 服务代码需安装对应语言的 gRPC 插件 --grpc-java_outsrc/main/java-IPATH, --proto_pathPATH # 指定 .proto 文件的搜索路径可多次使用 -I src/main/proto -I ../shared/proto--descriptor_set_outFILE # 生成描述符文件包含所有编译的 .proto 信息 --descriptor_set_outmy_protos.desc--version # 显示 protoc 版本 protoc --version-h, --help # 显示帮助信息 protoc --help--encodeMESSAGE # 将文本消息编码为二进制需指定 .proto protoc --encodeMyMessage my.proto input.txt--decodeMESSAGE # 将二进制消息解码为文本 protoc --decodeMyMessage my.proto input.bin示例
# 场景 1生成 Java 代码
protoc \--proto_pathsrc/main/proto \ # .proto 文件搜索路径--java_outsrc/main/java \ # Java 代码输出目录src/main/proto/hello.proto # 待编译的 proto 文件# 场景 2生成 Java gRPC 代码
# 需提前安装 grpc-java 插件protoc-gen-grpc-java
protoc \--proto_pathsrc/main/proto \--java_outsrc/main/java \--grpc-java_outsrc/main/java \ # 生成 gRPC 服务代码--pluginprotoc-gen-grpc-java/path/to/protoc-gen-grpc-java \ # 显式指定插件路径src/main/proto/hello.proto# 场景 3多文件批量编译
protoc \-I src/main/proto \-I ../shared/protos \ # 多路径导入--java_outout/java \src/main/proto/*.proto \ # 编译所有 proto 文件../shared/protos/utils/*.proto# 场景 4生成描述符文件Descriptor Set
protoc \--proto_pathsrc/main/proto \--descriptor_set_outmy_protos.desc \ # 输出描述符文件--include_imports \ # 包含所有依赖src/main/proto/hello.proto5、Protobuf 开发示例
Java gRpc开发一个通讯录服务根据联系人名字返回其电话号码。
需要注意的是grpc服务调用底层已经用protobuf实现了序列化与反序列化故无需手动序列化。
创建 Maven 项目 contract contract 项目添加三个模块grpc-api、grpc-service、grpc-client grpc-api 模块修改内容 在 grpc-api 模块的 src/main目录下新建目录proto创建 contract.proto ⽂件syntax proto3;option java_multiple_files false;
option java_package com.example;
option java_outer_classname ContactProtomessage ContractRequest{string name 1;
}message ContractResponse{string tel 1;
}service ContractService{rpc query(ContractRequest) returns (ContractResponse);
}修改 grpc-api 模块pom.xml文件导入 gprc-java 相关依赖dependencygroupIdio.grpc/groupIdartifactIdgrpc-netty-shaded/artifactIdversion1.70.0/versionscoperuntime/scope
/dependency
dependencygroupIdio.grpc/groupIdartifactIdgrpc-protobuf/artifactIdversion1.70.0/version
/dependency
dependencygroupIdio.grpc/groupIdartifactIdgrpc-stub/artifactIdversion1.70.0/version
/dependency
dependency !-- necessary for Java 9 --groupIdorg.apache.tomcat/groupIdartifactIdannotations-api/artifactIdversion6.0.53/versionscopeprovided/scope
/dependency修改 grpc-api 模块pom.xml文件导入 protobuf 插件用于生成代码也可以用protoc编译器生成build !-- 在Maven构建过程中自动检测操作系统类型并根据操作系统选择合适的protoc编译器和gRPC Java插件版本从而编译.proto文件并生成相应的Java代码 --extensionsextension !-- 检测操作系统的类型和版本 --groupIdkr.motd.maven/groupIdartifactIdos-maven-plugin/artifactIdversion1.7.1/version/extension/extensionspluginsplugin !-- 编译.proto文件生成Java代码 --groupIdorg.xolstice.maven.plugins/groupIdartifactIdprotobuf-maven-plugin/artifactIdversion0.6.1/versionconfiguration!-- 指定用于编译的protoc编译器版本和分类器classifier--protocArtifactcom.google.protobuf:protoc:3.25.5:exe:${os.detected.classifier}/protocArtifact!-- 指定用于编译的插件ID这里使用的是grpc-java表示将使用gRPC Java的插件来生成额外的Java代码。 --pluginIdgrpc-java/pluginId!-- 指定gRPC Java插件的版本和分类器 --pluginArtifactio.grpc:protoc-gen-grpc-java:1.70.0:exe:${os.detected.classifier}/pluginArtifact!-- 指定代码生成位置 --outputDirectory${basedir}/src/main/java/outputDirectory!-- 是否清空生成路径下的资源 --clearOutputDirectoryfalse/clearOutputDirectory/configurationexecutionsexecutiongoals!-- compile目标用于编译标准的Protobuf文件 --goalcompile/goal!-- compile-custom目标通常用于编译那些需要特殊处理的Protobuf文件如果有的话。 --goalcompile-custom/goal/goals/execution/executions/plugin/plugins
/build${os.detected.classifier} 可能会飘红Cannot resolve symbol os. detected. classifier 点击 Reimport 即可 生成代码(base) zsMac contract % cd grpc-api
(base) zsMac grpc-api % mvn protobuf:compile protobuf:compile-custom生成的代码在 contract/grpc-api/src/main/java/com/example ContractServiceGrpc类结构 核心类 类名作用ContractServiceGrpc入口类包含服务描述符、方法定义和 Stub 工厂方法如 newStubContractServiceImplBase服务端基类需要继承并实现 query 方法的具体逻辑ContractServiceBlockingStub客户端同步调用存根直接阻塞等待响应如 query 方法调用ContractServiceFutureStub客户端异步调用存根返回 ListenableFuture 对象处理响应ContractServiceStub客户端异步流式存根使用 StreamObserver 处理请求和响应ServiceDescriptor服务的元数据描述包含方法名、请求响应类型等信息 关键方法 方法功能getQueryMethod()返回 query 方法的描述符请求类型 ContractRequest响应类型 ContractResponse。newBlockingStub(Channel)创建同步客户端存根用于阻塞式调用服务端方法。bindService(AsyncService)将服务端实现类绑定到 gRPC 服务器生成 ServerServiceDefinition。 grpc-server 模块 导入依赖dependencygroupIdcom.example/groupIdartifactIdgrpc-api/artifactIdversion1.0-SNAPSHOT/version
/dependency编写服务类 ContractServiceImplpackage com.example.service;import com.example.ContactProto;
import com.example.ContractServiceGrpc;
import io.grpc.stub.StreamObserver;public class ContractServiceImpl extends ContractServiceGrpc.ContractServiceImplBase {Overridepublic void query(ContactProto.ContractRequest request, StreamObserverContactProto.ContractResponse responseObserver) {//1.业务处理System.out.println(Receive client data: request.getName());try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}//2.封装响应ContactProto.ContractResponse response ContactProto.ContractResponse.newBuilder().setTel(100 110120119).build();//3.响应responseObserver.onNext(response);responseObserver.onCompleted();}
}编写启动类Main.javapackage com.example;import com.example.service.ContractServiceImpl;
import io.grpc.ServerBuilder;import java.io.IOException;public class Main {public static void main(String[] args) throws IOException, InterruptedException {ServerBuilder.forPort(9000).addService(new ContractServiceImpl()).build().start().awaitTermination();}}grpc-client 导入依赖dependencygroupIdcom.example/groupIdartifactIdgrpc-api/artifactIdversion1.0-SNAPSHOT/version
/dependency编写调用类Main.javapackage com.example;import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;import java.util.Iterator;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class Main {public static void main(String[] args) {//1.创建通信管道ManagedChannel managedChannel ManagedChannelBuilder.forAddress(localhost, 9000).usePlaintext().build();try{//2.获取代理对象 stubContractServiceGrpc.ContractServiceBlockingStub contractServiceBlockingStub ContractServiceGrpc.newBlockingStub(managedChannel);//3.创建请求对象ContactProto.ContractRequest contractRequest ContactProto.ContractRequest.newBuilder().setName(Mary).build();//4.grpc调用ContactProto.ContractResponse contractResponse contractServiceBlockingStub.query(contractRequest);//5.业务处理System.out.println(unary rpc call: contractResponse.getTel());} catch (Exception e){System.out.println(e.getMessage());;} finally {managedChannel.shutdown();}}
}5、Protobuf 与 gRPC 的协作
服务定义在 .proto 中定义 RPC 方法如 rpc SayHello(…)代码生成protoc 生成服务端和客户端桩代码如 GreeterGrpc.java数据传输gRPC 使用 Protobuf 作为默认序列化协议高效传输二进制数据
6、适用场景与局限性
适用场景
微服务间通信如 gRPC需要高性能序列化的场景如游戏、IoT大数据存储如 Hadoop、Kafka 消息格式
局限性
可读性差二进制数据无法直接阅读需预定义 Schema灵活性不如 JSON适合结构化数据版本管理复杂度需谨慎处理字段变更
7、最佳实践
合理规划标签号频繁使用的字段用 1-15 以节省空间避免修改字段类型可能导致解析错误使用 optional 字段proto3 默认以支持字段缺失版本兼容性测试确保新旧版本协议可互操作
十、资料
深入protobufProtocol Buffers原理简化你的数据序列化ProtoBuf 入门详解protobuf简介