proto安装

命令行安装

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两种不同的请求信息