吉林省城市建设学校网站网络广告策划案例
本文主要内容来自于分布式系统:概念与设计
在设计RPC的实现的时候,有几个问题需要提前说明一下:
- 基于接口的编程实现
- 与RPC有关的调用语义
- 透明性:是本地调用和远程调用相同;
1. 基于接口编程
关于基于接口编程,只要是对于面向对象的编程语言熟悉的人,都会对这种说法不陌生,尤其是使用Java的程序员。当在一个程序中需要各个模块之间进行通信时,尤其是对于Web应用程序,肯定会说为了解耦,通过接口来完成通信,这里所说的“接口”一般是指Web Services,但是不管是RPC还是Web Services,其思想是相同的,都是为了隐藏除了接口以外的所有信息,这样只有模块的接口保持相同,那么模块内的实现就可以随意改变而不受影响。
而对于接口编程,我们在本地程序开发中是十分熟悉的。但是在分布式环境下我们是怎么考虑要使用基于接口的编程的?
- 首先的考虑就是与实现无关,不管接口的实现怎么变化,接口的定义不变,那么调用的过程就不会受影响;
- 在分布式环境中,在不同主机的不同进程中运行的模块的底层实现可能是不同的,比如有使用Java或者php的,但是只要它们对外的接口定义相同就可以,至于实现,who cares!!
虽然有上述的优点,但是在实现的过程中要考虑的问题也是很多的,尤其是在分布式的环境下,在定义接口时要考虑如下问题:
- 在一个进程中的模块要跨进程去访问另一个进程中的模块的变量是不可能的,但是这种情况更准确的说是对于在不同主机的情况下,学过操作系统的都清楚,在本地不同进程中之间的通信是有多种方式的,比如常见的共享内存、管道、队列等等;但是对于在不同主机的情况下,这几种方式是不能用的,但是可以通过对特定变量设置setter和getter方法来说实现;
- 在本地过程调用时使用的参数传递机制,比如值传递和引用传递,不适合在分布式的环境下使用,尤其是引用传递,在分布式环境中是通过在传递的消息中加入参数段来完成参数的传递的,在上一篇文章中提到的请求-应答协议中需要传递的消息部分中的arguments可以作为发送的请求消息的参数和应答消息的返回结果来实现,当然这只是一种实现手段;
上面这几个问题对于接口规范的定义是有很大影响的。在定义接口规范时,首先要考虑的就是在分布式环境下不同接口的实现方式可能是不同的,因此需要一种通用的“粘合剂”来建立不同方式的接口互相调用的桥梁,这就是接口定义语言IDL,这不是一种像Java和Python一类的编程语言,而更像是一种规范,在其中要说明接口的名称、传递的参数的结构形式、以及参数类型,比如是请求消息的参数还是应答消息的参数。
2. RPC调用语义
所谓的调用语义其实就是在执行远程调用的过程中发生的不同的传输情况,针对不同的传输情况对应着不同的容错机制,在执行远程调用过程中可以采取的容错手段如上文所说:
- 重发请求消息:直到收到应答消息或者客户端已经认定服务端出现故障才会停止,这种一般会通过指定超时时要求重发的次数,如果在设定的次数内还是没有应答消息,可以认定为服务端出现故障;
- 过滤重复请求:当在上一步重发请求消息时,服务端设计是否要过滤重复的请求;
- 结果缓存:在服务器上是否保存请求消息的执行结果,这样就当应答消息丢失时不需要重新执行对应的操作也可以返回执行结果;
通过对上述这三种措施的组合使用会造成调用远程过程中发生的各种情况,这种调用的情况和本地来比是有很大差别的,在本地调用相关过程时一定会返回结果,也就是说该过程一定且只执行一次,但是在调用远程过程中就不一定是这样,所以才有了上面的三种措施来保证远程调用过程的可靠性。在远程调用的过程,要根据自己的业务需要来选择相应的调用语义,也就是可能出现的情况,如下表所示:
调用语义 | 执行表现 | 故障类型 | 重发请求消息 | 过滤重复请求 | 重新执行&&重传应答 | 使用场景 |
---|---|---|---|---|---|---|
或许 | 远程方法可能执行一次或根本不执行 | 遗漏故障&&系统崩溃 | 否 | 不适用 | 不适用 | 仅对可以接受偶然调用失败的应用有用 |
至少一次 | 远程过程可以返回一个结果或者一个异常 | 系统崩溃&&随机崩溃 | 是 | 否 | 重新执行过程 | 当服务器上的服务接口方法都是幂等操作 |
至多一次 | 程过程可以返回一个结果或者一个异常 | 是 | 是 | 重传应答 |
3. 透明性
所谓的透明性,就是要隐藏远程调用中相对于本地调用要多做的一部分工作,使调用者可以像调用本地过程一样来调用远程过程,至于其中在调用远程过程中涉及到的编解码、序列化与反序列化、超时重发等问题全部对调用者隐藏起来。但是由于远程调用相对于本地调用更容易发生错误,因为它们会涉及到网络传输、不同的主机以及不同的进程等,所以不管使用上述的哪一种语义,或者说不管怎么组合上述提出的三种容错策略,仍然还可能接受不到结果,而且更麻烦的是不能判别故障是由于网络问题还是由于服务器的问题。
4. RPC软件设计
在RPC的实现过程中有如下的组件:
在客户端进程中有如下组件:
- 客户端程序client:该模块就像调用本地过程一样调用远程过程;
- 客户端存根过程stub procedure:对于服务接口中的每一个方法,对应的在客户端都会有一个存根过程,该组件就是用来隐藏远程过程调用的细节,对于客户端来说就是一个本地过程,它把过程标识符operationId和操作需要的参数arguments编码成请求消息传递给通信模块,当应答消息返回时,它将对结果进行解码;
- 通信模块:将stub procedure打包的信息发送到服务端以及接收应答消息;
在服务端进程中有如下组件:
- 通信模块:接受请求消息并发送应答消息;
- 分发器:根据请求消息中的operationId选择对应的服务器存根过程;
- 服务器存根过程:服务器存根过程与客户端存根过程的作用类似,都是对请求消息解码,然后调用对应的服务过程,并把返回的应答消息进行编码;
- 服务过程:服务端过程的具体实现;
客户端和服务端存根过程以及分发器程序可以通过接口编译器从服务接口定义中自动生成,也就是专门有一个编译器来编译我们的IDL文件,这种方式在老式SOAP类型的Web Services中还会用到。
相关文章:
- 深入浅出 RPC - 浅出篇
- 深入浅出 RPC - 深入篇