文章目录
- 1. 简介
- 2. 安装gRPC
- 2.1. 下载protobuf
- 2.2. 安装grpc核心库
- 2.3. 安装protoc的Go插件
- 2.4. 检查
- 3. 入门示例
- 4. proto文件介绍
- 5. 服务端代码编写
- 6. 客户端代码编写
- 7. 认证以及安全传输
1. 简介
在 gRPC 中,客户端应用程序可以像本地对象一样直接调用不同机器上的服务器应用程序上的方法,从而使您更轻松地创建分布式应用程序和服务。与许多 RPC 系统一样,gRPC 基于定义服务的思想,指定可以远程调用的方法及其参数和返回类型。在服务器端,服务器实现这个接口并运行一个gRPC服务器来处理客户端调用。在客户端,客户端有一个存根(在某些语言中简称为客户端),它提供与服务器相同的方法。
gRPC 客户端和服务器可以在各种环境中运行和相互通信,并且可以用 gRPC 支持的任何语言编写。例如,您可以使用 Java 轻松创建 gRPC 服务器,并使用 Go、Python 或 Ruby 编写客户端。
默认情况下,gRPC 使用 Protocol Buffers,这是 Google 成熟的开源机制,用于序列化结构化数据。
gPRC传输的是二进制字节流的数据,我们可以使用 **Protocol Buffers **进行代码的编码和解码的工作,从而用于数据传输。
protocol buffers (ProtoBuf)是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。
2. 安装gRPC
2.1. 下载protobuf
从github下载https://github.com/protocolbuffers/protobuf发布版本然后配置环境变量。
其中:
- bin 目录下的 protoc 是可执行文件。
- include 目录下的是 google 定义的.proto文件,我们import "google/protobuf/timestamp.proto"就是从此处导入。
protoc
2.2. 安装grpc核心库
go get google.golang.org/grpc@latest
2.3. 安装protoc的Go插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
该插件会根据.proto文件生成一个后缀为.pb.go的文件,包含所有.proto文件中定义的类型及其序列化方法。
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
该插件会生成一个后缀为_grpc.pb.go的文件,其中包含:
- 一种接口类型(或存根) ,供客户端调用的服务方法。
- 服务器要实现的接口类型。
上述命令会默认将插件安装到
G
O
P
A
T
H
/
b
i
n
,为了
p
r
o
t
o
c
编译器能找到这些插件,请确保你的
GOPATH/bin,为了protoc编译器能找到这些插件,请确保你的
GOPATH/bin,为了protoc编译器能找到这些插件,请确保你的GOPATH/bin在环境变量中。
2.4. 检查
依次执行以下命令检查一下是否开发环境都准备完毕。
- 确认 protoc 安装完成。
protoc --version
- 确认 protoc-gen-go 安装完成。
protoc-gen-go --version
- 确认 protoc-gen-go-grpc 安装完成。
protoc-gen-go-grpc --version
3. 入门示例
构建如下文件目录
在hello.proto中写入下面代码
syntax = "proto3"; // 版本声明,使用Protocol Buffers v3版本
option go_package = ".;service"; // 指定生成的Go代码在你项目中的导入路径 和 包名
// package pb; // 包名
// 定义服务
service SayHello {
// SayHello 方法
rpc SayHello (HelloRequest) returns (HelloResponse) {}
}
// 请求消息
// 注意这里并不是赋值,而是定义的这个变量在这个message中的位置
message HelloRequest {
string requestName = 1;
// int64 age = 2;
}
// 响应消息
message HelloResponse {
string responseReply = 1;
}
进入相应文件目录执行以下命令生成go代码和grpc的相关代码
protoc --go_out=. hello.proto //.代表输出目录 hello.proto表示给哪个文件生成代码
protoc --go-grpc_out=. hello.proto
接下来就可以根据业务需求补充实现生成代码文件的内容。
4. proto文件介绍
- message
protobuf 中定义一个消息类型是通过关键字 message字段指定的。
消息就是需要传输的数据格式的定义。
message 关键字类似于C++中的class,JAVA中的class,go中的struct。
在消息中承载的数据分别对应于每一个字段,其中每个字段都有一个名字和一种类型。
- field
required:消息体中必填字段,不设置会导致编码异常。在protobuf2中使用,在protobuf3中被删去。
optional:消息体中可选字段。protobuf3没有了required,optional等说明关键字,都默认为optional。
repeated:消息体中可重复字段,重复的值的顺序会被保留,在go中重复的会被定义为切片。
- 消息号
下面定义中的1,必须唯一。
string requestName = 1;
- 嵌套消息
可以在消息中嵌套定义消息
message Person {
message HelloRequest {
string requestName = 1;
}
string name = 1;
int32 id = 2;
bool has_ponycopter = 3;
}
- 服务定义
如果想要将消息类型用到RPC系统,可以在.proto文件中定义一个RPC服务接口,protocol buffer会根据所选择的不同语言生成服务接口的代码和存根。
service SearchService {
# rpc 服务函数名 (参数) 返回 (返回参数)
rpc Search(SearchRequest) returns (SearchResponse)
}
5. 服务端代码编写
基于生成的gprc服务端代码重写业务逻辑。
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
pb "grpc_demo/hello-server/proto"
"net"
)
type server struct {
pb.UnimplementedSayHelloServer
}
func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
fmt.Printf("服务端被 %v 调用", req.RequestName)
return &pb.HelloResponse{ResponseReply: "hello" + req.RequestName}, nil
}
func main() {
// 开启端口
listen, _ := net.Listen("tcp", ":9090")
// 创建gRPC服务
grpcServer := grpc.NewServer()
// 在rpc服务端中注册我们自己编写的服务
pb.RegisterSayHelloServer(grpcServer, &server{})
//启动服务
err := grpcServer.Serve(listen)
if err != nil {
fmt.Printf("failed to serve: %v", err)
return
}
}
6. 客户端代码编写
// hello-client/main.go
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "grpc_demo/hello-server/proto"
"log"
)
func main() {
// 连接到server端,此处禁用安全传输,没有加密和验证
conn, err := grpc.Dial("127.0.0.1:9090", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
// 建立连接
client := pb.NewSayHelloClient(conn)
// 执行rpc调用
resp, _ := client.SayHello(context.Background(), &pb.HelloRequest{RequestName: "acezsq"})
fmt.Println(resp.GetResponseReply())
}
首先运行服务端,之后运行客户端,可以看到客户端完成了调用服务端的方法。
完整的项目代码文件在:https://github.com/acezsq/grpc_demo
7. 认证以及安全传输
客户端与服务端通信之前,客户端如何知道自己的数据发送给哪一个服务端?反过来,服务端也需要有一种方式确定一下自己的数据要返回给谁。
此处的认证不是用户身份认证而是多个client和多个server之间的识别。
- SSL/TLS认证方式(采用http2协议)
- 基于Token的认证方式(基于安全连接)
- 不采用任何措施的链接,不安全的连接
- 自定义的身份认证