golang之gRPC
相关链接:
grpc:
https://grpc.io/docs/languages/go/quickstart/
protobuf:
https://protobuf.dev/programming-guides/proto3/
protobuf语法:
示例:
syntax = "proto3"; // 声明请求参数 message SearchRequest { string query = 1; int32 page = 2; int32 page_size = 3; }
说明:
- 声明proto文件使用的语法版本,目前是proto2 和proto3, 如果不声明的话, 他会默认使用protocol2,并且语法声明必须是在非空格的第一行
- 声明数据使用message,里面定义参数的类型,名称,索引位置(注意从1开始), 以分号结尾
- 索引的取值范围为:1 ~ 536,870,911
- 索引的值必须保证唯一性
枚举类型:
enum Status { STATUS_UNSPECIFIED = 0; STATUS_OK = 1; STATUS_FAIL = 2; }
通过enum关键字定义枚举类型,
- 枚举是一个Int32类型
- 第一个枚举类必须从0开始,可以使用XXX_UNSPECIFIED作为占位符
- 不推荐出现负数
字段标签:
optional:
- FileOptions —— 文件级别
- MessageOptions —— 消息级别
- FieldOptions —— 字段级别
- ServiceOptions —— service级别
- MethodOptions —— method级别
repeated:只有标量,枚举,message类型可以被修饰使用
标识被重复人一次(包括0次),可标识当前修饰类型的变长数组
// repeated message RepeatedMessage { repeated SearchRequest requests = 1; repeated Status status = 2; repeated int32 number = 3; }
map:
message MapMessage{ map<string, string> message = 1; map<string, SearchRequest> request = 2; }
any:
any类型可以包含一个不需要指定类型的任意的序列化消息。要使用any类型,需要import google/protobuf/any.proto。any类型字段的encode/decode交由各语言的运行时各自实现,例如在Go语言中可以这样读写any类型的字段:
... import "google/protobuf/any.proto"; ... message AnyMessage { string message = 1; google.protobuf.Any details = 2; } ...
oneof
Protocol buffer compiler安装
【源码】
自定义安装->linux/mac
# 1.卸载老版编译器 apt remove protobuf-compiler # 2.下载并解压 wget https://github.com/protocolbuffers/protobuf/releases/download/v23.0/protoc-23.0-linux-x86_64.zip unzip protoc-23.0-linux-x86_64.zip -d protoc-23.0 # 3.设置环境变量 export PATH="$PATH:/usr/local/src/protoc-23.0/bin" # 4.测试 protoc --version # libprotoc 23.0
window需要到github上下载压缩包到本地并将bin目录下的protoc.exe文件加入环境变量即可
https://github.com/protocolbuffers/protobuf/releases/tag/v23.4
Go 支持
# 1.安装 $ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest # 2.添加环境变量 export PATH="$PATH:$(go env GOPATH)/bin"
相关命令:
# 根据.proto文件,生成xxx.pb.go 和 xxx_grpc.pb.go
protoc --go_out=. --go_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \ helloworld/helloworld.proto
protoc -I=pb \ --go_out=pb --go_opt=paths=source_relative \ --go-grpc_out=pb --go-grpc_opt=paths=source_relative \ pb/addsrv.proto
Demo示例:
1.初始化项目
mkdir user-service cd user-service go mod init user-service
mkdir service
2.在Service目录中添加users.proto文件
syntax = "proto3"; package service; option go_package="user-service/service"; // 请求消息 message UserRequest { string email = 1; int32 id = 2; } // 响应数据 message User { string id = 1; string first_name = 2; string last_name = 3; int32 age = 4; } message UserResponse { User user = 1; } service Users { rpc GetUser(UserRequest) returns (UserResponse) {} }
proto同级目录下生成pb.go 和grpc.pb.go文件
protoc --go_out=. --go-opt=paths=source-relative --go-grpc_out=. --go-grpc_opt=paths=source-relative users.proto
3.新增服务文件 user-service/server/server.go
package main import ( "context" "google.golang.org/grpc" "log" "net" "os" users "user-service/service" ) func main() { addr := os.Getenv("LISTEN_ADDR") if len(addr) == 0 { addr = ":5001" } listen, err := net.Listen("tcp", addr) if err != nil { log.Fatal(err) } s := grpc.NewServer() registerServices(s) log.Fatal(startServer(s, listen)) } type userService struct { users.UnimplementedUsersServer } func (s *userService) GetUser(ctx context.Context, in *users.UserRequest) (*users.UserResponse, error) { log.Printf("收到数据. 邮件:%s id:%d", in.Email, in.Id) // 返回响应数据 u := users.User{ Id: "user-id", FirstName: "first name", LastName: "last name", Age: 10, } return &users.UserResponse{ User: &u, }, nil } func registerServices(s *grpc.Server) { users.RegisterUsersServer(s, &userService{}) } func startServer(s *grpc.Server, l net.Listener) error { return s.Serve(l) }
4.新增客户端user-service/client/main.go文件
package main import ( "context" "google.golang.org/grpc" "log" "os" users "user-service/service" ) func main() { if len(os.Args) != 2 { log.Fatal("缺少grpc服务地址") } addr := os.Args[1] // 初始化grpc连接 conn, err := setupGrpcConn(addr) if err != nil { log.Fatal(err) } defer conn.Close() // 获取服务客户端实例化 c := getUserServiceClient(conn) // 调用指定方法 user, err := getUser(c, &users.UserRequest{ Email: "liaotiam@126.com", Id: 110, }) if err != nil { log.Fatal(err) } log.Printf("响应数据: %v", user) } func getUser(client users.UsersClient, u *users.UserRequest) (*users.UserResponse, error) { return client.GetUser(context.Background(), u) } func getUserServiceClient(conn *grpc.ClientConn) users.UsersClient { return users.NewUsersClient(conn) } func setupGrpcConn(addr string) (*grpc.ClientConn, error) { return grpc.DialContext( context.Background(), addr, grpc.WithInsecure(), grpc.WithBlock(), ) }
分别启动服务端与客户端, 查看是否调用成功
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战