[go-每日一库] golang-grpc库-protoc基本参数与简单实践(一)
grpc使用protobuf进行序列化、反序列化,通常用protoc作为编译工具,对于go使用grpc作为rpc的框架,由于protoc工具未实现go-generate,我们需要使用protoc-gen-go插件帮我们生成go文件。
本文的分享分为以下方面:
- 准备工作
- protoc工具的相关命令
- proto文件的定义
- go-grpc的实践
1.准备工作
用go实现grpc的编码,首先下载protoc和protoc-gen-go工具插件。
protoc
官方下载地址:https://github.com/google/protobuf/releases
根据自己的操作系统选择对应版本下载后解压,另外记得设置环境变量:
如果是win平台,直接解压,然后编辑环境变量,add一下即可。
如果是linux平台,编辑/etc/profile,加入解压后的protoc的文件夹,最后source后就可以了。
安装后验证:
protoc --version libprotoc 3.19.4
protoc-gen-go
命令行下载:
$ go get google.golang.org/protobuf/cmd/protoc-gen-go@latest $ go get google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
2.protoc命令参数
受限篇幅,命令参数仅针对常用参数,-I[--proto_path]
,xx_out
。
-I/--proto_path
指定导入的proto文件的路径,如-I
,我们可以用-I.
,指定本文件夹下的proto文件,如果是proto_path,如--proto_path=$PATH
即可。
XX_out
该参数指定protoc工具生成对应编程语言的service文件,如go_out
,在使用时--go_out=plugins=grpc:.
,即指明grpc的插件,本文件夹下。
3.proto文件的定义
首先看个usage:
syntax = "proto3"; // version package demo; // pkg name option go_package = "./"; // 注意包名, client、server should import pkg // define request body message helloRequest { string name = 1; // arg-1, type: string, field: name int64 id = 2; // arg-2, type: int64, field: id } // define response body message helloResponse { string resp = 1; // arg-1, type: string, field: resp } // define service service helloService { rpc hello(helloRequest) returns (helloResponse) {} // rpc service, func: hello, params: helloRequest, return helloResponse }
syntax
指明protoc的版本
package
指明pkg名,用以取分不同的serivce文件,也指定当前的proto文件属于那个包
options
指定编译后的文件的输出目录
message
定义参数
service
定义服务名
接着看看在定义参数的一些注意事项:
支持的数据类型
复杂数据类型
定义List列表(值可以是普通变量,也可以复杂对象):
message ComplexObject { repeated string sons = 4; // List列表 repeated Result result = 6; // 复杂的对象List } // 定义一个新的对象 message Result { string url = 1; string title = 2; repeated string snippets = 3; }
定义枚举成员:
message ComplexObject { ... Gender gender = 5; // Enum值 ... } enum Gender { MAN = 0; WOMAN = 1; }
定义Any对象(用于定义任意的值):
message ComplexObject { repeated google.protobuf.Any any = 7; // Any对象 }
定义Map对象(key,value都可以是复杂对象):
message ComplexObject { map<string, MapVaule> map = 8; // 定义Map对象 } // 定义Map的value值 message MapVaule { string mapValue = 1; }
通过 reserved保留 Assigning Tags和filed name用于将来扩展用:
message ComplexObject { reserved 12, 15, 9 to 11; // 预留将来使用的Assigning Tags, reserved "foo", "bar"; // 预留将来使用的filed name }
4.protoc的实践
c/s目录结构
proto文件
syntax = "proto3"; // version package demo; // pkg name option go_package = "./"; // 注意包名, client、server should import pkg // define request body message helloRequest { string name = 1; // arg-1, type: string, field: name int64 id = 2; // arg-2, type: int64, field: id } // define response body message helloResponse { string resp = 1; // arg-1, type: string, field: resp } // define service service helloService { rpc hello(helloRequest) returns (helloResponse) {} // rpc service, func: hello, params: helloRequest, return helloResponse }
生成service文件
进入proto目录:
protoc -I. --go_out=plugins=grpc:. hello.proto
go.mod
module demo go 1.16 replace ( ../go-grpc-v1 => ../demo ) require ( google.golang.org/grpc v1.48.0 google.golang.org/protobuf v1.28.0 )
server
package main import ( "context" hellopb "demo/proto" "fmt" "google.golang.org/grpc" "log" "net" ) const ( ADDR string = "localhost:50052" ) func main() { log.Println("grpc server is up...") lis, err := net.Listen("tcp", ADDR) if err != nil { log.Fatal(err.Error()) } s := grpc.NewServer() hellopb.RegisterHelloServiceServer(s, &Service{}) err = s.Serve(lis) if err != nil { log.Fatal(err.Error()) } } type Service struct {} func (s *Service) Hello(ctx context.Context, req *hellopb.HelloRequest) (*hellopb.HelloResponse, error) { fmt.Printf("receive request, params -> name: %s, id: %d\n", req.Name, req.Id) return &hellopb.HelloResponse{ Resp: fmt.Sprintf("resp from server, name: %s, id: %d", req.GetName(), req.GetId()), }, nil }
client
package main import ( "context" hellopb "demo/proto" "fmt" "google.golang.org/grpc" "log" ) func main() { conn, err := grpc.Dial("localhost:50052", grpc.WithInsecure()) // conn rpc server if err != nil { log.Fatal(err.Error()) } defer conn.Close() client := hellopb.NewHelloServiceClient(conn) // new a client resp, err := client.Hello(context.Background(), &hellopb.HelloRequest{Name: "Bob", Id: 1000}) // send request, receive response if err != nil { log.Fatal(err.Error()) } fmt.Println(resp.Resp) }
最后先跑server,再跑client,即完成c/s的grpc的简单实践。
总结
使用 gRPC 的 3个 步骤: 1)需要使用 protobuf 定义接口,即编写 .proto 文件; 2)然后使用 protoc 工具配合编译插件编译生成特定语言或模块的执行代码,比如 Go、Java、C/C++、Python 等。 3)分别编写 server 端和 client 端代码,写入自己的业务逻辑。
参考:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2021-07-21 【linux】ubuntu20.04下import sqlite3报错libsqlite3.so.0