命令行安装
linux
# 更新包列表 sudo apt update # 安装 unzip(后面解压 protoc 用) sudo apt install -y unzip # 下载 protoc(这里用 v27.1,你也可以换最新的) PROTOC_ZIP=protoc-27.1-linux-x86_64.zip wget https://github.com/protocolbuffers/protobuf/releases/download/v27.1/$PROTOC_ZIP # 解压到 /usr/local sudo unzip -o $PROTOC_ZIP -d /usr/local bin/protoc sudo unzip -o $PROTOC_ZIP -d /usr/local 'include/*' # 删除压缩包 rm -f $PROTOC_ZIP
mac
# 下载 curl -SL -O https://github.com/protocolbuffers/protobuf/releases/download/v33.2/protoc-33.2-osx-aarch_64.zip # 解压 sudo unzip -o protoc-33.2-osx-aarch_64.zip -d ./protoc
记得把环境变量加入~/.zshrc中
# protoc export PROTOC="/opt/protoc" export PATH="$PROTOC/bin:$PATH"
Github 安装
前往 https://github.com/protocolbuffers/protobuf/releases
对应的protoc目录树
pzj@pzjmac protoc % tree . ├── bin │ └── protoc ├── include │ └── google │ └── protobuf │ ├── any.proto │ ├── api.proto │ ├── compiler │ │ └── plugin.proto │ ├── cpp_features.proto │ ├── descriptor.proto │ ├── duration.proto │ ├── empty.proto │ ├── field_mask.proto │ ├── go_features.proto │ ├── java_features.proto │ ├── source_context.proto │ ├── struct.proto │ ├── timestamp.proto │ ├── type.proto │ └── wrappers.proto └── readme.txt 6 directories, 17 files
安装Go插件
安装go对应的proto插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
验证是否安装成功
protoc-gen-go --version protoc-gen-go-grpc --version
oneof语法
在proto语法当中oneof关键字比较特殊,代表多选一
实际上就是定义proto时不确定使用什么字段,设置oneof,本质上类似Go语法当中的枚举类型
举个例子
如果我的博客文章更新了,既可以选择短信推送也可以选择邮件推送
可以这样定义proto
syntax = "proto3";
option go_package="hello/pb;pb";
package pb;
message NoticeRequest {
string msg = 1;
oneof msg_type {
string phone = 2;
string email = 3;
}
}
message NoticeResponse {
string msg = 1;
}
service NoticeService {
rpc Notice (NoticeRequest) returns (NoticeResponse);
}
定义server端代码
type NoticeServer struct {
pb.UnimplementedNoticeServiceServer
}
func (s *NoticeServer) Notice(ctx context.Context, req *pb.NoticeRequest) (*pb.NoticeResponse, error) {
send_msg := req.Msg
switch req.MsgType.(type) { // 类型断言
case *pb.NoticeRequest_Email:
send_msg += " send by email:" + req.GetEmail()
case *pb.NoticeRequest_Phone:
send_msg += " send by phone" + req.GetPhone()
}
return &pb.NoticeResponse{
Msg: send_msg,
}, nil
}
根据传过来的值进行类型断言后继续处理
定义client端代码
// notice
noticeResp, err := noticeClient.Notice(ctx, &pb.NoticeRequest{Msg: *msg, MsgType: &pb.NoticeRequest_Phone{
Phone: "15811111111",
}})
if err != nil {
log.Fatalf("failed to call Notice[phone]: %v", err)
}
log.Printf("Response: %s", noticeResp.Msg)
noticeResp, err = noticeClient.Notice(ctx, &pb.NoticeRequest{Msg: *msg, MsgType: &pb.NoticeRequest_Email{
Email: "email@codepzj.cn",
}})
if err != nil {
log.Fatalf("failed to call Notice[email]: %v", err)
}
log.Printf("Response: %s", noticeResp.Msg)
可以定义phone和email两种不同的请求信息