大家好,我是 frank。 欢迎大家点击标题下方蓝色文字「Golang 语言开发栈」关注公众号。 设为星标,第一时间接收推送文章。 文末扫码,大家一起学 Golang 语言。
01
介绍
在之前的两篇文章中,我们已经介绍了使用 gRPC 创建 RPC 应用的前导知识。我们了解到 gRPC 支持多语言,本文我们介绍在 Golang 语言中怎么使用 gRPC。
02
准备工作
既然我们要介绍 gRPC 怎么在 Golang 语言中使用,那么我们必须搭建 Golang 开发环境。这部分内容比较简单,本文就不再赘述了,如果有读者朋友对这块内容不清楚,建议阅读 Golang 官网文档。
此外,我们还需要安装接口设计语言 Protocol buffer 的编译器 protoc,我们在之前的文章「Protobuf - 更小、更快、更简单的交互式数据语言」中也已经介绍过 protoc 的安装方法,本文就不再赘述了,如果有需要了解的读者朋友,可以翻阅一下这篇文章。
最后,我们介绍一下 protoc 编译生成 pb 文件需要使用的插件 protoc-gen-go
和 protoc-gen-go-grpc
。插件安装方式,具体如下:
- 执行
go install
命令安装插件
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1
- 修改 PATH
$ export PATH="$PATH:$(go env GOPATH)/bin"
完成以上两步之后,我们就可以使用 protoc 编译 .proto
文件,生成 pb 文件了。
03
编写 .proto
文件和生成 pb 文件
在 Golang 语言中使用 gRPC,首先编写 .proto
文件,然后使用 protoc 编译 .proto
文件生成 pb 文件,最后编写剩余的 Golang 代码。
接口设计语言 protobuf,在之前的文章 「Golang 语言 gRPC 使用的接口设计语言 protobuf」 中也已经介绍过了,本文不再赘述,如果有需要了解的读者朋友,可以翻阅一下这篇文章。
示例代码:
- 编写
.proto
文件。
syntax = "proto3";
option go_package = "advanced_go/lesson06/proto/greeter";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
- 使用 protoc 编译
.proto
文件,生成 pb 文件。
$ protoc --go_out=. --go_opt=paths=source_relative
--go-grpc_out=. --go-grpc_opt=paths=source_relative
proto/helloworld.proto
04
编写服务端和客户端 Golang 代码
我们在之前的文章中介绍过 gRPC 是什么,接下来,我们通过示例代码介绍在 Golang 语言中怎么使用 gRPC,本文先来介绍使用 gRPC 的编码流程,限于篇幅,关于 gRPC 的更多使用方法,后续会新开篇文章介绍。
首先使用接口设计语言 protobuf 的编译器 protoc
、protoc-gen-go
和 protoc-gen-go-grpc
插件生成 pb 文件,我们通过查看生成的 pb 文件,可以看到 protoc 为我们自动生成结构体、接口和方法等 Golang 代码。
接下来,我们只需把剩余的 Golang 代码写完就可以了,具体实现如下:
服务端示例代码:
代码语言:javascript复制const (
port = ":50051"
)
type server struct {
pb.UnimplementedGreeterServer
}
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " in.GetName()}, nil
}
func main () {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
阅读上面这段代码,我们使用 Golang 语言编写了 SayHello 方法,该方法实际上就是 pb 文件中自动生成的 SayHello 方法的具体实现,对应自动生成的 pb 文件 helloworld_grpc.pb.go
中的代码如下:
// UnimplementedGreeterServer must be embedded to have forward compatible implementations.
type UnimplementedGreeterServer struct {
}
func (UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) {
return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
}
在 main 函数中,我们使用 grpc 调用 NewServer 函数创建一个服务,然后使用 pb 文件中的 RegisterGreeterServer 函数注册服务,对应自动生成的 pb 文件 helloworld_grpc.pb.go
中的代码如下:
func RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) {
s.RegisterService(&Greeter_ServiceDesc, srv)
}
客户端示例代码:
代码语言:javascript复制const(
address = ":50051"
defaultName = "word"
)
func main () {
conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
name := defaultName
if len(os.Args) > 1 {
name = os.Args[1]
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())
}
阅读上面这段代码,我们使用 pb 文件中的 NewGreeterClient 方法创建一个客户端,然后就可以使用创建的客户端直接调用服务端的 SayHello 方法,对应自动生成的 pb 文件 helloworld_grpc.pb.go
中的代码如下:
type GreeterClient interface {
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
}
type greeterClient struct {
cc grpc.ClientConnInterface
}
func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient {
return &greeterClient{cc}
}
func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
out := new(HelloReply)
err := c.cc.Invoke(ctx, "/Greeter/SayHello", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
编写完服务端和客户端代码,接下来,我们分别启动服务端和客户端,执行结果如下:
代码语言:javascript复制go run grpc_server/main.go
2021/09/11 23:02:59 server listening at [::]:50051
2021/09/11 23:03:23 Received: word
2021/09/11 23:03:31 Received: frank
go run grpc_client/main.go
2021/09/11 23:03:23 Greeting: Hello word
go run grpc_client/main.go frank
2021/09/11 23:03:31 Greeting: Hello frank
05
总结
本文我们介绍在 Golang 语言中怎么使用 gRPC,为了方便读者朋友们理解,文章通过一个简单示例从零到一的实现,介绍了在 Golang 语言中使用 gRPC 的编码流程。
建议读者朋友们阅读完本文,动手敲一遍示例代码,来进一步加深理解。限于篇幅,关于 gRPC 的更多使用方法,我们后续撰文介绍。
编码流程归纳如下:
- 搭建 Golang 开发环境。
- 安装 protobuf 编译器 protoc 和插件
protoc-gen-go
、protoc-gen-go-grpc
,设置环境变量。 - 初始化项目
go mod init
。 - 编写 protobuf,生成 pb 文件,执行
go mod tidy
整理依赖包。 - 编写剩余 Golang 代码。
推荐阅读:
Golang 语言中的 defer 怎么使用?
Golang 语言怎么使用 net/http 标准库开发 http 应用?
Golang 语言中怎么拦截系统信号和优雅退出 http server?
Golang 语言中 kafka 客户端库 sarama
Golang 语言中 Context 的使用方式
参考资料: https://golang.org/doc/install https://developers.google.com/protocol-buffers/docs/proto3 https://grpc.io/docs/languages/go/