SOA体系结构(Service Oriented Architecture,面向服务的架构)基于服务组件模型,将应用程序的不同功能单元(称为服务)通过定义良好的接口契约联系起来,接口是采用中立方式进行定义的,独立于实现服务的硬件平台、操作系统和编程语言,使得构建在这样系统中的服务可以以一种统一的、通用的、灵活的方式进行交互。
SOA不是一项技术,也不是一个标准,而是一种架构。SOA架构独立于标准,提供了架构的蓝图。架构蓝图切开、分块和组合企业应用程序层,将组件“服务”化。SOA中的服务与业务功能相关联,但在技术上独立于业务功能的实现。
在Dirk Krafzig等人所著的Enterprise SOA一书中,对SOA做了如下定义:
SOA是一个软件架构,包含四个关键概念:应用程序前端、服务、服务库和服务总线,一个服务包含一个合约、一个或多个接口及一个实现。
其中:
应用程序前端——业务流程的所有者;
服务——提供业务的功能,可以供应用程序前端或其他服务使用;
实现——提供业务的逻辑和数据;
合约——为服务客户指定功能、使用和约束;
接口——物理地址公开功能;
服务库——存储SOA中各个服务的服务合约;
服务总线——将应用程序前端和服务连在一起。
图2-4描述了SOA架构中的组成。
图2-4 SOA架构的组成
在SOA架构中,必须有如下重要实体角色,如图2-5所示。
图2-5 SOA实体角色
服务请求者(Service Customer): 服务请求者是一个应用程序、一个软件模块或需要一个服务的另一个服务。它发起对服务管理中心中的服务查询(服务寻址),通过服务寻址,与目标服务建立通道来绑定服务,调用远程服务接口功能。服务请求者根据服务接口契约来执行远程服务。
服务提供者(Service Provider): 服务提供者是一个可通过网络寻址的进程实体(托管服务进程),与部署在托管服务进程下的SOA服务组件一起实现服务功能,服务提供者自动将服务组件提供的服务名发布到服务注册中心,以便服务请求者可以发现和访问该服务。服务提供者接收和执行来自请求者的请求,通过接口提供服务。
服务管理中心(Service Management Center): 服务管理中心是服务提供者与服务请求者的联系中介,提供服务提供者的服务注册管理,同时提供服务请求者的服务寻址查询。提供服务管理域中全部服务资源注册管理表,以及服务查询请求接口,允许感兴趣的服务请求者查找服务资源。
SOA体系结构中的每个实体都扮演着服务提供者、请求者和管理中心这三种角色中的某一种(或多种)。SOA体系结构中的操作包括:
服务注册 ——为了使服务可访问,需要服务提供者向服务管理中心注册服务以使服务请求者可以发现和调用它。
服务寻址 ——服务请求者定位服务,方法是查询服务注册中心来找到满足其服务需求的服务资源网络地址。
服务交互(远程服务调用) ——在完成服务寻址之后,服务请求者根据与目标服务提供者建立的网络通道来调用服务。
Web Services是SOA架构系统的一个实例,在SOA架构实现中的应用非常广泛。
由于互联网的兴起,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消息的例子:
在这个例子中,方法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只是一种消息格式,并未定义垃圾回收、对象引用、存根生成和传输协议。
下面是一个简单的例子:
其中<soap:Envelope>是SOAP消息中的根节点,是SOAP消息中必需的部分。<soap:Header>是SOAP消息中的可选部分,指消息头。<soap:Body>是SOAP中必需的部分,指消息体。
上面例子中的SOAP消息结构如图2-6所示。
图2-6 SOAP消息结构
SOAP 只是提供了一个标准化的消息结构,为了实现它往往需要用 WSDL 来描述 Web Services的方法。WSDL(Web Services Description Language)是基于XML的一种用于描述Web Services及如何访问Web Services的语言。
WSDL文档包括以下几个部分。
类型(Types): 定义Web Services使用的数据类型。
消息(n/a): 描述使用消息的数据元素或参数。
接口(Interface): 描述服务提供的操作,包括操作及每个操作使用的输入和输出消息。
绑定(Binding): 为每个端口定义消息格式和协议细节。例如,它可以定义RPC式的消息。
服务(Service): 系统功能相关的集合,包括其关联的接口、操作、消息等。
终点(Endpoint): 定义地址或Web Services的连接点。
操作(Operation): 定义SOAP的动作,以及消息编码的方式。
下面是一个WSDL 2.0版本的例子:
从微软的产品角度来看,可以说.NET Remoting就是对DCOM的一种升级,它改善了很多功能,并极好地融合到.NET平台下。
Microsoft .NET Remoting 提供了一种允许对象通过应用程序域与另一对象进行交互的框架。这种框架提供了多种服务,包括激活和生存期支持,以及负责与远程应用程序进行消息传输的通信通道。格式化程序用于消息在通过通道传输之前,对其进行编码和解码。应用程序可以在注重性能的场合中使用二进制编码,在需要与其他远程处理框架进行交互的场合中使用XML编码。从一个应用程序域向另一个应用程序域传输消息时,所有的XML编码都使用SOAP协议。出于安全性方面的考虑,远程处理提供了大量挂钩,使得在消息流通过通道进行传输之前,安全接收器能够访问消息和序列化流。
.NET Remoting对象
有三类对象可以配置为.NET Remoting对象,可以根据应用程序的需要选择对象类型。
Single Call(单一调用对象): Single Call能且只能为一个请求提供服务。在需要对象完成的工作量有限的情况下Single Call非常有用。Single Call 对象通常不要求存储状态信息,并且不能在方法调用之间保留状态信息。但是,Single Call对象可以配置为负载平衡模式。
Singleton Objects(单一元素对象): Singleton Objects可以为多个客户端提供服务,因此可以通过保存客户端调用的状态信息来实现数据共享。当客户端之间需要明确地共享数据,并且不能忽略创建和维护对象的开销时,这类对象非常有用。
Client-Activated Objects(CAO,客户端激活的对象): CAO是服务器端的对象,根据来自客户端的请求激活这些对象。这种激活服务器对象的方法与传统的COM coclass激活方法非常相似。当客户端使用“new”运算符提交对服务器对象的请求时,会向远程应用程序发送一个激活请求消息。随后,服务器创建被请求类的实例,并向调用它的客户端应用程序返回ObjRef。然后,使用此ObjRef在客户端上创建代理。客户端的方法调用将在代理上执行。客户端激活的对象可以为特定的客户端(不能跨越不同的客户端对象)保存方法调用之间的状态信息。每个“new”调用都会向服务器类型的独立实例返回代理。
在.NET Remoting中,可以通过以下方式在应用程序之间传递对象:
作为方法调用中的参数,例如,public int myRemoteMethod (MyRemoteObject myObj);
方法调用的返回值,例如,public MyRemoteObject myRemoteMethod(String myString);
访问.NET组件的属性或字段得到的值,例如,myObj.myNestedObject。
对于Marshal By Value(MBV,按值封送)的对象,当它在应用程序之间传递时,将创建一个完整的副本。
对于Marshal By Reference(MBR,按引用封送)的对象,当它在应用程序之间传递时,将创建该对象的引用。当对象引用(ObjRef)到达远程应用程序后,将转变成“代理”返回给原始对象。
下面是一个简单.NET Remoting服务器对象的代码示例:
下面是客户端代码访问这个对象的代码示例:
租用生存期
对于那些在应用程序之外传送的对象,将创建一个租用。租用有一个租用时间。如果租用时间为0,则租用过期,对象就断开与.NET Romoting框架的连接。一旦AppDomain中的所有对象引用都被释放,则在下次垃圾回收时,该对象将被回收。租用控制了对象的生存期。
对象有默认的租用阶段。当客户端要在同一服务器对象中维护状态时,可以通过许多方法扩展租用阶段,使对象继续生存。
服务器对象可以将其租用时间设置为无限,这样Remoting在垃圾回收时就不会回收此对象。
客户端可以调用RemotingServices.GetLifetimeService方法,从AppDomain的租用管理器中获取服务器对象的租用。然后,客户端可以通过Lease对象调用Lease.Renew方法以延长租用。
客户端可将AppDomain的租用管理器作为特定的租用注册负责人。当Remoting对象的租用过期时,租用管理器将通知负责人提出续租的申请。
如果设置了 ILease::RenewOnCallTime 属性,则每次调用 Remoting 对象时,都会用RenewOnCallTime属性指定的总时间更新租用。
集成.NET Remoting对象
.NET Remoting对象可以集成在:
托管可执行项——.NET Remoting对象可以集成在任何常规的.NET EXE或托管服务中。
.NET组件服务——.NET Remoting对象可以集成在.NET组件服务基础结构中,以便使用各种COM+服务,例如,事务、JIT和对象池等。
IIS——.NET Remoting对象可以集成在Internet Information Server(IIS)中。默认情况下,集成在IIS中的Remoting对象通过HTTP通道接收消息。要在IIS中集成Remoting对象,必须创建一个虚拟的根,并将remoting.config文件复制到IIS中。包含Remoting对象的可执行文件或DLL应放在IIS根指向的目录下的bin目录中。需要注意的是,IIS根名称应该与配置文件中指定的应用程序名称相同。当第一个消息到达应用程序时,Remoting 配置文件会自动加载。使用这种方法,可以将.NET Remoting 对象作为 Web服务。
下面是一个Remoting.cfg文件的例子:
通道服务 (System.Runtime.Remoting.Channels)
.NET应用程序和AppDomain之间使用消息进行通信。.NET通道服务为这一通信过程提供了基础传输机制。
.NET框架提供了HTTP和TCP通道,但是第三方也可以编写并使用自己的通道。默认情况下,HTTP通道使用SOAP进行通信,TCP通道使用二进制有效负载进行通信。
通过使用编写到集成混合应用程序中的自定义通道,可以插入通道服务(使用IChannel)。
下面是一个加载通道服务的例子:
序列化格式化程序(System.Runtime.Serialization.Formatters)
.NET 序列化格式化程序对.NET 应用程序和 AppDomain 之间的消息进行编码和解码。在.NET 运行时中有两个本地格式化程序,分别为二进制(System.Runtime.Serialization.Formatters.Binary)和SOAP(System.Runtime.Serialization.Formatters.Soap)。
通过实现 IRemotingFormatter 接口,并将其插入上文介绍的通道中,可以插入序列化格式化程序。这样,我们可以灵活地选择通道和格式化程序的组合方式,采用最适合应用程序的方案。
例如,可以采用HTTP通道和二进制格式化程序(串行化二进制数据),也可以采用TCP通道和SOAP格式。
Remoting上下文
上下文是一个包含共享公共运行时属性的对象的范围。某些上下文属性的例子是与同步和线程紧密相关的。当.NET对象被激活时,运行时将检查当前上下文是否一致,如果不一致,则创建新的上下文。多个对象可以同时在一个上下文中运行,并且一个AppDomain中可以有多个上下文。
一个上下文中的对象调用另一个上下文中的对象时,调用将通过上下文代理来执行,并且会受组合的上下文属性的强制策略影响。新对象的上下文通常是基于类的元数据属性选择的。
可以与上下文绑定的类称作上下文绑定类。上下文绑定类可以具有称为上下文属性的专用自定义属性。上下文属性是完全可扩展的,我们可以创建这些属性并将它们附加到自己的类中。与上下文绑定的对象是从System.ContextBoundObject导出的。
.NET Remoting元数据和配置文件
元数据
.NET 框架使用元数据和程序集来存储有关组件的信息,使得跨语言编程成为可能。.NET Remoting使用元数据来动态创建代理对象。在客户端创建的代理对象具有与原始类相同的成员。但是,代理对象的实现仅仅将所有请求通过.NET Remoting 运行时转发给原始对象。序列化格式化程序使用元数据在方法调用和有效负载数据流之间来回转换。
客户端可以通过以下方法获取访问Remoting对象所需的元数据信息。
服务器对象的.NET程序集:服务器对象可以创建元数据程序集,并将其分发给客户端。在编译客户端对象时,客户端对象可以引用这些程序集。在客户端和服务器都是托管组件的封闭环境中,这种方法非常有用。
Remoting对象可以提供WSDL文件,用于说明对象及其方法。所有根据WSDL文件读取和生成SOAP请求的客户端都可以调用此对象,或使用SOAP与之通信。使用与.NET SDK一同分发的SOAPSUDS.EXE工具,.NET Remoting服务器对象可以生成具有元数据功能的 WSDL 文件。当某个组织希望提供所有客户都能访问和使用的公共服务时,这种方法非常有用。
.NET客户可以使用SOAPSUDS工具从服务器上下载XML架构(在服务器上生成),生成仅包含元数据(没有代码)的源文件或程序集。我们可以根据需要将源文件编译到客户端应用程序中。如果多层应用程序中某一层的对象需要访问其他层的 Remoting对象,则经常使用此方法。
配置文件
配置文件(.config文件)用于指定给定对象的各种Remoting特有信息。通常情况下,每个AppDomain都有自己的配置文件。使用配置文件有助于实现位置的透明性。配置文件中指定的详细信息也可以通过编程来完成。使用配置文件的主要好处在于,它将与客户端代码无关的配置信息分离出来,这样在日后更改时仅需要修改配置文件,而不用编辑和重新编译源文件。.NET Remoting的客户端和服务器对象都使用配置文件。
典型的配置文件包含以下信息及其他信息:
集成应用程序信息;
对象名称;
对象的URI;
注册的通道(可以同时注册多个通道);
服务器对象的租用时间信息。
下面是一个配置文件示例:
.NET Remoting方案
了解.NET Remoting 如何工作之后,让我们来看一下各种方案,分析如何在不同的方案中充分发挥.NET Remoting的优势。表2-1列出了可能的客户端/服务器组合,以及默认情况下采用的底层协议和有效负载。请注意,.NET Remoting 框架是可扩展的,我们可以编写自己的通信通道和序列化格式化程序。
表2-1 可能的客户端/服务器组合及默认情况下采用的底层协议和有效负载
使用HTTP-SOAP
Web 服务是可以通过 URL 寻址的资源,并通过编程向需要使用这些资源的客户端返回信息。客户端使用Web Services时不必考虑其实现细节。Web Services使用称为“合约”的严格定义的接口,此接口采用Web服务说明语言(WSDL)文件描述。
.NET Remoting对象可以集成在IIS中,作为Web服务。任何可以使用WSDL文件的客户端都可以按照WSDL文件中指定的合约,对Remoting对象执行SOAP调用。IIS使用ISAPI扩展将这些请求路由到相应的对象。这样,Remoting对象就可以作为Web服务对象来使用,从而充分发挥.NET 框架基础结构的作用。如果希望不同平台/环境的程序均能够访问对象,则可以采用这种配置。这种配置便于客户端通过防火墙访问.NET对象。
使用SOAP-HTTP通道
默认情况下,HTTP 通道使用SOAP 格式化程序,如图2-7所示。因此,如果客户端需要通过Internet访问对象,则可以使用HTTP通道。因为这种方法使用HTTP协议,所以此配置允许通过防火墙远程访问.NET对象。只需要按前面介绍的方法将这些对象集成在IIS中,即可将它配置为 Web 服务对象。随后,客户端就可以读取这些对象的 WSDL 文件,使用 SOAP 与Remoting对象通信。
图2-7 .NET使用SOAP-HTTP通道
使用TCP通道
默认情况下,TCP通道使用二进制格式化程序,如图2-8所示。此格式化程序以二进制格式对数据进行序列化,并使用原始socket在网络中传送数据。如果对象部署在受防火墙保护的封闭环境中,则此方法是理想的选择。这种方法使用socket在对象之间传递二进制数据,因此性能极佳。由于它使用TCP通道来提供对象,因此在封闭环境中具有低开销的优点。由于防火墙和配置的问题,此方法不宜在Internet上使用。
图2-8 .NET使用TCP通道
使用非托管的COM组件
可以通过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消息和远程过程调用的规范。JAX-WS的一个目标是平台互操作性,其API使用SOAP和WSDL,双方不需要Java环境。
创建一个RPC端点
在服务器端通过下面的步骤创建一个RPC端点。
定义一个接口(Java接口);
实现服务;
创建一个发布者,用于创建服务的实例,并发布一个服务名字。
在客户端:
创建一个代理(客户端存根)——使用wsimport命令根据WSDL文档创建一个客户机存根;
编写一个客户端,通过代理创建远程服务的一个实例(存根),调用它的方法。
JAX-RPC执行流程如下:
Java客户机调用存根上的方法(代理);
存根调用适当的Web服务;
Web服务器被调用并执行JAX-WS框架;
框架调用实现;
实现返回结果给该框架;
该框架将结果返回给Web服务器;
服务器将结果发送给客户端存根;
客户端存根返回信息给调用者。
JAX-WS调用流程如图2-9所示。
图2-9 JAX-WS调用流程
SOAP 虽然仍然广泛用于部署应用,但在许多环境中很多厂商已经抛弃了 SOAP,转而使用其他更轻量、更容易理解,或者与Web交互模型更干净的机制。例如,Google的API在2006年后就不再支持SOAP接口,而是使用AJAX、XML-RPC和REST作为替代者。一个匿名的微软员工批评SOAP过于复杂,因为“我们希望我们的工具来阅读它,而不是人”。不管上述言论是否准确,有一点是可以肯定的,SOAP显然是一个复杂和高度冗长的格式。
AJAX
Web浏览器最初的设计是为Web页面提供非动态的交互模型。Web浏览器是建立在同步的请求-响应(request-response)上的交互模型。发送一个请求到服务器,服务器返回整个页面。当时没有更新部分页面的好方法,唯一可行的方法是利用帧,即将不同的页面加载到每一帧,其实现是笨重的,也有很大的限制性。改变了这一切的关键因素是:
文档对象模型(Document Object Model)和JavaScript的出现,使得可以以编程的方式来更改Web页面的各个部分;
AJAX提供了以非阻塞方式与服务器进行交互的方法,即允许底层JavaScript在等待服务器结果时,用户仍然可以与页面进行交互。
AJAX的全称是Asynchronous JavaScript And XML(异步的JavaScript和XML),特点如下:
它是异步的,因为客户端在等待服务器结果时不会被阻塞。
AJAX集成到了JavaScript中,作为浏览器解释Web页面的一部分。JavaScript使用HTTPRequest来调用AJAX请求。JavaScript也可能修改文档对象模型,定义页面的样子。
数据以XML文档形式发送和接收(在后期发展中,AJAX也支持其他的数据格式,比如JSON)。
AJAX在推动Web 2.0发展的过程中发挥了重要的作用,比如产生了很多高度交互的服务,如Google Maps、Writely等。基本上,它允许JavaScript发出HTTP请求,获取并处理结果,刷新局部页面元素而不是整个页面。在大多数浏览器中请求的格式如下:
随着HTTP API、云服务、敏捷开发、持续交付、DevOps理论的发展和实践,以及基于容器技术来部署应用和服务的日渐成熟,面向服务的架构也在不断演变,其中包括 REST 风格的架构、微服务架构、Serverless架构等架构形式,本章的后面几节也会一一讲解这些新兴架构的风格。