Golang(Go)gRPC:proto、客户端、服务端 的使用

原文链接:https://xiets.blog.csdn.net/article/details/144357277

版权声明:原创文章禁止转载

专栏目录:Golang 专栏(总目录)

gRPC 相关链接:

Go gRPC:

1. gPRC 简介

RPC 是远程过程调用 (Remote Procedure Call) 的缩写形式,gRPC 最初是由 Google 创建的,高性能、开源通用、跨语言和平台 的 RPC 框架。

gRPC 使用 Protocol Buffers 定义服务,Protocol Buffers 是一个强大的二进制序列化工具集和语言。

2. 安装 gRPC

把 proto 文件编译生成 Go 代码,需要安装 Protocol Buffer 编译器 和 两个 Go 插件。

2.1 安装 Protocol Buffer 编译器

安装 Protocol Buffer 编译器安装:

  • Linux 安装:apt install -y protobuf-compiler
  • MacOS 安装:brew install protobuf

其他安装方式,参考:Protocol Buffer Compiler Installation

安装后将得到一个 protoc 命令工具:

$ protoc --help

2.2 安装 Go 插件

需要安装两个 Go 插件:

protoc-gen-go-grpc 工具用于在 gRPC 的 protobuf 定义文件中生成服务端的 Go 语言绑定(name_grpc.pb.go 文件)。

protoc-gen-go 是一个 protoc 插件,用于为 Protocol Buffer 语言的 proto2 和 proto3 版本生成 Go 代码(name.pb.go 文件)。

使用 go install 命令安装这两个插件:

$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

安装后在 ${GOPATH}/bin 目录下将会多出两个命令工具:protoc-gen-go-grpcprotoc-gen-go

运行 protoc 命令生成 Go 代码时,该命令内部将会在 PATH 环境变量中查找 protoc-gen-go-grpcprotoc-gen-go 这两个命令。因此需要确保 ${GOPATH}/bin 路径在 PATH 环境变量中:

export PATH="${PATH}:$(go env GOPATH)/bin"

3. gRPC 使用示例

创建一个名为 project1 的项目:

$ mkdir project1 && cd project1 
$ go mod init xiets.com/project1

后面所有命令都将在 project1/ 目录下执行。

添加 gRPC 依赖模块:

$ go get -u google.golang.org/grpc

3.1 proto 文件定义服务

使用 proto3 语言定义服务,保存到 project1/protos/user.proto 文件:

syntax = "proto3";          // 指定语言版本

// 具体编程语言的参数。
// 生成 Go 代码的导包路径和包名, 必须包含 "." 或 "/"。
// 如果没有指定包名则默认使用路径的最后一个文件夹名作为包名。
// 也可以自定义包名, 自定义包名的格式为在路径后面加上分号和自定义包名, 如: "grpc_service/user;user2"。
option go_package = "grpc_service/user";

// 服务接口 (可以定义多个服务, 编译后映射为一个类/接口)
service UserServ {
    // 服务方法 (可以定义多个方法, 编译后映射为一个类/接口的方法)
    rpc Login (LoginRequest) returns (LoginReply);
}

// 消息对象, 请求参数 (编译后映射为一个类/对象)
message LoginRequest {
    string username = 1;    // 类/对象的字段 (从1开始编号, 最小编号是1, 可以不按数值顺序, 值不能重复)
    string password = 2;    // 一旦消息类型被使用, 字段编号就不能更改, 因为它标识了消息传输格式中的字段, 更改编号相当于删除旧字段再创建新字段
}

// 消息对象 (响应参数)
message LoginReply {
    string message = 1;
}

3.2 编译 proto 文件

生成 Go 代码,参考:https://protobuf.dev/reference/go/go-generated/

编译 proto 文件,生成 客户端 和 服务端 代码:

$ protoc -I./protos \
         --go-grpc_out=. \
         --go_out=. \
         --go-grpc_opt=paths=import \
         --go_opt=paths=import \
         ./protos/user.proto

# -IPATH, --proto_path=PATH
#       在 proto 文件中需要被 import 引用的其他 proto 文件所在目录, 可以多次指定多个。
#       import 操作将在此目录下查找。
#
# --go-grpc_out=OUT_DIR
#       user_grpc.pb.go 源码文件输出目录, 最终输出路径为: "{OUT_DIR}/{paths}/user_grpc.pb.go", 
#       其中 "{paths}" 与 --go-grpc_opt=paths 参数值有关, 详见后面解释。
#
# --go_out=OUT_DIR
#       user.pb.go 源码文件输出目录, 最终输出路径为: "{OUT_DIR}/{paths}/user.pb.go",
#       其中 "{paths}" 与 --go_opt=paths 参数值有关, 详见后面解释。
#
# --go-grpc_opt=paths
# --go_opt=paths
#       源码路径 在 OUT_DIR 目录下的形式, 有两个值: "import"(默认) 和 "source_relative"
#
#       import: 
#           参数值形式: 
#               --go-grpc_opt=paths=import
#               --go_opt=paths=import
#           源码保存路径使用导包路径, 即 go_package 参数定义的多级路径, 最终路径为: "{OUT_DIR}/{go_package}/*.pb.go"
#
#       source_relative: 
#           参数值形式: 
#               --go-grpc_opt=paths=source_relative
#               --go_opt=paths=source_relative
#           源码保存路径使用 proto 文件的相对路径 (相对于 -I 参数指定的目录), 使用此方式, go_package 参数的值只用于确定包名。
#           例如 "-I./protos" 和 "./protos/aa/bb/user.proto", 最终路径为: "{OUT_DIR}/aa/bb/*.pb.go"
#
# ./protos/user.proto
#       要编译的 proto 文件, 可以指定多个, 也支持 "./protos/*.proto" 通配符形式。

protoc 命令的 --xxx_out--xxx_opt 等类型的参数,如果不在 protoc --help 列出的参数中,则将从 PATH 目录下查找名为 protoc-gen-xxx 的插件命令,并把参数值传递给插件,通过插件生成相应的源码。前面安装的两个 Go 插件的作用就在这里。

编译后生成的文件目录结构:

project1
│── protos
│   └── user.proto
│── grpc_service
│   └── user
│       │── user.pb.go
│       └── user_grpc.pb.go
│── go.mod
└── go.sum

3.3 服务端代码

gRPC 服务端代码:project1/server/main.py

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"log"
	"net"
	pb "xiets.com/project1/grpc_service/user"
)

func main() {
	// 创建 TCP 监听
	listen, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	// 创建 gRPC 服务
	var opts []grpc.ServerOption
	grpcServer := grpc.NewServer(opts...)

	// 注册 UserServ 服务实例到 gRPC 服务
	userServ := &UserServ{}
	pb.RegisterUserServServer(grpcServer, userServ)

	// 在指定监听上运行 gGPC 服务 (阻塞至服务终止)
	err = grpcServer.Serve(listen)
	if err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

// UserServ 用于实现 UserServ 服务接口
type UserServ struct {
	pb.UnimplementedUserServServer
}

func (u *UserServ) Login(ctx context.Context, request *pb.LoginRequest) (*pb.LoginReply, error) {
	// 获取请求参数
	username := request.GetUsername()
	password := request.GetPassword()

	// 处理请求 ...
	log.Printf("UserServ.Login: username=%s, password=%s\n", username, password)

	// 构建响应参数
	reply := &pb.LoginReply{
		Message: fmt.Sprintf("Hi: %s", request.GetUsername()),
	}
	return reply, nil
}

启动 gRPC 服务端:

$ go run server/main.go

3.4 客户端代码

gRPC 服务端代码:project1/client/main.go

package main

import (
	"context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"log"
	pb "xiets.com/project1/grpc_service/user"
)

func main() {
	// 创建 gRPC 连接
	addr := "localhost:8080"
	opts := []grpc.DialOption{
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	}
	conn, err := grpc.Dial(addr, opts...)
	if err != nil {
		log.Fatalf("grpc dial error: %v", err)
	}

	// 延迟关闭连接
	defer func(conn *grpc.ClientConn) {
		_ = conn.Close()
	}(conn)

	// 创建 UserServ 客户端实例 (在指定连接上通信)
	client := pb.NewUserServClient(conn)

	// 创建请求对象
	req := &pb.LoginRequest{
		Username: "hello",
		Password: "world",
	}
	// 调用请求, 返回响应
	resp, err := client.Login(context.Background(), req)
	if err != nil {
		log.Fatalf("grpc call error: %v", err)
	}
	// 获取响应参数
	log.Printf("resp: %s\n", resp.GetMessage())
}

运行 gRPC 客户端:

$ go run client/main.go
首先,需要安装gRPCprotobuf。可以使用以下命令: ``` go get -u google.golang.org/grpc go get -u github.com/golang/protobuf/proto go get -u github.com/golang/protobuf/protoc-gen-go ``` 接下来,我们需要定义protobuf文件,其中包含服务的RPC方法和消息格式,例如: ``` syntax = "proto3"; package myservice; message Request { string message = 1; } message Response { string message = 1; } service MyService { rpc SayHello(Request) returns (Response) {} } ``` 然后,使用以下命令将protobuf文件编译为Go代码: ``` protoc --go_out=plugins=grpc:. myservice.proto ``` 这将生成myservice.pb.go文件。 接下来,我们需要实现gRPC服务端客户端。以下是一个简单的示例: 服务端: ``` package main import ( "log" "net" "google.golang.org/grpc" pb "path/to/myservice" ) type myServiceServer struct{} func (s *myServiceServer) SayHello(ctx context.Context, req *pb.Request) (*pb.Response, error) { log.Printf("Received message: %v", req.Message) return &pb.Response{Message: "Hello " + req.Message}, nil } func main() { lis, err := net.Listen("tcp", ":50051") if err != nil { log.Fatalf("failed to listen: %v", err) } s := grpc.NewServer() pb.RegisterMyServiceServer(s, &myServiceServer{}) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } } ``` 客户端: ``` package main import ( "log" "golang.org/x/net/context" "google.golang.org/grpc" pb "path/to/myservice" ) func main() { conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure()) if err != nil { log.Fatalf("failed to connect: %v", err) } defer conn.Close() c := pb.NewMyServiceClient(conn) res, err := c.SayHello(context.Background(), &pb.Request{Message: "World"}) if err != nil { log.Fatalf("failed to call SayHello: %v", err) } log.Printf("Response message: %v", res.Message) } ``` 运行服务端客户端,即可进行gRPC通信。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谢TS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值