简单的了解一下 gRPC
以及一些简单的使用
一、RPC
RPC(Remote Procedure Call)
,全称远程过程调用。可以理解为一种协议,是用来屏蔽分布式计算中的各种调用细节,使得可以像本地调用一样来调用一个远程的服务。
PRC调用流程图
RPC 和 HTTP 的区别
- 概念区分:RPC是一种远程调用协议,里面包括传输协议和序列化协议,传输协议可以是TCP、UDP或者是HTTP等
- 从OSI七层网络模型来区分,HTTP是应用层协议,RPC常用的TCP传输协议是在传输层
二、gRPC
2.1、 gRPC 介绍
官网:https://grpc.io
中文文档:http://doc.oschina.net/grpc
gRPC
是一个高性能、开源和通用的 RPC 框架,是基于 HTTP2
协议标准设计开发,默认采用 Protocol Buffers
数据序列化协议,支持多种开发语言。gRPC
提供了一种简单的方法来精确的定义服务,并且可以为客户端和服务端自动生成可靠的功能库。
在gRpc中,调用方一般称为 client,被调用方称为 server。gRPC 是基于定义服务的思想,简单理解就是通过某种方式来描述一个服务,这种描述是和语言无关的。在这个服务定义的过程中,描述了提供的服务服务名是什么,有那种方法可以调用,需要那些入参,以及返回什么。
在服务定义好之后, server 需要按照约定来实现接口,client 只需要直接调用定义好的方法就能拿到预期的返回结果,gRPC会屏蔽传输的细节,比如传输时候的编码解码以及网络传输等。
gRPC 是支持多语言的,是数据传输的过程中使用了 Protocol Buffers
, Protobuf
是它的简称,是谷歌开源的一套成熟的数据结构序列化机制。
可以把 Protobuf 理解为一个代码生成工具和数据序列化工具,定义的方法可以转换成func函数,在发送和请求的时候,这个工具还会完成对应的编码解码的工作。
2.2、Protobuf 安装
<1>. 安装protobuf
github:https://github.com/protocolbuffers/protobuf/releases
Mac 可以使用 brew 来安装
1 2 3 4 5
| $ brew install protobuf
# 验证是否下载成功 $ protoc --version libprotoc 3.21.5
|
<2>. 安装 gRPC 核心库
1 2 3 4 5 6 7 8 9 10 11 12
| $ go get google.golang.org/grpc
go: downloading google.golang.org/grpc v1.54.0 go: downloading golang.org/x/net v0.8.0 go: downloading google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f go get: added github.com/golang/protobuf v1.5.2 go get: added golang.org/x/net v0.8.0 go get: added golang.org/x/sys v0.6.0 go get: added golang.org/x/text v0.8.0 go get: added google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f go get: added google.golang.org/grpc v1.54.0 go get: added google.golang.org/protobuf v1.28.1
|
<3>. 插件安装
上面安装的是 protobuf 编译器,还需要安装 go 对应的代码生成工具
1 2
| $ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
|
有一些老的教程里面是获取 github.com/golang/protobuf/protoc-gen-go,这个是旧版本的
安装之后会生成在 GOPATH/bin 下面,需要加一下环境变量,在 ~/.zshrc
里面加入下面的环境变量
1
| export PATH="$PATH:$(go env GOPATH)/bin"
|
验证安装是否成功
1 2 3 4 5
| $ protoc-gen-go --version protoc-gen-go v1.28.1
$ protoc-gen-go-grpc --version protoc-gen-go-grpc 1.2.0
|
2.3、Proto 文件编写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| syntax = "proto3";
option go_package = ".;service";
service HelloGrpc { rpc SayHello(HelloRequest) returns (HelloResponse); }
message HelloRequest { string requestName = 1; }
message HelloResponse { string respName = 1; }
|
生成Go代码
1 2
| # 同时生成hello.pb.go 和 hello_grpc.pb.go 文件 $ protoc --go-grpc_out=. --go_out=. hello.proto
|
2.4、客户端和服务端代码编写
目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| grpc-basic ├── go-client │ ├── main.go │ └── proto │ ├── hello.pb.go │ ├── hello.proto │ └── hello_grpc.pb.go ├── go-server │ ├── main.go │ └── proto │ ├── hello.pb.go │ ├── hello.proto │ └── hello_grpc.pb.go ├── go.mod └── go.sum
|
服务端
- 创建 gRPC Server 对象
- 将 server 注册到 gRPC Server 内部注册中心
- 创建 Listen,监听 TCP 端口
- gRPC Server 开始 listen.Accept,直到 Stop
go-server/main.go
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 32
| package main
import ( "context" "fmt" "google.golang.org/grpc" pb "grpc-basic/go-server/proto" "net" )
type HelloServer struct { pb.UnimplementedHelloGrpcServer }
func (h *HelloServer) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) { return &pb.HelloResponse{RespName: "hello" + req.RequestName}, nil }
func main() { gServer := grpc.NewServer() pb.RegisterHelloGrpcServer(gServer, &HelloServer{}) listen, _ := net.Listen("tcp", ":1234") err := gServer.Serve(listen) if err != nil { fmt.Printf("error msg") } }
|
客户端
- 创建与给定目标的连接交互
- 创建 server 的客户端对象
- 发送 PRC 请求,等待响应,接收返回结果
- 输出结果
go-client/main.go
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
| package main
import ( "context" "fmt" "google.golang.org/grpc" pb "grpc-basic/go-client/proto" )
func main() { conn, err := grpc.Dial("127.0.0.1:1234", grpc.WithInsecure()) if err != nil { fmt.Printf("error") } defer conn.Close()
client := pb.NewHelloGrpcClient(conn)
resp, err := client.SayHello(context.TODO(), &pb.HelloRequest{RequestName: "hanpy"}) if err != nil { fmt.Printf("resp error") } fmt.Println("返回:", resp.GetRespName()) }
|
运行测试
1 2 3 4 5 6
| # 启动服务端 $ go run grpc-basic/go-server/main.go
# 客户端 $ go run grpc-basic/go-client/main.go 返回: hellohanpy
|