gRPC 简单入门
hanpy

简单的了解一下 gRPC 以及一些简单的使用

一、RPC

RPC(Remote Procedure Call),全称远程过程调用。可以理解为一种协议,是用来屏蔽分布式计算中的各种调用细节,使得可以像本地调用一样来调用一个远程的服务。

PRC调用流程图

image

RPC 和 HTTP 的区别

  1. 概念区分:RPC是一种远程调用协议,里面包括传输协议和序列化协议,传输协议可以是TCP、UDP或者是HTTP等
  2. 从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会屏蔽传输的细节,比如传输时候的编码解码以及网络传输等。

image

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
// 指定proto语法版本
syntax = "proto3";

// 生成的go文件放在那个目录下面以及包名是什么
// . 代表当前目录, service代表生成的go文件的包名是service
option go_package = ".;service";

// 定义服务,名字叫 HelloGrpc
// 服务中有一个rpc方法,名为SayHe11o
// 这个方法会发送一个HelloRequest,然后返回一个HelloResponse。
service HelloGrpc {
rpc SayHello(HelloRequest) returns (HelloResponse);
}

// message 有点类似结构体
// "=" 并不是赋值的意义, 是定义这个变量在 message 中的位置
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

服务端

  1. 创建 gRPC Server 对象
  2. 将 server 注册到 gRPC Server 内部注册中心
  3. 创建 Listen,监听 TCP 端口
  4. 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"
)

// HelloServer 实现定义的方法
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() {
// 创建 gRPC Server
gServer := grpc.NewServer()
// 注册编写的服务
pb.RegisterHelloGrpcServer(gServer, &HelloServer{})
// 监听端口
listen, _ := net.Listen("tcp", ":1234")
// 启动服务
err := gServer.Serve(listen)
if err != nil {
fmt.Printf("error msg")
}
}

客户端

  1. 创建与给定目标的连接交互
  2. 创建 server 的客户端对象
  3. 发送 PRC 请求,等待响应,接收返回结果
  4. 输出结果

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() {
// 连接到 server 端,禁用安全传输,没有加密和验证
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