grpc
初识protobuffer
|
|
第一个proto
proto文件都需要以.proto作为后缀。
下面就是一个简单的protobuffer,name = 1不是指他的值是1,而是指他在Person中的序号是1.
option go_package = “path;package”
path代表生成的go文件在当前目录,package代表包为first。
|
|
生成go文件
protoc –go_out= . ./*.proto
生成的pb.go文件
|
|
序列化和反序列化
|
|
proto和json的相互转换
首先安装一个包
|
|
|
|
在protobuffer中定义服务
如果你想在RPC(远程过程调用)系统中使用你的消息类型,你可以在一个.proto文件中定义一个RPC服务接口,并且Protocol Buffers编译器将以你选择的语言生成服务接口代码和存根。因此,例如,如果你想定义一个RPC服务,它的方法接受你的SeanchRequest并返回一个SeanchResponse,你可以在你的.proto文件中定义它,如下所示:
|
|
初识rpc
在上述本地过程调用的例子中,我们是在一台计算机上执行了计算机上的程序,完成调用。随着计算机技术的发展和需求场景的变化,有时就需要从一台计算机上执行另外一台计算机上的程序的需求,因此后来又发展出来了RPC技术。特别是目前随着互联网技术的快速迭代和发展,用户和需求几乎都是以指数式的方式在高速增长,这个时候绝大多数情况下程序都是部署在多台机器上,就需要在调用其他物理机器上的程序的情况。
RPC是Remote Procedure Call Protocol单词首字母的缩写,简称为:RPC,翻译成中文叫远程过程调用协议。所谓远程过程调用,通俗的理解就是可以在本地程序中调用运行在另外一台服务器上的程序的功能方法。这种调用的过程跨越了物理服务器的限制,是在网络中完成的,在调用远端服务器上程序的过程中,本地程序等待返回调用结果,直到远端程序执行完毕,将结果进行返回到本地,最终完成一次完整的调用。
需要强调的是:远程过程调用指的是调用远端服务器上的程序的方法整个过程。
rpc的组成
RPC技术在架构设计上有四部分组成,分别是:客户端、客户端存根、服务端、服务端存根。
这里提到了客户端和服务端的概念,其属于程序设计架构的一种方式,在现代的计算机软件程序架构设计上,大方向上分为两种方向,分别是:B/S架构、C/S架构。B/S架构指的是浏览器到服务器交互的架构方式,另外一种是在计算机上安装一个单独的应用,称之为客户端,与服务器交互的模式。
由于在服务的调用过程中,有一方是发起调用方,另一方是提供服务方。因此,我们把服务发起方称之为客户端,把服务提供方称之为服务端。以下是对RPC的四种角色的解释和说明:
- **客户端(Client):**服务调用发起方,也称为服务消费者。
- **客户端存根(Client Stub):**该程序运行在客户端所在的计算机机器上,主要用来存储要调用的服务器的地址,另外,该程序还负责将客户端请求远端服务器程序的数据信息打包成数据包,通过网络发送给服务端Stub程序;其次,还要接收服务端Stub程序发送的调用结果数据包,并解析返回给客户端。
- **服务端(Server):**远端的计算机机器上运行的程序,其中有客户端要调用的方法。
- **服务端存根(Server Stub):**接收客户Stub程序通过网络发送的请求消息数据包,并调用服务端中真正的程序功能方法,完成功能调用;其次,将服务端执行调用的结果进行数据处理打包发送给客户端Stub程序。
rpc的原理
了解完了RPC技术的组成结构我们来看一下具体是如何实现客户端到服务端的调用的。实际上,如果我们想要在网络中的任意两台计算机上实现远程调用过程,要解决很多问题,比如:
- 两台物理机器在网络中要建立稳定可靠的通信连接。
- 两台服务器的通信协议的定义问题,即两台服务器上的程序如何识别对方的请求和返回结果。也就是说两台计算机必须都能够识别对方发来的信息,并且能够识别出其中的请求含义和返回含义,然后才能进行处理。这其实就是通信协议所要完成的工作。
让我们来看看RPC具体是如何解决这些问题的,RPC具体的调用步骤图如下:
在上述图中,通过1-10的步骤图解的形式,说明了RPC每一步的调用过程。具体描述为:
- 1、客户端想要发起一个远程过程调用,首先通过调用本地客户端Stub程序的方式调用想要使用的功能方法名;
- 2、客户端Stub程序接收到了客户端的功能调用请求,将客户端请求调用的方法名,携带的参数等信息做序列化操作,并打包成数据包。
- 3、客户端Stub查找到远程服务器程序的IP地址,调用Socket通信协议,通过网络发送给服务端。
- 4、服务端Stub程序接收到客户端发送的数据包信息,并通过约定好的协议将数据进行反序列化,得到请求的方法名和请求参数等信息。
- 5、服务端Stub程序准备相关数据,调用本地Server对应的功能方法进行,并传入相应的参数,进行业务处理。
- 6、服务端程序根据已有业务逻辑执行调用过程,待业务执行结束,将执行结果返回给服务端Stub程序。
- 7、服务端Stub程序**将程序调用结果按照约定的协议进行序列化,**并通过网络发送回客户端Stub程序。
- 8、客户端Stub程序接收到服务端Stub发送的返回数据,**对数据进行反序列化操作,**并将调用返回的数据传递给客户端请求发起者。
- 9、客户端请求发起者得到调用结果,整个RPC调用过程结束。
go实现rpc
在Go语言官方网站的pkg说明中,提供了官方支持的rpc包,具体链接如下:https://golang.org/pkg/net/rpc/。官方提供的rpc包完整的包名是:net/rpc。根据官方的解释,rpc包主要是提供通过网络访问一个对象方法的功能。
本节课,我们就来学习如何使用go语言官方提供的RPC包实现RPC调用编码。
服务定义和暴露
|
|
上述代码是go语言官方给出的对外暴露的服务方法的定义标准,其中包含了主要的几条规则,分别是:
- 1、对外暴露的方法有且只能有两个参数,这个两个参数只能是输出类型或内建类型,两种类型中的一种。
- 2、方法的第二个参数必须是指针类型。
- 3、方法的返回类型为error。
- 4、方法的类型是可输出的。
- 5、方法本身也是可输出的。
我们举例说明:假设目前我们有一个需求,给出一个float类型变量,作为圆形的半径,要求通过RPC调用,返回对应的圆形面积。具体的编程实现思路如下:
|
|
注册服务和监听
|
|
经过服务注册和监听处理,RPC调用过程中的服务端实现就已经完成了。接下来需要实现的是客户端请求代码的实现。
客户端调用
上述的调用方法核心在于client.Call方法的调用,该方法有三个参数,第一个参数表示要调用的远端服务的方法名,第二个参数是调用时要传入的参数,第三个参数是调用要接收的返回值。
|
|
客户端异步调用
|
|
多参数传递
|
|
|
|
rpc和protobuffer
首先可以将call函数中的req和resp都用protobuffer生成。其他步骤类似
|
|
|
|
服务器
|
|
客户端
|
|
初识grpc
得到grpc
|
|
grpc的使用
我们想要实现的是通过gRPC框架进行远程服务调用,首先第一步应该是要有服务。利用之前所掌握的内容,gRPC框架支持对服务的定义和生成。 gRPC框架默认使用protocol buffers作为接口定义语言,用于描述网络传输消息结构。除此之外,还可以使用protobuf定义服务接口。
|
|
gRPC编译支持
如果定义的.proto文件,如本案例中所示,定义中包含了服务接口的定义,而我们想要使用gRPC框架实现RPC调用。开发者可以采用protocol-gen-go库提供的插件编译功能,生成兼容gRPC框架的golang语言代码。只需要在基本编译命令的基础上,指定插件的参数,告知protoc编译器即可。具体的编译生成兼容gRPC框架的服务代码的命令如下:
|
|
pb.go生成的客户端方法代码,给了生成客户端的方法,给了调用服务的方法
|
|
pb.go生成的服务器方法代码,提供了注册已实现OrderService接口的方法,定义了一个没有实现方法的类。
|
|
实现rpc服务
在上面的proto文件中定义的方法,我们可以发现并没有方法体,类似于接口,我们要实现这个方法。
对于实现的方法,参数会对应的改变。
|
|
grpc实现服务器
这里由于我的包名设置成了grpc(方便复习),所以将google的grpc起别名成了Grpc
|
|
grpc实现客户端
|
|
grpc的流模式
在上节课内容中,我们学习了使用gRPC框架实现服务的调用编程。在gRPC框架中,诸如上节课我们学习的在客户端与服务端之间通过消息结构体定义的方式来传递数据,我们称之为“单项RPC”,也称之为简单模式。除此之外,gRPC中还有数据流模式的RPC调用实现,这正是我们本节课要学习的内容。
服务器流形式
定义proto文件
这一次的proto文件和上一个Cs模式的有些许不同,return的带有stream
|
|
通过stream修饰的方式表示该接口调用时,服务端会以数据流的形式将数据返回给客户端。
|
|
生成的文件也会有些许不同。
对于方法方面,客户端如下,发现相较于上面的非流传输,多了一个recv函数,这个应该时用来接受服务器传过来的OrderInfo
|
|
服务器这边也是,多了一个send方法。
|
|
服务器
由于服务器用的流形式回传,所以参数中带有OrderService_GetOrderInfoServer这个含有send方法的接口,用于传输流。
|
|
|
|
服务的监听与处理与前文所学内容没有区别,依然是相同的步骤:
|
|
客户端
服务端使用Send方法将数据以流的形式进行发送,客户端可以使用Recv()方法接收流数据,因为数据流失源源不断的,因此使用for无限循环实现数据流的读取,当读取到io.EOF时,表示流数据结束。客户端数据读取实现如下
先还是拨号到指定端口,然后就可以创建一个client调用服务,服务会返回一个OrderService_GetOrderInfoClient,该接口实现了Recv,我们直接调用Recv方法接受信息即可。
|
|
客户端流模式
定义proto文件
|
|
生成的客户端文件
|
|
SendAndClose和Recv方法是客户端流模式下的服务端对象所拥有的方法。
服务器
|
|
Send和CloseAndRecv是客户端流模式下的客户端对象所拥有的方法。
服务器实现
由于信息全在流中,所以不需要req了。
|
|
监听服务器依然差不多
|
|
客户端实现
|
|
这个交互类似于,服务器会通过recv接收然后进行业务处理,然后通过sendAndClose进行回传结果,client通过send发送流信息,然后通过CloseAndRecive接受消息。
双向流模式
定义proto文件
|
|
客户端
|
|
服务器
|
|
可以发现无论是服务器还是客户端都增加了recv和send方法。
服务器实现
参数是实现了recv和send的接口,可以发现recv和send都放到了同一个for中,如果读取结束自动break
|
|
客户端实现
|
|
grpc验证
gRPC中默认支持两种授权方式,分别是:SSL/TLS认证方式、基于Token的认证方式。
SSL全称是Secure Sockets Layer,又被称之为安全套接字层,是一种标准安全协议,用于在通信过程中建立客户端与服务器之间的加密链接。 TLS的全称是Transport Layer Security,TLS是SSL的升级版。在使用的过程中,往往习惯于将SSL和TLS组合在一起写作SSL/TLS。 简而言之,SSL/TLS是一种用于网络通信中加密的安全协议。
基于token
在gRPC中,允许开发者自定义自己的认证规则,通过
|
|
可以看到我们需要实现一个PerRPCCredentials接口
|
|
我们自己创建一个结构体实现一下吧
|
|
以后客户端可以通过token来访问
|
|
服务器
这里的服务器通过metadata.FromIncomingContext(ctx),获得key,secret,然后进行校验。
|
|
客户端
要注意 grpc.Dial(“localhost:8081”, grpc.WithPerRPCCredentials(tk),grpc.WithInsecure()),如果只用token需要用grpc.WithInsecure()
否则会报错。
|
|
grpc拦截器
在上节课程中,我们学习使用了gRPC框架中的两种认证方式:TLS验证和Token验证。
但是,在服务端的方法中,每个方法都要进行token的判断。程序效率太低,可以优化一下处理逻辑,在调用服务端的具体方法之前,先进行拦截,并进行token验证判断,这种方式称之为拦截器处理。
除了此处的token验证判断处理以外,还可以进行日志处理等。
使用拦截器,首先需要注册。在grpc中编程实现中,可以在NewSever时添加拦截器设置,grpc框架中可以通过UnaryInterceptor方法设置自定义的拦截器,并返回ServerOption。具体代码如下:
|
|
点进去发现需要一个函数
|
|
我们按自己的逻辑实现就好了。
|
|
当然光写了拦截方法不行,我们得注册到服务器
|
|
整体代码:
服务器
|
|
客户端
|
|