On this page
article
5.Go-gRPC
Go-gRPC介绍
1.gRPC 是什么
gRPC是google开源的一个RPC框架和库,支持多语言之间的通信。底层通信采用的是 HTTP2 协议。gRPC在设计上使用了 ProtoBuf 这种接口描述语言。这种IDL语言可以定义各种服务,google还提供了一种工具 protoc 来编译这种IDL语言,生成各种各样的语言来操作服务。
2.gRPC特点
- 定义服务简单,可以很快的搭建出一个RPC调度的服务
- gRPC是与语言无关,平台无关的。你定义好了一个protobuf协议,就可以用protoc生成不同语言的协议框架
- 使用HTTP2协议,支持双向流。客户端和服务端可以双向通信
3.RPC与RESTful区别是什么
- 在客户端和服务端通信还有一种基于http协议的 RESTful 架构模式,RESTful一般是对于资源的操作,它是名词(资源地址),然后添加一些动作对这些资源进行操作。而RPC是基于函数,它是动词。
- RPC一般基于TCP协议,当然gRPC是基于HTTP2,但它也是比HTTP协议更加有效率和更多特性。RESTful一般都是基于HTTP协议。
- 传输方面:自定义的TCP协议或者使用HTTP2协议,报文体积更小,所以传输效率更高,RESTful一般基于http协议,报文体积大。
- gRPC用的是protobuf的IDL语言,会编码为二进制协议的数据,而RESTful一般是用json的数据格式,所以json格式的编解码更耗时。
4.gRPC通信流程
- 客户端(gRPC Stub)调用A方法,发起RPC调用
- 对请求信息使用Protobuf进行对象序列化压缩(IDL)
- 服务端(gPRC Server)接收到请求后,解码请求体,进行业务逻辑处理并返回。
- 对响应结果使用Protobuf进行对象序列化压缩(IDL)
- 客户端接受到服务端响应,解码请求体。回调被调用的A方法,唤醒正在等待响应(阻塞)的客户端调用并返回响应结果
5.gRPC环境安装
5.1安装 protobuf
从官方仓库:https://github.com/google/protobuf/releases 下载适合你平台的预编译好的二进制文件(protoc-<version>-<platform>.zip
)。
- 适用Windows 64位protoc-3.20.1-win64.zip
- 适用于Mac Intel 64位protoc-3.20.1-osx-x86_64.zip
- 适用于Mac ARM 64位protoc-3.20.1-osx-aarch_64.zip
- 适用于Linux 64位protoc-3.20.1-linux-x86_64.zip
- 将下载的zip解压到指定位置,这里以mac系统为例
fei@feideMBP protoc % pwd
/usr/local/protoc
fei@feideMBP protoc % tree -L 2 ./
./
├── bin
│ └── protoc
└── include
├── github.com
└── google
5 directories, 1 file
fei@feideMBP protoc % protoc --version
libprotoc 3.20.3
fei@feideMBP protoc % head -n1 ~/.zshrc
export PATH=$PATH:/Volumes/data/go/bin:/usr/local/protoc/bin:/Volumes/data/go/pkg/bin
- 还需要安装protobuf的golang编译器插件
protoc-gen-go
,用于生成go文件安装protoc-gen-go-grpc
和protoc-gen-go
,因为protoc没有内置go生成器,想实现.proto→.go
的转换的话
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
- 验证安装
fei@feideMBP protoc % protoc-gen-go-grpc --version
protoc-gen-go-grpc 1.2.0
fei@feideMBP protoc % protoc-gen-go --version
protoc-gen-go v1.28.1
6.使用protoc示例
6.1目录结构
./
├── client
│ └── main.go //客户端代码
├── go.mod
├── go.sum
├── pb //pb生成的文件
│ ├── message.pb.go
│ └── message_grpc.pb.go
├── proto //proto文件
│ └── message.proto
├── server //服务端入口
│ └── main.go
└── serverImpl //服务端业务逻辑
└── MassageSendServerImpl.go
6.2proto文件定义
// 定义proto的版本
syntax = "proto3";
// ../pb定义了生成的go文件的位置,pb是go文件的包名
option go_package = "../pb;pb";
// 定义响应数据
message MessageReply {
string response_something = 1;
}
// 定义请求数据
message MessageReq {
string say_something = 1;
}
//定义了一个service,有一个rpc方法
service MessageSender {
rpc Send(MessageReq) returns (MessageReply){}
}
6.3pb文件生成
//生成数据结构相关pb文件 req resp
fei@feideMBP proto % protoc --go_out=. message.proto
//生成方法相关pb文件
fei@feideMBP proto % protoc --go-grpc_out=. message.proto
fei@feideMBP proto % ls -l ../pb
total 24
-rw-r--r-- 1 fei admin 6540 Oct 3 01:10 message.pb.go
-rw-r--r-- 1 fei admin 3614 Oct 3 01:16 message_grpc.pb.go
6.4逻辑实现
serverImpl/MassageSendServerImpl.go
package serverImpl
import (
"context"
"log"
"test/pb"
)
// 定义结构体: MessageSenderServerImpl 结构体嵌入了 pb.UnimplementedMessageSenderServer。这是 gRPC 自动生成的类型,用于提供默认的未实现方法,便于后续实现。
type MessageSenderServerImpl struct {
*pb.UnimplementedMessageSenderServer
}
func (MessageSenderServerImpl) Send(ctx context.Context, req *pb.MessageReq) (*pb.MessageReply, error) {
log.Printf("Receive Message:%s", req.GetSaySomething())
return &pb.MessageReply{
ResponseSomething: req.GetSaySomething() + "是笨蛋",
}, nil
}
pb.UnimplementedMessageSenderServer
- 自动生成:
- pb.UnimplementedMessageSenderServer 是由 Protocol Buffers (protobuf) 工具自动生成的 gRPC 服务器类型。当定义 gRPC 服务时,protobuf 会根据服务定义生成相应的代码。 它包含了所有 gRPC 方法的默认实现,这些方法通常返回错误,表示未实现。
- 目的:
- 嵌入这个类型可以让自定义的 gRPC 服务器(如 MessageSenderServerImpl)继承默认的方法实现。这意味着你不需要手动实现所有的方法,只需实现你需要的方法即可。
结构体嵌入的好处
- 简化代码:
- 通过嵌入,避免了重复编写所有 gRPC 接口的方法。只需实现特定的方法(如 Send),其余未实现的方法将保留原有的默认行为。
- 保持一致性:
- 使用生成的未实现类型确保遵循 protobuf 定义的一致性。这降低了因手动实现而引起的不一致风险。
- 便于扩展:
- 如果将来需要添加更多的 gRPC 方法,只需在 pb 文件中定义新方法并重新生成代码,无需修改现有的实现。
server/main.go
package main
import (
"google.golang.org/grpc"
"net"
"test/pb"
"test/serverImpl"
)
func main() {
//new一个grpc服务
gser := grpc.NewServer()
//注册服务
pb.RegisterMessageSenderServer(gser, serverImpl.MessageSenderServerImpl{})
//启动gRPC
tcplis, err := net.Listen("tcp", ":8001")
if err != nil {
panic("tcp启动失败:" + err.Error())
}
err = gser.Serve(tcplis)
if err != nil {
panic("grpc启动失败:" + err.Error())
}
}
pb.RegisterMessageSenderServer(gser, serverImpl.MessageSenderServerImpl{})
注册服务:
pb.RegisterMessageSenderServer
是由protobuf
自动生成的函数,用于将实现的服务注册到 gRPC 服务器中。- 第一个参数是 gser,表示要注册服务的 gRPC 服务器实例。
- 第二个参数是一个具体的服务实现实例,这里使用的是
serverImpl.MessageSenderServerImpl{}
,它实现了 gRPC 服务的相关方法(如前面提到的 Send 方法)。
测试
fei@feideMBP server % go run main.go
2024/10/03 03:33:52 Receive Message:虞锋
6.5客户端实现
package main
import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"log"
"test/pb"
)
func main() {
//此行尝试连接运行在 localhost 上的 gRPC 服务器,端口为 8001,并使用不安全凭据。
dial, err := grpc.Dial("localhost:8001", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
panic("gRPC连接失败" + err.Error())
}
log.Printf("gRPC建联成功")
defer dial.Close()
//New一个gRPC Client对象
msc := pb.NewMessageSenderClient(dial)
send, err := msc.Send(context.Background(), &pb.MessageReq{
SaySomething: "虞锋",
})
if err != nil {
panic("gRPC请求失败" + err.Error())
}
log.Printf("gRPC reply:%s", send.ResponseSomething)
}
测试
//启动服务端
fei@feideMBP server % go run main.go
2024/10/03 03:59:09 Receive Message:虞锋
//启动客户端
fei@feideMBP client % go run main.go
2024/10/03 04:00:04 gRPC建联成功
2024/10/03 04:00:04 gRPC reply:虞锋是笨蛋
Last updated 04 Oct 2024, 05:06 +0800 .