很长时间以来都没有怎么好好搞清楚RPC(即Remote Procedure Call,远程过程调用)和HTTP调用的区别,不都是写一个服务而后在客户端调用么?这里请容许我迷之一笑~Naive!本文简单地介绍一下两种形式的C/S架构,先说一下他们最本质的区别,就是RPC主要是基于TCP/IP协议的,而HTTP服务主要是基于HTTP协议的,咱们都知道HTTP协议是在传输层协议TCP之上的,因此效率来看的话,RPC固然是要更胜一筹啦!下面来具体说一说RPC服务和HTTP服务。javascript
为何RPC呢?就是没法在一个进程内,甚至一个计算机内经过本地调用的方式完成的需求,好比好比不一样的系统间的通信,甚至不一样的组织间的通信。因为计算能力须要横向扩展,须要在多台机器组成的集群上部署应用。
RPC的协议有不少,好比最先的CORBA,Java RMI,Web Service的RPC风格,Hessian,Thrift,甚至Rest API。css
关于RPC
RPC框架,首先了解什么叫RPC,为何要RPC,RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,因为不在一个内存空间,不能直接调用,须要经过网络来表达调用的语义和传达调用的数据。html
好比说,一个方法多是这样定义的:
Employee getEmployeeByName(String fullName)
那么:java
在说RPC和HTTP的区别以前,我觉的有必要了解一下OSI的七层网络结构模型(虽然实际应用中基本上都是五层),它能够分为如下几层: (从上到下)ios
实际应用过程当中,五层协议结构里面是没有表示层和会话层的。应该说它们和应用层合并了。咱们应该将重点放在应用层和传输层这两个层面。由于HTTP是应用层协议,而TCP是传输层协议。好,知道了网络的分层模型之后咱们能够更好地理解为何RPC服务相比HTTP服务要Nice一些!程序员
从三个角度来介绍RPC服务:分别是RPC架构,同步异步调用以及流行的RPC框架。web
先说说RPC服务的基本架构吧。容许我可耻地盗一幅图哈~咱们能够很清楚地看到,一个完整的RPC架构里面包含了四个核心的组件,分别是Client ,Server,Client Stub以及Server Stub,这个Stub你们能够理解为存根。分别说说这几个组件:ajax
RPC主要是用在大型企业里面,由于大型企业里面系统繁多,业务线复杂,并且效率优点很是重要的一块,这个时候RPC的优点就比较明显了。实际的开发当中是这么作的,项目通常使用maven来管理。好比咱们有一个处理订单的系统服务,先声明它的全部的接口(这里就是具体指Java中的interface
),而后将整个项目打包为一个jar
包,服务端这边引入这个二方库,而后实现相应的功能,客户端这边也只须要引入这个二方库便可调用了。为何这么作?主要是为了减小客户端这边的jar
包大小,由于每一次打包发布的时候,jar
包太多老是会影响效率。另外也是将客户端和服务端解耦,提升代码的可移植性。spring
什么是同步调用?什么是异步调用?同步调用
就是客户端等待调用执行完成并返回结果。异步调用
就是客户端不等待调用执行完成返回结果,不过依然能够经过回调函数等接收到返回结果的通知。若是客户端并不关心结果,则能够变成一个单向的调用。这个过程有点相似于Java中的callable
和runnable
接口,咱们进行异步执行的时候,若是须要知道执行的结果,就可使用callable
接口,而且能够经过Future
类获取到异步执行的结果信息。若是不关心执行的结果,直接使用runnable
接口就能够了,由于它不返回结果,固然啦,callable
也是能够的,咱们不去获取Future
就能够了。编程
目前流行的开源RPC框架仍是比较多的。下面重点介绍三种:
偷偷告诉你
集团内部已经不怎么使用dubbo啦,如今用的比较多的叫HSF,又名“好舒服”。后面有可能会开源,你们拭目以待。
其实在好久之前,我对于企业开发的模式一直定性为HTTP接口开发,也就是咱们常说的RESTful风格的服务接口。的确,对于在接口很少、系统与系统交互较少的状况下,解决信息孤岛初期常使用的一种通讯手段;优势就是简单、直接、开发方便。利用现成的http协议进行传输。咱们记得以前本科实习在公司作后台开发的时候,主要就是进行接口的开发,还要写一大份接口文档,严格地标明输入输出是什么?说清楚每个接口的请求方法,以及请求参数须要注意的事项等。好比下面这个例子:POST http://www.httpexample.com/restful/buyer/info/share
接口可能返回一个JSON字符串或者是XML文档。而后客户端再去处理这个返回的信息,从而能够比较快速地进行开发。可是对于大型企业来讲,内部子系统较多、接口很是多的状况下,RPC框架的好处就显示出来了,首先就是长连接,没必要每次通讯都要像http同样去3次握手什么的,减小了网络开销;其次就是RPC框架通常都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调用方来讲是无感知、统一化的操做。
引自:https://blog.csdn.net/kkkloveyou/article/details/51874354
本文介绍了什么是远程过程调用(RPC),RPC 有哪些经常使用的方法,RPC 经历了哪些发展阶段,以及比较了各类 RPC 技术的优劣。
RPC 是远程过程调用(Remote Procedure Call)的缩写形式,Birrell 和 Nelson 在 1984 发表于 ACM Transactions on Computer Systems 的论文《Implementing remote procedure calls》对 RPC 作了经典的诠释。RPC 是指计算机 A 上的进程,调用另一台计算机 B 上的进程,其中 A 上的调用进程被挂起,而 B 上的被调用进程开始执行,当值返回给 A 时,A 进程继续执行。调用方能够经过使用参数将信息传送给被调用方,然后能够经过传回的结果获得信息。而这一过程,对于开发人员来讲是透明的。
图1 描述了数据报在一个简单的RPC传递的过程
注:上述论文,能够在线阅读 http://www.cs.virginia.edu/~zaher/classes/CS656/birrel.pdf。
远程过程调用采用客户机/服务器(C/S)模式。请求程序就是一个客户机,而服务提供程序就是一台服务器。和常规或本地过程调用同样,远程过程调用是同步操做,在远程过程结果返回以前,须要暂时停止请求程序。使用相同地址空间的低权进程或低权线程容许同时运行多个远程过程调用。
让咱们看看本地过程调用是如何实现的。考虑下面的 C 语言的调用:
count = read(fd, buf, nbytes);
其中,fd 为一个整型数,表示一个文件。buf 为一个字符数组,用于存储读入的数据。 nbytes 为另外一个整型数,用于记录实际读入的字节数。若是该调用位于主程序中,那么在调用以前堆栈的状态如图2(a)所示。为了进行调用,调用方首先把参数反序压入堆栈,即为最后一个参数先压入,如图2(b)所示。在 read 操做运行完毕后,它将返回值放在某个寄存器中,移出返回地址,并将控制权交回给调用方。调用方随后将参数从堆栈中移出,使堆栈还原到最初的状态。
图2 过程调用中的参数传递
RPC 背后的思想是尽可能使远程过程调用具备与本地调用相同的形式。假设程序须要从某个文件读取数据,程序员在代码中执行 read 调用来取得数据。在传统的系统中, read 例程由连接器从库中提取出来,而后连接器再将它插入目标程序中。 read 过程是一个短过程,通常经过执行一个等效的 read 系统调用来实现。即,read 过程是一个位于用户代码与本地操做系统之间的接口。
虽然 read 中执行了系统调用,但它自己依然是经过将参数压入堆栈的常规方式调用的。如图2(b)所示,程序员并不知道 read 干了啥。
RPC 是经过相似的途径来得到透明性。当 read 其实是一个远程过程时(好比在文件服务器所在的机器上运行的过程),库中就放入 read 的另一个版本,称为客户存根(client stub)。这种版本的 read 过程一样遵循图2(b)的调用次序,这点与原来的 read 过程相同。另外一个相同点是其中也执行了本地操做系统调用。惟一不一样点是它不要求操做系统提供数据,而是将参数打包成消息,然后请求此消息发送到服务器,如图3所示。在对 send 的调用后,客户存根调用 receive 过程,随即阻塞本身,直到收到响应消息。
图3 客户与服务器之间的RPC原理
当消息到达服务器时,服务器上的操做系统将它传递给服务器存根(server stub)。服务器存根是客户存根在服务器端的等价物,也是一段代码,用来将经过网络输入的请求转换为本地过程调用。服务器存根通常先调用 receive ,而后被阻塞,等待消息输入。收到消息后,服务器将参数由消息中提取出来,而后以常规方式调用服务器上的相应过程(如图3所示)。从服务器角度看,过程好像是由客户直接调用的同样:参数和返回地址都位于堆栈中,一切都很正常。服务器执行所要求的操做,随后将获得的结果以常规的方式返回给调用方。以 read 为例,服务器将用数据填充 read 中第二个参数指向的缓冲区,该缓存区是属于服务器存根内部的。
调用完后,服务器存根要将控制权教会给客户发出调用的过程,它将结果(缓冲区)打包成消息,随后调用 send 将结果返回给客户。过后,服务器存根通常会再次调用 receive,等待下一个输入的请求。
客户机器接收到消息后,客户操做系统发现该消息属于某个客户进程(实际上该进程是客户存根,知识操做系统没法区分两者)。操做系统将消息复制到相应的缓存区中,随后解除对客户进程的阻塞。客户存根检查该消息,将结果提取出来并复制给调用者,然后以一般的方式返回。当调用者在 read 调用进行完毕后从新得到控制权时,它所知道的惟一事就是已经获得了所需的数据。它不指导操做是在本地操做系统进行,仍是远程完成。
整个方法,客户方能够简单地忽略不关心的内容。客户所涉及的操做只是执行普通的(本地)过程调用来访问远程服务,它并不须要直接调用 send 和 receive 。消息传递的全部细节都隐藏在双方的库过程当中,就像传统库隐藏了执行实际系统调用的细节同样。
概况来讲,远程过程调用包含以下步骤:
以上步骤就是将客户过程对客户存根发出的本地调用转换成对服务器过程的本地调用,而客户端和服务器都不会意识到中间步骤的存在。
RPC 的主要好处是双重的。首先,程序员可使用过程调用语义来调用远程函数并获取响应。其次,简化了编写分布式应用程序的难度,由于 RPC 隐藏了全部的网络代码存根函数。应用程序没必要担忧一些细节,好比 socket、端口号以及数据的转换和解析。在 OSI 参考模型,RPC 跨越了会话层和表示层。
要实现远程过程调用,需考虑如下几个问题。
传递值参数比较简单,下图图展现了一个简单 RPC 进行远程计算的例子。其中,远程过程 add(i,j) 有两个参数 i 和 j, 其结果是返回 i 和 j 的算术和。
图4 经过RPC进行远程计算的步骤
经过 RPC 进行远程计算的步骤有:
固然,这里只是作了简单的演示,在实际分布式系统中,还须要考虑其余状况,由于不一样的机器对于数字、字符和其余类型的数据项的表示方式常有差别。好比整数型,就有 Big Endian 和 Little Endian 之分。
传递引用参数相对来讲比较困难。单纯传递参数的引用(也包含指针)是彻底没有意义的,由于引用地址传递给远程计算机,其指向的内存位置可能跟远程系统上彻底不一样。若是你想支持传递引用参数,你必须发送参数的副本,将它们放置在远程系统内存中,向他们传递一个指向服务器函数的指针,而后将对象发送回客户端,复制它的引用。若是远程过程调用必须支持引用复杂的结构,好比树和链表,他们须要将结构复制到一个无指针的表示里面(好比,一个扁平的树),并传输到在远程端来重建数据结构。
在本地系统上不存在数据不相容的问题,由于数据格式老是相同的。而在分布式系统中,不一样远程机器上可能有不一样的字节顺序,不一样大小的整数,以及不一样的浮点表示。对于 RPC,若是想与异构系统通讯,咱们就须要想出一个“标准”来对全部数据类型进行编码,并能够做为参数传递。例如,ONC RPC 使用 XDR (eXternal Data Representation) 格式 。这些数据表示格式可使用隐式或显式类型。隐式类型,是指只传递值,而不传递变量的名称或类型。常见的例子是 ONC RPC 的 XDR 和 DCE RPC 的 NDR。显式类型,指须要传递每一个字段的类型以及值。常见的例子是 ISO 标准 ASN.1 (Abstract Syntax Notation)、JSON (JavaScript Object Notation)、Google Protocol Buffers、以及各类基于 XML 的数据表示格式。
有些实现只容许使用一个协议(例如 TCP )。大多数 RPC 实现支持几个,并容许用户选择。
相比于本地过程调用,远程过程调用出错的机会将会更多。因为本地过程调用没有过程调用失败的概念,项目使用远程过程调用必须准备测试远程过程调用的失败或捕获异常。
调用一个普通的过程语义很简单:当咱们调用时,过程被执行。远程过程彻底一次性调用成功是很是难以实现。执行远程过程能够有以下结果:
RPC 系统一般会提供至少一次或最多一次的语义,或者在二者之间选择。若是须要了解应用程序的性质和远程过程的功能是否安全,能够经过屡次调用同一个函数来验证。若是一个函数能够运行任何次数而不影响结果,这是幂等(idempotent)函数的,如天天的时间、数学函数、读取静态数据等。不然,它是一个非幂等(nonidempotent)函数,如添加或修改一个文件)。
毫无疑问,一个远程过程调用将会比常规的本地过程调用慢得多,由于产生了额外的步骤以及网络传输自己存在延迟。然而,这并不该该阻止咱们使用远程过程调用。
使用 RPC,咱们必须关注各类安全问题:
远程过程调用有诸多的优势:
任何 RPC 实现都须要提供一组支持库。这些包括:
因此,判断一种通讯方式是不是 RPC,就看它是否提供上述的 API。
Sun 公司是第一个提供商业化 RPC 库和 RPC 编译器。在1980年代中期 Sun 计算机提供 RPC,并在 Sun Network File System(NFS) 获得支持。该协议被主要以 Sun 和 AT&T 为首的 Open Network Computing (开放网络计算)做为一个标准来推进。这是一个很是轻量级 RPC 系统可用在大多数 POSIX 和类 POSIX 操做系统中使用,包括 Linux、SunOS、OS X 和各类发布版本的 BSD。这样的系统被称为 Sun RPC 或 ONC RPC。
ONC RPC 提供了一个编译器,须要一个远程过程接口的定义来生成客户机和服务器的存根函数。这个编译器叫作 rpcgen。在运行此编译器以前,程序员必须提供接口定义。包含函数声明的接口定义,经过版本号进行分组,并被一个独特的程序编码来标识。该程序编码可以让客户来肯定所需的接口。版本号是很是有用的,即便客户没有更新到最新的代码仍然能够链接到一个新的服务器,只要该服务器还支持旧接口。
参数经过网络转化成一种隐式类型序列化格式被称为 XDR (eXternal Data Representation)。这将确保参数可以发送到异构系统能够被正常使用,及时这些系统可能使用了不一样的字节顺序,不一样大小的整数,或不一样的浮点或字符串表示。最后,Sun RPC 提供了一个实现必要的支持 RPC 协议和 socket 例程的运行时库。
全部的程序员都须要写是一个客户端程序(client.c),服务器功能(server.c)和 RPC 接口定义(date.x)。当 RPC 接口定义(后缀为.x 的文件,例如 date.x)是用 rpcgen 编译的,会建立三个或四个文件。下面是 date.x 的例子:
建立客户端和服务器可执行文件的第一步是定义在文件 date.x 里的编译数据。以后,客户端和服务器端函数可能被编译,并连接各自 rpcgen 生成的存根函数。
在旧版本里,传输协议只能将字符串“tcp”或字符串“udp”来指定各自的 IP 服务 RPC,且仅限于 Linux 实现的 RPC。为了使接口更加灵活,UNIX 系统从版本 4 (SunOS 从版本 5)开始网络选择程序容许一个更普通的规范。他们搜索文件(/etc/netconfig),来查找第一个知足您需求的提供者。最后一个参数能够是:
每一个远程过程调用最初仅限于接受一个输入参数。系统只是后来修改成支持多个参数。支持单一参数 RPC 在一些 rpcgen 的版本中仍然是默认的,好比苹果的 OS X。传递多个参数必须经过定义一个结构,包含所需的参数,初始化它,并传递这个结构。
远程过程调用返回一个指针指向结果而不是指望的结果。服务器函数必须修改来能接受一个 RPC 定义(.x 文件)中声明的值的指针做为输入,并返回一个结果值的指针。在服务器上,一个指针必须是指向静态数据的指针。不然,当过程返回或释放过程的框架所指出的区域将未定义。在客户端,返回指针可让咱们区分一个失败的 RPC(空指针)和一个空返回从服务器(间接空指针)。
RPC 过程的名称若在 RPC 定义文件中作了定义,则会转换为小写,并在后缀价下划线,后跟一个版本号。例如,BIN_DATE 转成为引用函数 bin_date_1 。您的服务器必须实现 bin_date_1。
当咱们启动服务器,服务器存根代码(程序)在后台运行运行。它建立一个 socket 并可绑定任何本地端口到 socket。而后调用一个在 RPC 库的函数 svc_register,来注册程序编号和版本。这个是用来联系 port mapper(端口映射器)。port mapper 是一个独立的进程,一般是在系统启动时启动。它跟踪端口号、版本号以及程序编号。在 UNIX 系统版本4中,这个进程称为 rpcbind。在 Linux 、OS X 和 BSD 系统,它被称为 portmap。
图5 ONC RPC 中的函数查找
当咱们开始客户端程序时,它首先用远程系统的名称、程序编号、版本号和协议来调用 clnt_create 。它接触远程系统上的端口映射器,来为系统找到合适的端口。
而后客户端调用 RPC 存根函数(在本例中为 bin_date_1)。该函数发送一条消息(如,数据报)到服务器(使用早些时候发现的端口号)并等待响应。对于数据报服务来讲,若没有接收到响应,它将从新发送一个固定的次数请求。
消息接着被远程系统接收到,它调用服务器函数(bin_date_1)并将返回值返回给客户端存根。客户端存根然后返回到客户端发出调用的代码。
DCE(Distributed Computing Environment,分布式计算环境)是一组由OFS(Open Software Foundation,开放软件基金会)设计的组件,用来提供支持分布式应用和分布式环境。与 X/Open 合并后,这组织成为了 The Open Group (开放式开发组)。DCE 提供的组件包括一个分布式文件服务、时间服务、目录服务以及其余服务。固然,咱们感兴趣的是 DCE 的远程过程调用。它很是相似于 Sun RPC。接口是由 Interface Definition Notation (IDN) 定义的。相似于 Sun RPC,接口定义就像函数原型。
Sun RPC 不足之处在于,服务器的标识是一个“独特”的 32-bit 数字。虽然这是一个比在 socket 中 16-bit 可用空间更大的空间,但仍然没法知足数字惟一性的需求。DCE RPC 考虑到了这一缺陷,它无需程序员来处理编码。在编写应用程序时的第一步是从 uuidgen 程序得到一个唯一的 ID。这个程序会生成一个包含 ID 接口的原型 IDN 文件,并保证永远不会再次使用。它是一个 128-bit 的值,其中包含一个位置代码和建立时间的编码。而后用户编辑原型文件,填写远程过程声明。
在这一步后,IDN 的编译器 dceidl(相似于 rpcgen)会生成一个头、客户机存根和服务器存根。
Sun RPC 的另外一个缺陷是,客户端必须知道服务器在哪台机器上。当它要访问时,必需要询问机器上的 RPC 名称服务程序编码所对应的端口号。DCE 支持将多个机器组织成为管理实体,称为 cells。cell 目录服务器使得每台机器知道如何与另一台负责维护 cell 信息服务机器交互。
在 Sun RPC 中,服务器只能用本地名称服务(端口映射器)来注册其程序编号到端口映射。而在 DCE 中,服务器用 RPC 守护进程(名称服务器)来注册其端点(端口)到本地机器,而且用 cell 目录服务器注册其程序名字到机器的映射。当客户机想要与一个 RPC 服务器创建通讯,它首先要求其 cell 目录服务器来定位服务器所在的机器。而后客户端从 RPC 守护进程处得到机器上服务器进程的端口号。DCE 的跨 cell 还支持更复杂的搜索。
DCE RPC 定义了 NDR (Network Data Representation) 用于对网络进行编码来封送信息。与用一个单一的规范来表示不一样的数据类型相比,NDR 支持多规范(multi-canonical)格式。容许客户端来选择使用哪一种格式,理想的状况是不须要将它从本地类型来转换。若是这不一样于服务器的本地数据表示,服务器将仍然须要转换,但多规范格式能够避免当客户端和服务器都共享相同的本地格式的状况下转换为其余外部格式。例如,在一个规定了大端字节序网络数据格式的状况下,客户端和服务器只支持小端字节序,那么客户端必须将每一个数据从小端字节序转为大端字节序,而当服务器接受到消息后,将每一个数据转回小端字节序。多规范网络数据表示将容许客户端发送网络消息包含小端字节序格式的数据。
图6 DCE RPC 中的函数查找
面向对象的语言开始在1980年代末兴起,很明显,当时的 Sun ONC 和 DCE RPC 系统都没有提供任何支持诸如从远程类实例化远程对象、跟踪对象的实例或提供支持多态性。现有的 RPC 机制虽然能够运做,但他们仍然不支持自动、透明的方式的面向对象编程技术。
1992年4月,微软发布 Windows 3.1 包括一种机制称为 OLE (Object Linking and Embedding)。这容许一个程序动态连接其余库来支持的其余功能。如将一个电子表格嵌入到 Word 文档。OLE 演变成了 COM (Component Object Model)。一个 COM 对象是一个二进制文件。使用 COM 服务的程序来访问标准化接口的 COM 对象而不是其内部结构。COM 对象用全局惟一标识符(GUID)来命名,用类的 ID 来识别对象的类。几种方法来建立一个 COM 对象(例如 CoGetInstanceFromFile)。COM 库在系统注册表中查找相应的二进制代码(一个 DLL 或可执行文件),来建立对象,并给调用者返回一个接口指针。COM 的着眼点是在于同一台计算机上不一样应用程序之间的通信需求.
DCOM( Distributed Component Object Model)是 COM 的扩展,它支持不一样的两台机器上的组件间的通讯,并且不论它们是运行在局域网、广域网、仍是 Internet 上。借助 DCOM 你的应用程序将可以进行任意空间分布。DCOM 于1996年在 Windows NT4.0 中引入的,后来改名为 COM+。因为 DCOM 是为了支持访问远程 COM 对象,须要建立一个对象的过程,此时须要提供服务器的网络名以及类 ID。微软提供了一些机制来实现这一点。最透明的方式是远程计算机的名称固定在注册表(或 DCOM 类存储)里,与特定类 ID 相关联。以此方式,应用程序不知道它正在访问一个远程对象,而且可使用与访问本地 COM 对象相同的接口指针。另外一方面,应用程序也可指定一个机器名做为参数。
因为 DCOM 是 COM 这个组件技术的无缝升级,因此你可以从你现有的有关 COM 得知识中获益,你的之前在 COM 中开发的应用程序、组件、工具均可以移入分布式的环境中。DCOM 将为你屏蔽底层网络协议的细节,你只须要集中精力于你的应用。
DCOM 最大的缺点是这是微软独家的解决办法,在跨防火墙方面的工做作得不是很好(大多数RPC系统也有相似的问题),由于防火墙必须容许某些端口来容许 ORPC 和 DCOM 经过。
虽然 DCE 修复的一些 Sun RPC 的缺点,但某些缺陷依然存在。例如,若是服务器没有运行,客户端是没法链接到远程过程进行调用的。管理员必需要确保在任何客户端试图链接到服务器以前将服务器启动。若是一个新服务或接口添加到了系统,客户端是不能发现的。最后,面向对象语言指望在函数调用中体现多态性,即不一样类型的数据的函数的行为应该有所不一样,而这点偏偏是传统的 RPC 所不支持的。
CORBA (Common Object Request Broker Architecture) 就是为了解决上面提到的各类问题。是由 OMG 组织制订的一种标准的面向对象应用程 序体系规范。或者说 CORBA体系结构是对象管理组织(OMG)为解决分布式处理环境(DCE)中,硬件和软件系统的互连而提出的一种解决方案。OMG 成立于1989年,做为一个非营利性组织,集中致力于开发在技术上具备先进性、在商业上具备可行性而且独立于厂商的软件互联规范,推广面向对象模型技术,加强软件的可移植性(Portability)、可重用性(Reusability)和互操做性(Interoperability)。该组织成立之初,成员包括 Unisys、Sun、Cannon、Hewlett-Packard 和 Philips 等在业界享有声誉的软硬件厂商,目前该组织拥有800多家成员。
CORBA 体系的主要内容包括如下几部分:
当客户端发出请求时,ORB 作了以下事情:
IDL(Interface Definition Language) 是用于指定类的名字、属性和方法。它不包含对象的实现。IDL 编译器生成代码来处理编组、解封以及ORB与网络之间的交互。它会生成客户机和服务器存根。IDL 是编程语言中立,支持包括C、C++、Java、Perl、Python、Ada、COBOL、Smalltalk、Objective C 和 LISP 等语言。一个示例IDL以下所示:
Module StudentObject {
Struct StudentInfo {
String name;
int id; float gpa; }; exception Unknown {}; interface Student { StudentInfo getinfo(in string name) raises(unknown); void putinfo(in StudentInfo data); }; };
IDL数据类型包括:
编程中最多见的实现方式是经过对象引用来实现请求。下面是一个使用 IDL 的例子:
Student st = ... // get object reference try { StudentInfo sinfo = st.getinfo("Fred Grampp"); } catch (Throwable e) { ... // error }
在 CORBA 规范中,没有明确说明不一样厂商的中间件产品要实现全部的服务功能,而且容许厂商开发本身的服务类型。所以, 不一样厂商的 ORB 产品对 CORBA 服务的支持能力不一样,使咱们在针对待开发系统的功能进行中间件产品选择时,有更多的选择余地。
CORBA 的不足有:
更多有关 CORBA 的优缺点,能够参阅 Michi Henning 的《The rise and fall of CORBA》。
CORBA 旨在提供一组全面的服务来管理在异构环境中(不一样语言、操做系统、网络)的对象。Java 在其最初只支持经过 socket 来实现分布式通讯。1995年,做为 Java 的缔造者,Sun 公司开始建立一个 Java 的扩展,称为 Java RMI(Remote Method Invocation,远程方法调用)。Java RMI 容许程序员建立分布式应用程序时,能够从其余 Java 虚拟机(JVM)调用远程对象的方法。
一旦应用程序(客户端)引用了远程对象,就能够进行远程调用了。这是经过 RMI 提供的命名服务(RMI 注册中心)来查找远程对象,来接收做为返回值的引用。Java RMI 在概念上相似于 RPC,但能在不一样地址空间支持对象调用的语义。
与大多数其余诸如 CORBA 的 RPC 系统不一样,RMI 只支持基于 Java 来构建,但也正是这个缘由, RMI 对于语言来讲更加整洁,无需作额外的数据序列化工做。Java RMI 的设计目标应该是:
分布式对象模型与本地 Java 对象模型类似点在于:
不一样点在于:
全部的远程接口都继承自 java.rmi.Remote
接口。例如:
public interface bankaccount extends Remote { public void deposit(float amount) throws java.rmi.RemoteException; public void withdraw(float amount) throws OverdrawnException, java.rmi.RemoteException; }
注意,每一个方法必须在 throws 里面声明 java.rmi.RemoteException
。 只要客户端调用远程方法出现失败,这个异常就会抛出。
Java.rmi.server.RemoteObject
类提供了远程对象实现的语义包括hashCode、equals 和 toString。 java.rmi.server.RemoteServer
及其子类提供让对象实现远程可见。java.rmi.server.UnicastRemoteObject
类定义了客户机与服务器对象实例创建一对一的链接.
Java RMI 经过建立存根函数来工做。存根由 rmic 编译器生成。自 Java 1.5 以来,Java 支持在运行时动态生成存根类。编译器 rmic 会提供各类编译选项。
引导名称服务提供了用于存储对远程对象的命名引用。一个远程对象引用能够存储使用类 java.rmi.Naming
提供的基于 URL 的方法。例如,
BankAccount acct = new BankAcctImpl(); String url = "rmi://java.sun.com/account"; // bind url to remote object java.rmi.Naming.bind(url, acct); // look up account acct = (BankAccount)java.rmi.Naming.lookup(url);
图7 Java RMI 工做流程
RMI 是一个三层架构(图8)。最上面是 Stub/Skeleton layer(存根/骨架层)。方法调用从 Stub、Remote Reference Layer (远程引用层)和 Transport Layer(传输层)向下,传递给主机,而后再次经传 Transport Layer 层,向上穿过 Remote Reference Layer 和 Skeleton ,到达服务器对象。 Stub 扮演着远程服务器对象的代理的角色,使该对象可被客户激活。Remote Reference Layer 处理语义、管理单一或多重对象的通讯,决定调用是应发往一个服务器仍是多个。Transport Layer 管理实际的链接,而且追踪能够接受方法调用的远程对象。服务器端的 Skeleton 完成对服务器对象实际的方法调用,并获取返回值。返回值向下经 Remote Reference Layer 、服务器端的 Transport Layer 传递回客户端,再向上经 Transport Layer 和 Remote Reference Layer 返回。最后,Stub 程序得到返回值。
要完成以上步骤须要有如下几个步骤:
图8 Java RMI 架构
根据 Java 虚拟机的垃圾回收机制原理,在分布式环境下,服务器进程须要知道哪些对象再也不由客户端引用,从而能够被删除(垃圾回收)。在 JVM中,Java 使用引用计数。当引用计数归零时,对象将会垃圾回收。在RMI,Java 支持两种操做:dirty 和 clean。本地 JVM 按期发送一个 dirty 到服务器来讲明该对象仍在使用。按期重发 dirty 的周期是由服务器租赁时间来决定的。当客户端没有须要更多的本地引用远程对象时,它发送一个 clean 调用给服务器。不像 DCOM,服务器不须要计算每一个客户机使用的对象,只是简单的作下通知。若是它租赁时间到期以前没有接收到任何 dirty 或者 clean 的消息,则能够安排将对象删除。
因为互联网的兴起,Web 浏览器成为占主导地位的用于访问信息的模型。如今的应用设计的首要任务大多数是提供用户经过浏览器来访问,而不是编程访问或操做数据。
网页设计关注的是内容。解析展示方面每每是繁琐的。传统 RPC 解决方案能够工做在互联网上,但问题是,他们一般严重依赖于动态端口分配,每每要进行额外的防火墙配置。
Web Services 成为一组协议,容许服务被发布、发现,并用于技术无关的形式。即服务不该该依赖于客户的语言、操做系统或机器架构。
Web Services 的实现通常是使用 Web 服务器做为服务请求的管道。客户端访问该服务,首先是经过一个 HTTP 协议发送请求到服务器上的 Web 服务器。Web 服务器配置识别 URL 的一部分路径名或文件名后缀并将请求传递给特定的浏览器插件模块。这个模块能够除去头、解析数据(若是须要),并根据须要调用其余函数或模块。对于这个实现流,一个常见的例子是浏览器对于 Java Servlet 的支持。HTTP 请求会被转发到 JVM 运行的服务端代码来执行处理。
XML-RPC 是1998年做为一个 RPC 消息传递协议,将请求和响应封装解析为人类可读的 XML格式。XML 格式基于 HTTP 协议,缓解了传统企业的防火墙须要为 RPC 服务器应用程序打开额外的端口的问题。
下面是一个 XML-RPC 消息的例子:
<methodCall> <methodName> sample.sumAndDifference </methodName> <params> <param><value><int> 5 </int></value></param> <param><value><int> 3 </int></value></param> </params> </methodCall>
这个例子中,方法 sumAndDifference 有两个整数参数 5 和 3。
XML-RPC 支持的基本数据类型是:int、string、boolean、double 和 dateTime.iso8601。此外,还有 base64 类型用于编码任意二进制数据。array 和 struct 容许定义数组和结构。
XML-RPC 不限制语任何特定的语言,也不是一套完整的软件来处理远程过程,诸如存根生成、对象管理和服务查找都不在协议内。如今有不少库针能够针对不一样的语言,好比 Apache XML-RPC 能够用于 Java、Python 和 Perl。
XML-RPC 是一个简单的规范(约7页),没有雄心勃勃的目标——它只关注消息,而并不处理诸如垃圾收集、远程对象、远程过程的名称服务和其余方面的问题。然而,即便没有普遍的产业支持,简单的协议却能普遍采用。
SOAP(Simple Object Access Protocol,简单对象访问协议),是以 XML-RPC 规范做为建立 SOAP 的依据,成立于1998年,得到微软和 IBM 的大力支持。该协议在建立初期只做为一种对象访问协议,但因为 SOAP 的发展,其协议已经不单只是用于简单的访问对象,因此这种 SOAP 缩写已经在标准的1.2版后被废止了。1.2版在2003年6月24日成为 W3C 的推荐版本。SOAP 指定 XML 做为无状态的消息交换格式,包括了 RPC 式的过程调用。
有关 SOAP 的标准能够参阅 https://www.w3.org/TR/soap/。
SOAP 只是一种消息格式,并未定义垃圾回收、对象引用、存根生成和传输协议。
下面是一个简单的例子:
<?xml version='1.0' ?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"> <env:Header> <m:reservation xmlns:m="http://travelcompany.example.org/reservation" env:role="http://www.w3.org/2003/05/soap-envelope/role/next" env:mustUnderstand="true"> <m:reference>uuid:093a2da1-q345-739r-ba5d-pqff98fe8j7d</m:reference> <m:dateAndTime>2001-11-29T13:20:00.000-05:00</m:dateAndTime> </m:reservation> <n:passenger xmlns:n="http://mycompany.example.com/employees" env:role="http://www.w3.org/2003/05/soap-envelope/role/next" env:mustUnderstand="true"> <n:name>Åke Jógvan Øyvind</n:name> </n:passenger> </env:Header> <env:Body> <p:itinerary xmlns:p="http://travelcompany.example.org/reservation/travel"> <p:departure> <p:departing>New York</p:departing> <p:arriving>Los Angeles</p:arriving> <p:departureDate>2001-12-14</p:departureDate> <p:departureTime>late afternoon</p:departureTime> <p:seatPreference>aisle</p:seatPreference> </p:departure> <p:return> <p:departing>Los Angeles</p:departing> <p:arriving>New York</p:arriving> <p:departureDate>2001-12-20</p:departureDate> <p:departureTime>mid-morning</p:departureTime> <p:seatPreference/> </p:return> </p:itinerary> <q:lodging xmlns:q="http://travelcompany.example.org/reservation/hotels"> <q:preference>none</q:preference> </q:lodging> </env:Body> </env:Envelope>
其中<soap:Envelope>
是 SOAP 消息中的根节点,是 SOAP 消息中必须的部分。<soap:Header>
是 SOAP 消息中可选部分,是指消息头。<soap:Body>
是 SOAP 中必须部分,是指消息体。
上面例子的 SOAP 消息结构以下:
图9 SOAP 消息结构
SOAP 它只是提供了一个标准化的消息结构,为了实现它每每须要用 WSDL 来描述 Web Services 的方法。WSDL (Web Services Description Language) 是基于 XML 的一种用于描述 Web Services 以及如何访问 Web Services 的语言。
WSDL 文档包括如下几个部分:
下面是一个 WSDL 2.0 版本的例子:
<?xml version="1.0" encoding="UTF-8"?> <description xmlns="http://www.w3.org/ns/wsdl" xmlns:tns="http://www.tmsws.com/wsdl20sample" xmlns:whttp="http://schemas.xmlsoap.org/wsdl/http/" xmlns:wsoap="http://schemas.xmlsoap.org/wsdl/soap/" targetNamespace="http://www.tmsws.com/wsdl20sample"> <documentation> This is a sample WSDL 2.0 document. </documentation> <!-- Abstract type --> <types> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.tmsws.com/wsdl20sample" targetNamespace="http://www.example.com/wsdl20sample"> <xs:element name="request"> ... </xs:element> <xs:element name="response"> ... </xs:element> </xs:schema> </types> <!-- Abstract interfaces --> <interface name="Interface1"> <fault name="Error1" element="tns:response"/> <operation name="Get" pattern="http://www.w3.org/ns/wsdl/in-out"> <input messageLabel="In" element="tns:request"/> <output messageLabel="Out" element="tns:response"/> </operation> </interface> <!-- Concrete Binding Over HTTP --> <binding name="HttpBinding" interface="tns:Interface1" type="http://www.w3.org/ns/wsdl/http"> <operation ref="tns:Get" whttp:method="GET"/> </binding> <!-- Concrete Binding with SOAP--> <binding name="SoapBinding" interface="tns:Interface1" type="http://www.w3.org/ns/wsdl/soap" wsoap:protocol="http://www.w3.org/2003/05/soap/bindings/HTTP/" wsoap:mepDefault="http://www.w3.org/2003/05/soap/mep/request-response"> <operation ref="tns:Get" /> </binding> <!-- Web Service offering endpoints for both bindings--> <service name="Service1" interface="tns:Interface1"> <endpoint name="HttpEndpoint" binding="tns:HttpBinding" address="http://www.example.com/rest/"/> <endpoint name="SoapEndpoint" binding="tns:SoapBinding" address="http://www.example.com/soap/"/> </service> </description>
从微软的产品角度来看,能够说 .NET Remoting 就是 DCOM 的一种升级,它改善了不少功能,并极好的融合到 .NET 平台下。Microsoft .NET Remoting 提供了一种容许对象经过应用程序域与另外一对象进行交互的框架。
.NET Remoting 提供了一种容许对象经过应用程序域与另外一对象进行交互的框架。这种框架提供了多种服务,包括激活和生存期支持,以及负责与远程应用程序进行消息传输的通信通道。格式化程序用于在消息经过通道传输以前,对其进行编码和解码。应用程序能够在注重性能的场合使用二进制编码,在须要与其余远程处理框架进行交互的场合使用 XML 编码。在从一个应用程序域向另外一个应用程序域传输消息时,全部的 XML 编码都使用 SOAP 协议。出于安全性方面的考虑,远程处理提供了大量挂钩,使得在消息流经过通道进行传输以前,安全接收器可以访问消息和序列化流
有三类对象能够配置为 .NET Remoting 对象。您能够根据应用程序的须要来选择对象类型:
在 .NET Remoting 中,能够经过如下方式在应用程序之间传递对象:
public int myRemoteMethod (MyRemoteObject myObj)
public MyRemoteObject myRemoteMethod(String myString)
myObj.myNestedObject
对于 Marshal By Value (MBV,按值封送)的对象,当它在应用程序之间传递时,将建立一个完整的副本。
对于 Marshal By Reference (MBR,按引用封送)的对象,当它在应用程序之间传递时,将建立该对象的引用。当对象引用 (ObjRef) 到达远程应用程序后,将转变成“代理”返回给原始对象。
下面是一个简单 .NET Remoting 服务器对象的代码示例:
using System; using System.Runtime.Remoting; namespace myRemoteService { // Well Known Web Service object public class myRemoteObject : MarshalByRefObject { // Method myRemoteMethod public String myRemoteMethod(String s) { return "Hello World"; } } }
下面是客户端代码来访问这个对象:
using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using myRemoteService; public class Client { public static int Main(string[] args) { ChannelServices.RegisterChannel(new HttpChannel()); // Create an instance of a myRemoteObject class myRemoteObject myObj = ( myRemoteObject)Activator.GetObject(typeof(myRemoteObject), "http://myHost:7021/host/myRemoteObject.soap"); myObj. myRemoteMethod ("Hello World"); return 0; } }
对于那些具备在应用程序以外传送的对象引用的对象,将建立一个租用。租用具备一个租用时间。若是租用时间为 0,则租用过时,对象就断开与 .NET Romoting 框架的链接。一旦 AppDomain 中的全部对象引用都被释放,则下次垃圾回收时,该对象将被回收。租用控制了对象的生存期。
对象有默认的租用阶段。当客户端要在同一服务器对象中维护状态时,能够经过许多方法扩展租用阶段,使对象继续生存:
RemotingServices.GetLifetimeService
方法,从 AppDomain 的租用管理器获取服务器对象的租用。而后,客户端能够经过 Lease 对象调用 Lease.Renew 方法以延长租用。ILease::RenewOnCallTime
属性,则每次调用 Remoting 对象时,都会用 RenewOnCallTime 属性指定的总时间更新租用。.NET Remoting 对象能够集成在:
下面是一个 Remoting.cfg 文件的例子:
<configuration> <system.runtime.remoting> <application name="RemotingHello"> <service> <wellknown mode="SingleCall" type="Hello.HelloService, Hello" objectUri="HelloService.soap" /> </service> <channels> <channel port="8000" type="System.Runtime.Remoting.Channels.Http.HttpChannel, System.Runtime.Remoting" /> </channels> </application> </system.runtime.remoting> </configuration>
.NET 应用程序和 AppDomain 之间使用消息进行通讯。.NET 通道服务为这一通讯过程提供了基础传输机制。
.NET 框架提供了 HTTP 和 TCP 通道,可是第三方也能够编写并使用本身的通道。默认状况下,HTTP 通道使用 SOAP 进行通讯,TCP 通道使用二进制有效负载。
经过使用能够编写到集成混合应用程序中的自定义通道,能够插入通道服务(使用 IChannel)。
下面是一个加载通道服务的例子:
public class myRemotingObj { HttpChannel httpChannel; TcpChannel tcpChannel; public void myRemotingMethod() { httpChannel = new HttpChannel(); tcpChannel = new TcpChannel(); ChannelServices.RegisterChannel(httpChannel); // Register the HTTP Channel ChannelServices.RegisterChannel(tcpChannel); // Register the TCP Channel } }
.NET 序列化格式化程序对 .NET 应用程序和 AppDomain 之间的消息进行编码和解码。在 .NET 运行时中有两个本地格式化程序,分别为二进制 (System.Runtime.Serialization.Formatters.Binary
) 和 SOAP (System.Runtime.Serialization.Formatters.Soap
)。
经过实现 IRemotingFormatter 接口,并将其插入到上文介绍的通道中,能够插入序列化格式化程序。这样,您能够灵活地选择通道和格式化程序的组合方式,采用最适合应用程序的方案。
例如:您能够采用 HTTP 通道和二进制格式化程序(串行化二进制数据),也能够采用 TCP 通道和 SOAP 格式。
上下文是一个包含共享公共运行时属性的对象的范围。某些上下文属性的例子是与同步和线程紧密相关的。当 .NET 对象被激活时,运行时将检查当前上下文是否一致,若是不一致,将建立新的上下文。多个对象能够同时在一个上下文中运行,而且一个 AppDomain 中能够有多个上下文。
一个上下文中的对象调用另外一个上下文中的对象时,调用将经过上下文代理来执行,而且会受组合的上下文属性的强制策略影响。新对象的上下文一般是基于类的元数据属性选择的。
能够与上下文绑定的类称做上下文绑定类。上下文绑定类能够具备称为上下文属性的专用自定义属性。上下文属性是彻底可扩展的,您能够建立这些属性并将它们附加到本身的类中。与上下文绑定的对象是从 System.ContextBoundObject
导出的。
.NET 框架使用元数据和程序集来存储有关组件的信息,使得跨语言编程成为可能。.NET Remoting 使用元数据来动态建立代理对象。在客户端建立的代理对象具备与原始类相同的成员。可是,代理对象的实现仅仅将全部请求经过 .NET Remoting 运行时转发给原始对象。序列化格式化程序使用元数据在方法调用和有效负载数据流之间来回转换。
客户端能够经过如下方法获取访问 Remoting 对象所需的元数据信息:
配置文件(.config 文件)用于指定给定对象的各类 Remoting 特有信息。一般状况下,每一个 AppDomain 都有本身的配置文件。使用配置文件有助于实现位置的透明性。配置文件中指定的详细信息也能够经过编程来完成。使用配置文件的主要好处在于,它将与客户端代码无关的配置信息分离出来,这样在往后更改时仅须要修改配置文件,而不用编辑和从新编译源文件。.NET Remoting 的客户端和服务器对象都使用配置文件。
典型的配置文件包含如下信息及其余信息:
下面是一个配置文件示例:
<configuration> <system.runtime.remoting> <application name="HelloNew"> <lifetime leaseTime="20ms" sponsorshipTimeout="20ms" renewOnCallTime="20ms" /> <client url="http://localhost:8000/RemotingHello"> <wellknown type="Hello.HelloService, MyHello" url="http://localhost:8000/RemotingHello/HelloService.soap" /> <activated type="Hello.AddService, MyHello"/> </client> <channels> <channel port="8001" type="System.Runtime.Remoting.Channels.Http.HttpChannel, System.Runtime.Remoting" /> </channels> </application> </system.runtime.remoting> </configuration>
了解 .NET Remoting 如何工做以后,让咱们来看一下各类方案,分析如何在不一样的方案中充分发挥 .NET Remoting 的优点。下表列出了可能的客户端/服务器组合,以及默认状况下采用的底层协议和有效负载。请注意,.NET Remoting 框架是可扩展的,您能够编写本身的通讯通道和序列化格式化程序。
客户端 | 服务器 | 有效负载 | 协议 |
---|---|---|---|
.NET 组件 | .NET 组件 | SOAP/XML | http |
.NET 组件 | .NET 组件 | 二进制 | TCP |
托管/非托管 | .NET Web 服务 | SOAP/XML | http |
.NET 组件 | 非托管的传统 COM 组件 | NDR(网络数据表示形式) | DCOM |
非托管的传统 COM 组件 | .NET 组件 | NDR | DCOM |
Web 服务是能够经过 URL 寻址的资源,并经过编程向须要使用这些资源的客户端返回信息。客户端使用 Web Services 时没必要考虑其实现细节。Web Services 使用称为“合约”的严格定义的接口,此接口采用 Web 服务说明语言 (WSDL) 文件描述。
.NET Remoting 对象能够集成在 IIS 中,做为 Web 服务提供。任何可使用 WSDL 文件的客户端均可以按照 WSDL 文件中指定的合约,对 Remoting 对象执行 SOAP 调用。IIS 使用 ISAPI 扩展将这些请求路由到相应的对象。这样,Remoting 对象就能够做为 Web 服务对象来使用,从而充分发挥 .NET 框架基础结构的做用。若是您但愿不一样平台/环境的程序均可以访问对象,能够采用这种配置。这种配置便于客户端经过防火墙访问您的 .NET 对象。
图10 .NET 使用 HTTP-SOAP
默认状况下,HTTP 通道使用 SOAP 格式化程序。所以,若是客户端须要经过 Internet 访问对象,可使用 HTTP 通道。由于这种方法使用 HTTP,因此此配置容许经过防火墙远程访问 .NET 对象。只须要按前一节中介绍的方法将这些对象集成在 IIS 中,便可将它配置为 Web 服务对象。随后,客户端就能够读取这些对象的 WSDL 文件,使用 SOAP 与 Remoting 对象通讯。
默认状况下,TCP 通道使用二进制格式化程序。此格式化程序以二进制格式对数据进行序列化,并使用原始 socket 在网络中传送数据。若是对象部署在受防火墙保护的封闭环境中,此方法是理想的选择。这种方法使用 socket 在对象之间传递二进制数据,所以性能极佳。因为它使用 TCP 通道来提供对象,所以在封闭环境中具备低开销的优势。因为防火墙和配置的问题,此方法不宜在 Internet 上使用。
图11 .NET 使用 TCP 通道
能够经过 COM Interop Service 调用非托管的传统 COM 组件。当 .NET Remoting 客户端对象建立 COM 对象的实例时,该对象经过运行时可调用包装程序 (RCW) 来提供。其中,RCW 担当真正的非托管对象的代理。对于 .NET 客户,这些包装程序看起来和 .NET 客户端的任何其余托管类同样。但实际上,它们仅仅是托管 (.NET) 和非托管 (COM) 代码之间的封送调用。
一样地,您能够将 .NET Remoting 服务器对象提供给传统 COM 客户端。当 COM 客户端建立 .NET 对象的实例时,该对象经过 COM 可调用包装程序 (CCW) 来提供。其中,CCW 担当真正的托管对象的代理。
这两种方案都使用 DCOM 通讯。若是环境中既有传统的 COM 组件,又有 .NET 组件,那么这种互操做性将为您提供便利。
Microsoft .NET 框架提供了强大、可扩展、独立于语言的框架,适合开发可靠、可伸缩的分布式系统。.NET Romoting 框架提供了根据系统需求进行远程交互的强大手段。.NET Remoting 实现了与 Web 服务的无缝集成,并提供了一种方法,能够提供 .NET 对象以供跨平台访问。
Java RMI 与远程对象进行交互,其实现是须要基于 Java 的模型。此外,它没有使用 Web Services 和基于 HTTP 的消息传递。如今,已经出现了大量的软件来支持基于 Java 的 Web Services。JAX-WS (Java API for XML Web Services) 就是做为 Web Services 消息息和远程过程调用的规范。它容许一个调用基于Java的web服务使用Java RMI(即。,相对透明的程序员)。JAX-WS 的一个目标是平台互操做性。其 API 使用 SOAP 和WSDL。双方不须要 Java 环境。
在服务器端,进行下面的步骤来建立一个 RPC 端点:
在客户端:
JAX-RPC 执行流程以下:
图12 JAX-WS 调用流程
SOAP 虽然仍然是普遍部署应用,但在许多环境中不少厂商已经抛弃了 SOAP,转而使用其余更轻量、更容易理解、或者与 Web 交互模型更干净的机制。例如,Google 的 API 在2006年后就再也不支持 SOAP 接口,而是使用AJAX、XML-RPC 和 REST 做为替代。一个匿名的微软员工批评 SOAP 过于复杂,由于“咱们但愿咱们的工具来阅读它,而不是人”。无论上述言论是否准确,有一点是能够确定的,SOAP 显然是一个复杂和高度冗长的格式。
Web 浏览器最初的设计,是为 Web 页面提供非动态的交互模型。Web 浏览器是创建在同步的请求-响应(request-response)的交互模型。发送一个请求到服务器,服务器返回整个页面。在当时没有更新部分页面的好方法,而惟一可行的方法是利用帧,即将不一样的页面加载到每一帧,其实现是笨重的,也有很大的限制性。而改变了这一切的关键因素是:
AJAX 全称是 Asynchronous JavaScript And XML(异步的 JavaScript 和 XML)。让咱们看看这些三项:
AJAX 在推进 Web 2.0 的过程当中发挥了重要的,好比产生了不少高度交互的服务,如Google Maps、Writely等。基本上,它容许 JavaScript 发出HTTP 请求,获取和处理结果,刷新局部页面元素而不是整个页面。在大多数浏览器请求的格式以下:
new XMLHttpRequest() xmlhttp.open(“HEAD”, “index.html”, true)Tell object:
SOAP 在建立本身的消息传递协议时是基于HTTP,但实际上 REST (REpresentational State Transfer) 的方式才是保持 Web 的原理和使用 HTTP 协议的核心部分。
原始的 HTTP 协议已经定义了四个命令,清晰地映射到各类数据(定义为“资源”)操做:
REST 其背后的理念是使用这些 HTTP 命令来请求和操做数据。做为 HTTP协议的一部分,REST 使用 URL 来引用对象和操做。考虑这个 HTTP 操做列表的例子:
HTTP GET //www.waylau.com/parts
这个命令将返回一个 XML 文档,其中包含部分的列表。注意,返回的不是一个网页,只是一个包含所请求的数据 XML 数据结构。
<?xml version="1.0"?> <p:Parts xmlns:p="http://www.waylau.com" xmlns:xlink="http://www.w3.org/1999/xlink"> <Part id="00345" xlink:href="http://www.waylau.com/parts/00345"/> <Part id="00346" xlink:href="http://www.waylau.com/parts/00346"/> <Part id="00347" xlink:href="http://www.waylau.com/parts/00347"/> <Part id="00348" xlink:href="http://www.waylau.com/parts/00348"/> </p:Parts>
要特定部分的详细信息,发送一个HTTP get 命令:
HTTP GET //www.waylau.com/parts/00345
这将返回一个特定的信息部分:
<?xml version="1.0"?> <p:Part xmlns:p="http://www.waylau.com" xmlns:xlink="http://www.w3.org/1999/xlink"> <Part-ID>00345</Part-ID> <Name>Widget-A</Name> <Description>This part is used within the frap assembly</Description> <Specification xlink:href="http://www.waylau.com/parts/00345/specification"/> <UnitCost currency="USD">0.10</UnitCost> <Quantity>10</Quantity> </p:Part>
注意,上面例子简化了 partid 做为 URL 的参数。例如:
HTTP GET //www.waylau.com/parts?partid=00345
REST 不是 RPC,但也有相似的请求-响应模式。制定透明度请求、封送数据、解析响应这些不属于 REST。REST 应用很是普遍,如 Yahoo! Search API、Ruby on Rails、Twiter 和 Open Zing Services 等。
有些时候,不只仅是为了 RPC 和 Web Services 的须要,程序员只是想简化对网络上的数据的封送编组和解封的操做。Google Protocol Buffers 就是为序列化结构化数据提供了一种有效的机制,使它容易对网络上的数据进行编码和解码。Protocol Buffers 是一个紧凑的二进制格式比 XML 更简单、体积更小、速度更快。他们是独立于语言的,只定义数据类型。每一个消息是对数据名称、类型和值的结构化集合。消息结构定义在一个高级别的格式,相似于许多接口定义语言。而后文件能够根据你选择的语言来编译转换成与该语言相应的格式。Protocol Buffers 在 Google 中普遍使用。目前已经有超过48000种不一样的消息类型定义。Protocol Buffers 能够被运用在类 RPC 消息传递以及持久性存储(将数据转换成标准的串行形式写入到一个文件中)。下面是一个定义 Protocol Buffers 的例子:
message Person {
required string name = 1; required int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4; }
请注意,这只定义了数据结构,而不是功能。使用这个结构的例子是:
Person person; person.set_name("John Doe"); person.set_id(1234); person.set_email("jdoe@example.com"); fstream output("myfile", ios::out | ios::binary); person.SerializeToOstream(&output);
即便与紧凑的 XML 版本相比,Protocol Buffers 在时间和空间方面,解析将更加有效。下面是二者的对比.
这个是 XML 格式:
<person> <name>John Doe</name> <email>jdoe@example.com</email> </person>
这个没有编译的 Protocol Buffers 格式:
person { name: "John Doe" email: "jdoe@example.com" }
Protocol Buffers 产生的二进制消息大约是28字节长,解析耗时大概须要100-200ns。相比之下,XML 版本须要69个字节长(是 Protocol Buffers 的 2.5倍),耗时是5000-10000ns(是 Protocol Buffers 的 50倍)。
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。它基于 ECMAScript 的一个子集。JSON 采用彻底独立于语言的文本格式,可是也使用了相似于 C 语言家族的习惯(包括C、C++、C#、Java、JavaScript、Perl、Python 等)。这些特性使 JSON 成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成。JSON 不是一个诸如 Google Protocol Buffers 的二进制格式,所以适合使用基于 HTTP的消息传递。JSON 是能够做为 XML 替代品,在远程过程调用中,不少语言都支持 JSON-RPC。记住,这只是一个消息传递格式,JSON 并无试图提供 RPC 库来支持服务发现、绑定、托管和垃圾收集。