这是我参与「第五届青训营 」伴学笔记创作活动的第 9 天
前言
Protobuf(Protocol Buffers 的缩写)是一种由 Google 开发的数据序列化格式。它旨在成为一种语言和平台中立的方式,用于在不同系统、应用程序或进程之间进行结构化数据通信。
Protobuf 定义了一种语言无关的接口定义语言(IDL),可用于描述数据结构的结构。IDL 定义了数据结构的字段及其类型,以及任何其他元数据,例如字段名称、默认值和消息 ID。然后将 IDL 编译为不同编程语言的代码,例如 C++、Java、Python 等。
编码后的数据通常比其他序列化格式(例如 JSON 或 XML)更小、更快速处理。它还提供了不同版本的同一消息之间的向后兼容性,允许新客户端与旧服务器进行通信,反之亦然。
语法
Protobuf 的语法由.proto 文件定义。以下是.proto 文件中使用的一些关键语法元素的详细说明:
1. 语法版本
.proto 文件中应该始终指定使用的语法版本。目前,最新的语法版本是 proto3。例如:
syntax = "proto3";
2. 消息定义
消息是.proto 文件中最重要的元素之一,用于描述一种特定类型的数据结构。消息定义由关键字 message 开始,后跟消息名称和一对花括号,其中包含消息的字段定义。例如:
message Person {
string name = 1;
int32 age = 2;
repeated string phone_number = 3;
}
在上面的示例中,定义了一个名为 Person 的消息类型,它包含三个字段:name、age 和 phone_number。每个字段都有一个类型和一个唯一的数字标识符,用于在序列化和反序列化过程中识别该字段。
3. 字段定义
字段定义描述了消息中包含的特定字段的名称、类型和唯一标识符。字段定义由字段类型、字段名称和字段标识符组成。例如:
string name = 1;
在上面的示例中,定义了一个名为 name 的字符串类型的字段,并使用 1 作为其唯一标识符。标识符应从 1 开始,以便为每个字段分配唯一的标识符。
4. 枚举定义
枚举是一种用于定义一组可能的值的消息类型。枚举定义由关键字 enum 开始,后跟枚举名称和一对花括号,其中包含每个枚举值的名称和唯一的数值。例如:
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
在上面的示例中,定义了一个名为 PhoneType 的枚举类型,它包含三个枚举值:MOBILE、HOME 和 WORK。每个枚举值都有一个名称和一个唯一的数值,用于标识该值。
5. 服务定义
服务定义用于定义一组相关的 RPC 方法。服务定义由关键字 service 开始,后跟服务名称和一对花括号,其中包含每个 RPC 方法的定义。例如:
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse);
}
在上面的示例中,定义了一个名为 SearchService 的服务类型,它包含一个名为 Search 的 RPC 方法。该方法接受一个 SearchRequest 消息作为输入,并返回一个 SearchResponse 消息作为输出。
这些是.proto 文件中最重要的语法元素之一,但还有其他一些元素,如选项、扩展、注释等。了解这些元素可以帮助您更好地理解和使用 Protobuf。
Kitex 生成代码
下面用 Kitex 生成代码进行简单的实践。
首先,idl 文件夹下定义了一个 user.proto 文件,详细内容如下:
syntax = "proto3";
package user;
option go_package = "user";
message BaseResp {
int32 status_code = 1;//状态码
string status_message = 2;//状态描述
int64 service_time = 3;//服务时间
}
message User {
int64 id = 1;//用户id
string name = 2;//用户名称
int64 follow_count = 3;//关注总数
int64 follower_count = 4;//粉丝总数
bool is_follow = 5;//true-已关注,false-未关注
string avatar = 6; // 用户头像
string background_image = 7; // 用户个人页顶部大图
string signature = 8; // 个人简介
int64 total_favorited = 9; // 获赞数量
int64 work_count = 10; // 作品数量
int64 favorite_count = 11; // 点赞数量
}
message check_user_request {
string username = 1; //登录用户名
string password = 2;//登录密码
}
message check_user_response {
BaseResp base_resp = 1;
int64 user_id = 2;//用户id
}
message register_user_request {
string username = 1;//注册用户名,最长32个字符
string password = 2;//密码,最长32个字符
}
message register_user_response {
BaseResp base_resp = 1;
int64 user_id = 2;//用户id
string token = 3;//用户鉴权token
}
message user_info_request {
int64 user_id = 1;//用户id
string token = 2;//用户鉴权token
}
message user_info_response {
BaseResp base_resp = 1;
User user = 2;//用户信息
}
service UserService {
rpc CheckUser (check_user_request) returns (check_user_response) {}
rpc RegisterUser (register_user_request) returns (register_user_response) {}
rpc UserInfo (user_info_request) returns (user_info_response) {}
}
接着用下面的命令生成代码:
$ kitex -module ephmeral.com/Kitex_example -service user idl/user.proto
- -module 参数是 go mod 里面的地址,因为我是放在 GOPATH 外
- -service 参数表示服务的名称
然后看下生成代码的结构:
- kitex_gen:里面自动生成好的代码,可以不用管
- handler.go:这个文件是 rpc 调用处理函数,里面可以看到在 proto 中定义好的 service,需要我们去具体实现的逻辑
- main.go:主函数,创建 rpc 服务使用
- build 和 script 的文件是用来生成服务器的代码,用来运行的
下面是 handle.go 的生成的代码:
package main
import (
"context"
user "ephmeral.com/Kitex_example/kitex_gen/user"
)
// UserServiceImpl implements the last service interface defined in the IDL.type UserServiceImpl struct{}
// CheckUser implements the UserServiceImpl interface.func (s *UserServiceImpl) CheckUser(ctx context.Context, req *user.CheckUserRequest) (resp *user.CheckUserResponse, err error) {
// TODO: Your code here...
return
}
// RegisterUser implements the UserServiceImpl interface.func (s *UserServiceImpl) RegisterUser(ctx context.Context, req *user.RegisterUserRequest) (resp *user.RegisterUserResponse, err error) {
// TODO: Your code here...
return
}
// UserInfo implements the UserServiceImpl interface.func (s *UserServiceImpl) UserInfo(ctx context.Context, req *user.UserInfoRequest) (resp *user.UserInfoResponse, err error) {
// TODO: Your code here...
return
}
假设我们现在已经实现好了,那么只需要执行以下命令,对应的 user 服务就已经开启了。
$ sh build.sh
$ sh output/bootstrap.sh
小结
protobuf 是 RPC 框架必学的内容,了解相应的语法才能后面更好的使用 RPC 框架进行开发。