网站建设背景怎么设置成网站建设蘑菇街
一、为什么需要序列化?
- 数据跨平台/语言交互: 
- 不同编程语言(如 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_package="com.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:键值对(如 map<string, int32>)
 
 - 嵌套消息:消息内定义其他消息
 
2.3 版本控制策略
- 新增字段:使用新标签号,旧代码会忽略未知字段
 - 弃用字段:标记 reserved 防止误用
reserved 2, 15 to 20; // 保留标签号 reserved "email"; // 保留字段名 
3、Protobuf 序列化机制
3.1 二进制编码原理
- TLV 结构:每个字段按 Tag-Length-Value 编码(无冗余字段名) 
- Tag:字段标签号 + 数据类型(如 Varint, 64-bit, Length-delimited)
 - Value:根据类型压缩存储(如 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 --version
 
(2)IDEA配置protobuf插件
IntelliJ IDEA ⇒ Settings ⇒ Plugins ⇒ MarketPlace,输入 Protocol Buffers,点击 Install

(3)基础命令结构
protoc [OPTIONS] PROTO_FILESPROTO_FILES					# 待编译的 .proto 文件路径(如 src/main/proto/hello.proto)OPTIONS:						# 控制代码生成和编译行为的参数--<lang>_out=OUT_DIR		# 指定生成代码的语言和输出目录(如 --java_out、--python_out)(--java_out=src/main/java)--plugin=EXECUTABLE		# 指定自定义插件(如 gRPC 插件)	(--plugin=protoc-gen-grpc-java=/path/to/plugin)--grpc-<lang>_out			# 生成 gRPC 服务代码(需安装对应语言的 gRPC 插件)	(--grpc-java_out=src/main/java)-IPATH, --proto_path=PATH	# 指定 .proto 文件的搜索路径(可多次使用)	(-I src/main/proto -I ../shared/proto)--descriptor_set_out=FILE	# 生成描述符文件(包含所有编译的 .proto 信息)	(--descriptor_set_out=my_protos.desc)--version					# 显示 protoc 版本	( protoc --version)-h, --help					# 显示帮助信息	(protoc --help)--encode=MESSAGE			# 将文本消息编码为二进制(需指定 .proto)	(protoc --encode=MyMessage my.proto < input.txt)--decode=MESSAGE			# 将二进制消息解码为文本	(protoc --decode=MyMessage) my.proto < input.bin
 
示例:
# 场景 1:生成 Java 代码
protoc \--proto_path=src/main/proto \   # .proto 文件搜索路径--java_out=src/main/java \      # Java 代码输出目录src/main/proto/hello.proto      # 待编译的 proto 文件# 场景 2:生成 Java gRPC 代码
# 需提前安装 grpc-java 插件(protoc-gen-grpc-java)
protoc \--proto_path=src/main/proto \--java_out=src/main/java \--grpc-java_out=src/main/java \  # 生成 gRPC 服务代码--plugin=protoc-gen-grpc-java=/path/to/protoc-gen-grpc-java \  # 显式指定插件路径src/main/proto/hello.proto# 场景 3:多文件批量编译
protoc \-I src/main/proto \-I ../shared/protos \          # 多路径导入--java_out=out/java \src/main/proto/*.proto \       # 编译所有 proto 文件../shared/protos/utils/*.proto# 场景 4:生成描述符文件(Descriptor Set)
protoc \--proto_path=src/main/proto \--descriptor_set_out=my_protos.desc \  # 输出描述符文件--include_imports \                   # 包含所有依赖src/main/proto/hello.proto
 
5、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 = "ContactProto"message ContractRequest{string name = 1; }message ContractResponse{string tel = 1; }service ContractService{rpc query(ContractRequest) returns (ContractResponse); } - 修改 grpc-api 模块pom.xml文件,导入 gprc-java 相关依赖
<dependency><groupId>io.grpc</groupId><artifactId>grpc-netty-shaded</artifactId><version>1.70.0</version><scope>runtime</scope> </dependency> <dependency><groupId>io.grpc</groupId><artifactId>grpc-protobuf</artifactId><version>1.70.0</version> </dependency> <dependency><groupId>io.grpc</groupId><artifactId>grpc-stub</artifactId><version>1.70.0</version> </dependency> <dependency> <!-- necessary for Java 9+ --><groupId>org.apache.tomcat</groupId><artifactId>annotations-api</artifactId><version>6.0.53</version><scope>provided</scope> </dependency> - 修改 grpc-api 模块pom.xml文件,导入 protobuf 插件用于生成代码(也可以用protoc编译器生成)
${os.detected.classifier} 可能会飘红:<build> <!-- 在Maven构建过程中自动检测操作系统类型,并根据操作系统选择合适的protoc编译器和gRPC Java插件版本,从而编译.proto文件并生成相应的Java代码 --><extensions><extension> <!-- 检测操作系统的类型和版本 --><groupId>kr.motd.maven</groupId><artifactId>os-maven-plugin</artifactId><version>1.7.1</version></extension></extensions><plugins><plugin> <!-- 编译.proto文件,生成Java代码 --><groupId>org.xolstice.maven.plugins</groupId><artifactId>protobuf-maven-plugin</artifactId><version>0.6.1</version><configuration><!-- 指定用于编译的protoc编译器版本和分类器(classifier)--><protocArtifact>com.google.protobuf:protoc:3.25.5:exe:${os.detected.classifier}</protocArtifact><!-- 指定用于编译的插件ID,这里使用的是grpc-java,表示将使用gRPC Java的插件来生成额外的Java代码。 --><pluginId>grpc-java</pluginId><!-- 指定gRPC Java插件的版本和分类器 --><pluginArtifact>io.grpc:protoc-gen-grpc-java:1.70.0:exe:${os.detected.classifier}</pluginArtifact><!-- 指定代码生成位置 --><outputDirectory>${basedir}/src/main/java</outputDirectory><!-- 是否清空生成路径下的资源 --><clearOutputDirectory>false</clearOutputDirectory></configuration><executions><execution><goals><!-- compile目标用于编译标准的Protobuf文件 --><goal>compile</goal><!-- compile-custom目标通常用于编译那些需要特殊处理的Protobuf文件(如果有的话)。 --><goal>compile-custom</goal></goals></execution></executions></plugin></plugins> </build>Cannot resolve symbol 'os. detected. classifier',点击Reimport即可

 - 生成代码
生成的代码在(base) zs@Mac contract % cd grpc-api (base) zs@Mac grpc-api % mvn protobuf:compile protobuf:compile-customcontract/grpc-api/src/main/java/com/example

ContractServiceGrpc类结构:- 核心类 
类名 作用 ContractServiceGrpc 入口类,包含服务描述符、方法定义和 Stub 工厂方法(如 newStub) ContractServiceImplBase 服务端基类,需要继承并实现 query 方法的具体逻辑 ContractServiceBlockingStub 客户端同步调用存根,直接阻塞等待响应(如 query 方法调用) ContractServiceFutureStub 客户端异步调用存根,返回 ListenableFuture 对象处理响应 ContractServiceStub 客户端异步流式存根,使用 StreamObserver 处理请求和响应 ServiceDescriptor 服务的元数据描述,包含方法名、请求响应类型等信息  - 关键方法 
方法 功能 getQueryMethod() 返回 query 方法的描述符(请求类型 ContractRequest,响应类型 ContractResponse)。 newBlockingStub(Channel) 创建同步客户端存根,用于阻塞式调用服务端方法。 bindService(AsyncService) 将服务端实现类绑定到 gRPC 服务器,生成 ServerServiceDefinition。  
 - 核心类 
 
- 在 grpc-api 模块的 src/main目录下新建目录proto,创建 contract.proto ⽂件
 - grpc-server 模块 
- 导入依赖:
<dependency><groupId>com.example</groupId><artifactId>grpc-api</artifactId><version>1.0-SNAPSHOT</version> </dependency> - 编写服务类 ContractServiceImpl:
package 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, StreamObserver<ContactProto.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.java
package 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 
- 导入依赖:
<dependency><groupId>com.example</groupId><artifactId>grpc-api</artifactId><version>1.0-SNAPSHOT</version> </dependency> - 编写调用类Main.java
package 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 默认)以支持字段缺失
 - 版本兼容性测试:确保新旧版本协议可互操作
 
十、资料
- 深入protobuf(Protocol Buffers)原理:简化你的数据序列化
 - ProtoBuf 入门详解
 - protobuf简介
 
