仿 gRPC功能实现像调用本地方法一样调用其他服务器方法
简介
在介绍gRPC
简介之前我们先了解一写概念:
单体架构
单体架构简单理解就是所有的业务代码都在一台服务器上,一旦某个服务宕机,会引起整个应用不可用,隔离性差。只能整体应用进行伸缩,例如整体打包部署一台或多台服务器,浪费资源,可伸缩性差。代码耦合在一起,可维护性差。
微服务架构
解决了单体架构的弊端。可按需拆成多个服务,例如针对用户的请求非常多,针对支付的请求少,可以将用户业务功能拆为多个服务器,支付业务功能拆为单个服务器。
也有一些弊端,例如代码冗余,同样的代码在多个服务器上都要写,例如接口等。服务与服务之间存在调用关系,服务拆分之后,就是服务和服务之间发生是进程与进程之间的调用,服务器与服务器之间的调用。
这时候就需要发起网络调用, 网络调用一般使用HTTP
,但是在微服务架构中,HTTP
虽然方便,但性能较低,这时候就需要引入RPC
(远程过程调用),通过自定义协议发起TCP
调用,来加快传输效率。
RPC
RPC
的全称是Remote Procedure Call
,远程过程调用。这是一种协议,是用来屏蔽分布式计算中的各种调用细节,使得你可以像是本地调用一样直接调用一个远程的函数。
gPRC
gRPC
是一个高性能、开源和通用的RPC
框架,面向移动和 HTTP/2
设计。目前提供 C
、Java
和 Go
语言版本,分别是:grpc
, grpc-java
, grpc-go.
其中 C
版本支持 C
, C++
, Node.js
, Python
, Ruby
, Objective-C
, PHP
和 C#
支持。
中文文档:http://doc.oschina.net/grpc
在gRPC
中,我们称调用方为client
,被调用方为server
。跟其他的RPC
框架一样,gRPC
也是基于服务定义的思想。简单的来讲,就是我们通过某种方式来描述一个服务,这种描述方式是语言无关的。在这个服务定义的过程中,我们描述了我们提供的服务服务名是什么,有哪些方法可以被调用,这些方法有什么样的入参,有什么样的回参。
也就是说,在定义好了这些服务、这些方法之后,gRPC
会屏蔽底层的细节,client
只需要直接调用定义好的方法,就能拿到预期的返回结果。对于server
端来说,还需要实现我们定义的方法。同样的,gRPC
也会帮我们屏蔽底层的细节,我们只需要实现所定义的方法的具体逻辑即可。
可以发现,在上面的描述过程中,所谓的服务定义,就跟定义接口的语义是很接近的。我更愿意理解为这是一种"约定”,双方约定好接口,然后server
实现这个接口,client
调用这个接口的代理对象。到于其他的细节,交给gRPC
。
gRPC交互逻辑
服务端逻辑
- 创建
gRPC Server
对象,可以理解为它是Server
端的抽象对象。 - 将
server
(其包含需要被调用的服务端接口)注册到gRPC Server
的内部注册中心。- 这样可以在接受到请求时,通过内部的服务发现。发现该服务端接口并转接进行逻辑处理。
- 创建
Listen
,监听TCP
端口。 -
gRPC Server
开始lis.Accept
,直到Stop
。
客户端逻辑
- 创建与给定目标服务端的连接交互。
- 创建
server
的客户端对象。 - 发送
RPC
请求,等待同步响应,得到回调后返回响应结果。 - 输出响应结里。
示例图
如下图:业务服务器调用登录业务服务器,支付服务器,库存服务器。
原生实现仿gRPC框架
因为gRPC
框架目前不支持IRIS/Caché
,所以这里我们了解gRPC
原理后,仿造gRPC
框架实现类似的功能。通过正常编写代码无感知的情况下调用其他服务器上的代码方法。
注:为了显示这里使用Caché
作为客户端,IRIS
作为服务端。
编写客户端方法
- 首先在客户端也就是调用端创建客户端类
Util.RPC.Client
,代码如下:-
%DispatchClassMethod
- 动态派发方法是实现无感知的关键。 -
SERVERIP
- 目标服务端的IP
地址。 -
PORT
- 目标服务端的端口号。
-
Class Util.RPC.Client Extends %RegisteredObject
{
Parameter SERVERIP = "127.0.0.1";
Parameter PORT = 7788;
ClassMethod %DispatchClassMethod(class As %String, method As %String, args...) [ ServerOnly = 1 ]
{
#; 客户端通信、客户端需要设置服务器IP与端口号
#dim clientSocket As %IO.Socket = ##class(%IO.Socket).%New()
s host = ..#SERVERIP
s port = ..#PORT
s clientSocket.TranslationTable = "UTF8"
d clientSocket.Open(host, port, .sc)
s obj = {} //注释1
s obj.class = class
s obj.method = method
s params = []
s i = ""
for {
s i = $o(args(i))
q:(i = "")
d params.%Push(args(i))
}
s obj.params = params
d clientSocket.WriteLine(obj.%ToJSON()) //注释2
while (1) {
s data = clientSocket.ReadLine()
if (data '= "" ){ //注释3
ret data
}
}
q $$$OK
}
}
- 创建空类
M.Login
,M.Pay
,M.Stock
分别继承Util.RPC.Client
。- 目的是模拟交互接口,因为要调用其他服务器方法,首先要确定要调用的服务器接口名称。
-
按照常规调用方法的模式,来编写方法。
-
这里实际上是没有
Login
,Pay
,Stock
方法的,因为上一步创建的类为空类。 -
如果按照常规直接调用方法肯定会提示方法不存在的错误,这里实际上我们调用的是服务端的方法。
-
可以观察到,这里是按照正常调用类方法的方式编写的代码,并没有其他额外的操作。
-
Class M.RPC Extends %RegisteredObject
{
/// d ##class(M.RPC).Biz()
ClassMethod Biz()
{
w ##class(M.Login).Login("yx","123456"),!
w ##class(M.Pay).Pay(100),!
w ##class(M.Stock).Stock(3),!
}
}
编写服务端方法
- 创建服务端监听
Util.RPC.Server
类,这里模拟的是gRPC
创建Server
对象,创建Listen
,监听TCP
端口。代码如下:- 参数
PORT
为服务端监听和开启的接口。
- 参数
Class Util.RPC.Server Extends %RegisteredObject
{
Parameter PORT = 7788;
/// d ##class(Util.RPC.Server).ServerRPC()
ClassMethod ServerRPC()
{
#; 服务端通信、服务端需要打开端口,等待客户端通信
#dim severSocket As %IO.ServerSocket = ##class(%IO.ServerSocket).%New()
s port = ..#PORT
s severSocket.TranslationTable="UTF8"
s severSocket.ConnectionQueueSize = 2
d severSocket.Open(port, 10, .sc)
q:($$$ISERR(sc)) "Open:" _ $System.Status.GetOneErrorText(sc)
d severSocket.Listen(10, .sc)
q:($$$ISERR(sc)) "Listen:" _ $System.Status.GetOneErrorText(sc)
while (1) {
s data = severSocket.ReadLine()
if (data '= "") {
s obj = {}.%FromJSON(data) //注释1
s arg = obj.params.%Size()
for i = 1 : 1 : arg{
s arg(i) = obj.params.%Get(i - 1)
}
s ret = $classmethod(obj.class, obj.method, arg...) //注释2
d severSocket.WriteLine(ret) //注释3
}
}
q $$$OK
}
}
-
编写服务端
M.Login
的Login
方法,M.Pay
的Pay
方法,M.Stock
的Stock
方法。-
这里使用
IRIS
当服务端,实现的方法都在服务端,客户端是没有该方法的。 -
客户端调用的方法实际上是服务端的上的方法。
-
综合演示
-
IRIS
服务端开启监听方法。 -
客户端
Caché
调用无感知业务逻辑业务员Biz()
方法。-
可观察到客户端直接调用到了服务端的方法,并且编码方式跟正常编写代码并无差别。
-
客户端不需要了解底层的细节,
client
只需要直接调用定义好的方法。
-
通过这种方式我们就实现了类似gRPC
的功能,像正常编写代码一样调用服务端的程序。文章来源:https://www.toymoban.com/news/detail-702864.html
创造价值,分享学习,一起成长,相伴前行,欢迎大家提出意见,共同交流。文章来源地址https://www.toymoban.com/news/detail-702864.html
到了这里,关于仿`gRPC`功能实现像调用本地方法一样调用其他服务器方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!