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)。

  • 将下载的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-grpcprotoc-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

  1. 自动生成:
    • pb.UnimplementedMessageSenderServer 是由 Protocol Buffers (protobuf) 工具自动生成的 gRPC 服务器类型。当定义 gRPC 服务时,protobuf 会根据服务定义生成相应的代码。 它包含了所有 gRPC 方法的默认实现,这些方法通常返回错误,表示未实现。
  2. 目的:
    • 嵌入这个类型可以让自定义的 gRPC 服务器(如 MessageSenderServerImpl)继承默认的方法实现。这意味着你不需要手动实现所有的方法,只需实现你需要的方法即可。

结构体嵌入的好处

  1. 简化代码:
    • 通过嵌入,避免了重复编写所有 gRPC 接口的方法。只需实现特定的方法(如 Send),其余未实现的方法将保留原有的默认行为。
  2. 保持一致性:
    • 使用生成的未实现类型确保遵循 protobuf 定义的一致性。这降低了因手动实现而引起的不一致风险。
  3. 便于扩展:
    • 如果将来需要添加更多的 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 . history