共计 4974 个字符,预计需要花费 13 分钟才能阅读完成。
什么是 gRPC?
gRPC 的几种常见模式
在学习 gRPC 的时候,置信大家对于它的四种模式都有理解,咱们来简略回顾一下:
- 简略模式(Simple RPC):这种模式最为传统,即客户端发动一次申请,服务端响应一个数据,这和大家平时相熟的 RPC 没有什么大的区别,所以不再具体介绍。
- 服务端数据流模式(Server-side streaming RPC):这种模式是客户端发动一次申请,服务端返回一段间断的数据流。典型的例子是客户端向服务端发送一个股票代码,服务端就把该股票的实时数据源源不断的返回给客户端。如果是应用咱们容器云性能的同学应该会发现,咱们的容器实时日志流就是应用了这个典型模式。
- 客户端数据流模式(Client-side streaming RPC):与服务端数据流模式相同,这次是客户端源源不断地向服务端发送数据流,而在发送完结后,由服务端返回一个响应。典型的例子是物联网终端向服务器报送数据。
- 双向数据流模式(Bidirectional streaming RPC):顾名思义,这是客户端和服务端都能够向对方发送数据流,这个时候单方的数据能够同时相互发送,也就是能够实现实时交互。典型的例子是聊天机器人。
接下来咱们通过一个小例子来看看 gRPC 具体的应用流程。
假如咱们有一个聊天机器人,现须要减少一个对外提供服务的接口。具体需要为,接口传入参数是一个人名,返回一段内容是“Hello 人名”的音频。如果这个是让你在不应用 gRPC 的状况下,你会怎么做?大家可能会抉择应用 restful api 来实现这个性能,传入人名,返回音频二进制数据。
那么如果应用 gRPC,咱们须要怎么来设计呢?
第一步,须要定义一个接口文档,也就是 proto 文件。在定义内会定义一个 Service,接下来再在 Service 里定义一个 SayHello 的办法。上面定义传入参数,输出 name 返回 message,须要留神 message 是 bytes 类型,即返回的格局是二进制数据。对于 Golang 底层对应的是一个 bytes 数据,对于其余语言可能是字节流或二进制。
syntax = "proto3";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}}
// The request message containing the user's name.
message HelloRequest {string name = 1;}
// The response message containing the greetings
message HelloReply {bytes message = 1;
定义实现后,下一步就是应用 protoc 命令行工具生成代码。下图左侧是初始化的我的项目,你会发现,有一个独自的目录 protoc,寄存了 hello.proto 这个文件,这个文件就是后面定义好的。
下图右侧是主动生成代码后的我的项目构造,生成了一个 pkg/helloworld 的包,外面有一个 hello.pb.go,关上这个文件,你会发现方才定义的 proto 曾经被翻译成了 Go 语言。具体 protoc 命令行工具如何应用,能够自行搜寻下,这里不再过多开展。
定义好了 proto 文件,以及生成了相应的 package 代码,下一步咱们就能够编写业务逻辑了。
Hello gRPC – 服务端业务代码
import (
"google.golang.org/grpc"
pb "grpc-hw/pkg/helloworld"
)
// server is used to implement helloworld.GreeterServer.
type server struct {pb.UnimplementedGreeterServer}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {log.Printf("Received: %v", in.GetName())
tempFile := "srv.wav"
err := exec.Command("flite", "-t", "Hello"+in.GetName(), "-o", tempFile).Run()
if err != nil {return nil, fmt.Errorf("make audio failed: %v", err)
}
data, _ := ioutil.ReadFile(tempFile)
return &pb.HelloReply{Message: data}, nil
}
func main() {lis, _ := net.Listen("tcp", port)
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
s.Serve(lis)
}
在服务端侧,须要实现 SayHello 的办法来满足 GreeterServer 接口的要求。SayHello 办法的传入参数,是在 proto 文件中定义的 HelloRequest,传出参数,是在 proto 文件中定义的 HelloReply,以及一个 error。
业务逻辑也比较简单,获取 HelloRequest 中 Name 字段,而后通过命令行行工具转换成对应的音频,将 bytes 数组存在在 HelloReply 中返回。
Hello gRPC – 客户端业务代码
func main() {flag.StringVar(&address, "addr", address, "server address")
flag.StringVar(&name, "name", "world", "name")
flag.Parse()
// Set up a connection to the server.
conn, _ := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
if err != nil {log.Fatalf("could not greet: %v", err)
}
tempFile := "cli.wav"
ioutil.WriteFile(tempFile, r.Message, 0666)
exec.Command("afplay", tempFile).Run()}
咱们再来看一下如何实现 Client。首先,建设一个 gRPC 的连贯,并初始化 GreeterClient,而后间接调用下 GreeterClient 的 SayHello 的办法,将把返回后果另存为一个文件,通过播放器播放就能够了。
总体而言,整个应用过程很简略,并且非常容易上手,让开发能够更加关注在客户端、服务端业务的实现,不必操心传输层的事件。
gRPC 的应用总结
通过刚刚的小例子,咱们来总结一下 gRPC 的应用:
- 定义好接口文档
- 工具生成服务端 / 客户端代码
- 服务端补充业务代码
- 客户端建设 gRPC 连贯后,应用主动生成的代码调用函数
- 编译、运行
以上 5 步就是 gRPC 的简略应用办法了。
gRPC 与 Protobuf
接下来咱们来聊聊 gRPC 跟 Protobuf 之间的分割,当然在这之前咱们须要先晓得 Protobuf 是什么。
Protobuf
Protobuf 是一个语言无关、平台无关的可扩大的结构化数据序列化计划。大家可能会感觉它跟 JSON 如同没什么区别,性能上看起来是一样的,但像上文那样去定义 SayHello 的操作,JSON 是做不到的,JSON 只能定义一个申请体或者一个返回体,没法定义一个办法,然而 Protobuf 是能够的。
Protobuf 多用于协定通信、数据存储和其余更多用途。它是一个比拟灵便的、高效的、自动化的结构化数据序列机制,然而更小,更快并且更简略。一旦定义好数据如何结构,就能够应用非凡生成的代码来轻易地读写结构化数据,无需关怀用什么语言来实现。你甚至能够更新数据结构而不突破已部署的应用 ” 旧有 ” 格局编译的程序。
上图是 Protobuf 与 JSON 以及 JSON stream 三者之间的性能比拟,能够显著的看到在解码的时候 Protobuf 比其余两项快了不只一星半点。
上图中,咱们能够看到 Protobuf 还是有一些毛病的,比方浏览器的反对没有其余几个反对的好。然而,在数据安全方面,因为传输过程中采纳的是加密压缩后的字节流,个别无奈间接查看,安全性十分好。以及在处理速度方面,因为编解码效率很高使得整体吞吐量有了显著晋升。还有一点,定义方法,这个是其余两种序列化协定所做不到的。
gRPC 跟 Protobuf 的分割
尽管每次 gRPC 与 Protobuf 都是同时呈现的,然而其实两者之间并没有很深的分割。只是因为两者都是由 Google 开发的,和 gRPC 自身负载无关,在应用时也能够抉择 JSON 等等,然而思考到 Protobuf 有定义方法的劣势,在微服务里还是很举荐应用的。
gRPC vs Restful API
上图是 gRPC 与 Restful API 的比照,平时咱们可能更多应用 Restful API。但从图上能够看到,因为 gRPC 用的是 Protobuf,自身就比拟小所以很快,不像 JSON 体积比拟大、比较慢。另外,gRPC 是加载 HTTP/2 下面的,提早比拟低,也因为 HTTP/2 反对链接复用,这就能够让多个 stream 共用一个连贯,从而进一步晋升速度。比照 Restful 则应用的是 HTTP 1.1,提早比拟高,而且在个别状况下,每次申请都须要建一下新的连贯。
gRPC 是双向的。什么是双向呢?比方咱们平时做 Restful,都是从客户端到服务端,然而服务端没方法间接被动向客户端发送信息,gRPC 则能够。gRPC 也反对流,Restful 只反对 Request/Response 这样的机制。gRPC 是面向 API 的,没有限度,也面向增删改查。gRPC 能够通过 Protobuf 间接生成代码,而 Restful 须要依赖第三方。
另外 gRPC 反对 RPC 能够调用服务器上的一些办法,而 Restful 是基于 HTTP 的语法,很多货色须要本人去定义。这方面置信大家都有感触,比方 REST 定义 post/put/delete/get 时,因为每个人都有本人的习惯,所以合作时须要沟通探讨进行指定。然而 gRPC 就不须要了,它反对定义函数式方法,不须要大家去思考如何设计语法。
以上就是 gRPC 跟 Restful API 的比照。
引入 gRPC 须要思考哪些问题?
那么当咱们引入 gRPC 的时候须要思考什么呢?以下几点必定是不可避免的思考项:
- 是否能够满足以后需要
- 性能如何
- 连贯异样断开后,是否须要客户端重试
- TCP 连贯是否能够复用
- 业务层改变是否足够便当
- 业务前期迭代是否会呈现问题,如何防止
这个也是咱们引入一项新的货色时,往往须要思考到的问题。
回顾与总结
从抉择 gRPC 到整个我的项目落地,以及当初上线后失常应用。整个过程中,我对于我的项目的思考能够包含了过来、当初和将来三个阶段。
对我而言,过来就是要去看我抉择的这个货色,用的人多不多,欠缺水平怎么样了?而当初则是要联合我的项目,看看合不适合,能不能应用。当然不能思考到能应用就完结,咱们还须要思考这个我的项目在将来的 3-5 年的倒退,你引入它后在这个工夫内需不需要大的变动。这个是十分重要的,尽管咱们当初常说麻利开发,也常常会进行很多的调整,然而在相似 gRPC 这种底层根底来说,是固定的。
以上就是我明天的全副分享内容,讲的比较简单,心愿能带给大家一些播种。
举荐浏览
秋天的第一份“干货”I Referer 防盗链,为什么少了个字母 R?
“网页内容无法访问”可能是跨域谬误!