[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 端代码,写入自己的业务逻辑。

参考:

posted on   进击的davis  阅读(2634)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有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

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示