UML软件工程组织

用 Indigo 开发和运行连接系统的指南
转自:www.microsoft.com 作者:Don Box
摘要 本文描述新的编程框架集,它们是即将推出的 Windows 版本 "Longhorn" 的一部分。"Indigo" 是该框架的代号,它将为面向服务的设计提供丰富的支持,而面向服务的设计是对传统的面向对象的方法的补充。Indigo 会将 .NET Remoting、ASMX 和 .NET Enterprise Services 的最佳功能结合到一个统一的编程和管理模型中。Indigo 对标准协议(如 HTTP、XML 和 SOAP)提供深入的支持,这使得集成各种应用程序和服务变得更加容易,而且不会牺牲安全性或可靠性。
本页内容
面向服务的设计基础 面向服务的设计基础
什么是 Indigo? 什么是 Indigo?
对 Indigo 进行编程 对 Indigo 进行编程

"Longhorn" 是即将推出的 Microsoft® Windows® 版本的代号,它将包括一个统一的编程模型和通信基础结构,以用于开发连接的系统。这个基础结构(代号 "Indigo")通过面向服务的编程模型来简化开发,在该模型中,将使用异步消息传递功能来编写自治程序。为了使用这个编程模型,Indigo 提供了一套丰富的技术用来创建、使用、处理和传送消息。

Indigo 的一个主要优点是,它会为所有基于公共语言运行库 (CLR) 的远程技术提供一个统一的编程模型和协议栈。Indigo 会将 .NET Remoting、ASMX、System.Messaging 和 .NET Enterprise Services 的最佳功能结合到一个框架中,使得开发人员能够自由组合各种功能,如声明性事务处理、可插接式侦听器和传输以及基于 XML 架构的序列化。

而且,Indigo 统一了各种传输协议(HTTP、TCP、UDP、IPC)、安全机制(公钥和对称密钥、证书)、拓扑(点对点、通过中间物的端到端、对等、发布和订阅)和保证(事务处理、可靠、持久),从而使得 Indigo 能够为许多现有系统提供丰富的连接性。

Indigo 使得面向服务的编程可用于广泛的主流应用程序中。通过同时使用 CLR 和 Windows 的各种工具,Indigo 可用在对性能敏感的情形(如单主机甚至是单进程集成)中。与当前的技术相比,这种尺度不变性使得基于 Indigo 的服务可被更广泛的部署选项访问。

因为互操作性和范围非常重要,所以 Indigo 对 XML 和 SOAP 均提供了深入的支持,而且还会为不断涌现的高级 Web 服务协议提供支持,高级 Web 服务协议可为基于 SOAP 的应用程序添加安全性、可靠性和事务处理。

Indigo 是 Windows Longhorn 的一部分,可供每个 Longhorn 应用程序使用。对于 Windows XP 和 Windows Server™ 2003 来说,Indigo 还可单独下载。由于不同操作系统之间具有内在区别,所以在 Longhorn 上可能只能使用 Indigo 的少量高级功能或优化。但是,无论部署到哪个操作系统上,Indigo 都会为开发人员提供一个面向服务的一致编程模型。

面向服务的设计基础

Indigo 基于面向服务的结构和编程的原则。面向服务的设计是对面向对象设计的重要补充,面向对象的设计应用了从组件软件、面向消息的中间件和分布式对象计算得到的教训。面向服务的设计与面向对象设计的主要区别在于如何定义“应用程序”一词。面向对象的开发主要关注从互相依赖的类库构建的应用程序,而面向服务的开发则主要关注从一组自治服务构建的系统。这种区别对人们就开发经验进行的假设具有深远的影响。

在 Indigo 中,服务只是一个用户通过消息交换来与之交互的程序。系统就是一组部署的服务。构建每一个服务都是为了使其持续运行 — 某个特定服务的可用性和稳定性至关重要。构建由多个服务聚合的系统是为了使其允许进行更改 — 该系统必须适应在原始服务和客户端部署了很久之后才新出现的服务,而且这些情况下不得中断功能。

面向服务的开发基于如下所示的四个基本原则:

边界明显 面向服务的应用程序通常会包括跨越较长地理距离、多个信任机构和不同执行环境的服务。从复杂程度和性能的角度看,穿越这些边界所带来的成本并非微不足道。面向服务的设计了解这些成本,从而对边界交叉非常重视。因为每个跨边界的通信都有可能带来很高的成本,所以面向服务的设计基于显式的消息传递模型,而不基于隐式的方法调用。与分布式对象相比,面向服务的模型将跨服务的方法调用视为一种专用的实现技术(而非基元构造)— 某个特定的交互可以作为方法调用实现,这一事实是在服务边界外部看不到的专用实现细节。

尽管面向服务的设计不强调使用网络范围调用堆栈的 RPC 样式这一概念,但是它可以支持强因果性概念。通常会在消息中明确表明某个特定的消息属于哪个(哪些)消息链,这对于消息关联和实现几个常用的并发模型非常有用。

边界明显这一概念不仅适用于服务之间的通信,而且还适用于开发人员之间的通信。即使对于所有服务都部署到一个位置的情形,以下情况也非常普遍:每个服务的开发人员会跨越地理、文化和/或组织边界。其中的每个边界都会增加开发人员之间的通信成本。面向服务的设计通过降低必须在服务边界之间共享的抽象数量和复杂程度,来适应这种模型。通过尽可能缩小服务的覆盖范围来减少开发组织之间的交互和通信。在面向服务的设计中有一个永恒的主题,那就是简单和通用不是奢侈品,而是重要的生存技能。

服务自治 面向服务的设计反映了真实的世界,因为它不会假设存在全知或全能的圣贤,即不存在能够感知和控制运行系统所有部分的服务。这个服务自治概念表现在开发的几个方面,最明显的位置是部署和版本控制领域。

面向对象的程序往往作为一个单元来部署。尽管在二十世纪九十年代尽力使类能够独立部署,但事实证明,对于大多数开发组织来说,支持与组件进行面向对象的交互所要求的原则是不切实际的。在面临对于面向对象的接口进行版本控制的复杂性时,许多组织在如何部署面向对象的代码方面都变得极其保守。.NET 框架的 XCOPY 部署和专用程序集功能的普及性预示了这种趋势。

面向服务的开发与面向对象的开发不同,它假设以原子方式部署应用程序是例外情况,而不是常规情况。尽管各个服务几乎总是以原子方式部署的,但是整个系统/应用程序的聚合部署状态很少保持不变。单个的服务通常在开发任何商业应用程序(更不用说部署到空白系统了)很久之前就进行部署了。Amazon.com 就是这种“有产品就有顾客”哲学的一个示例。Amazon 的开发人员根本未曾料到,可通过多种方法使用他们的服务来构建新颖有趣的应用程序。

通常,面向服务的应用程序的拓扑会随着时间的推移而演变,有时无需管理员或开发人员的直接干预。可将新服务引入面向对象的系统的程度既取决于服务交互的复杂程度,又取决于以通常方式进行交互的服务的普遍性。面向服务的设计通过降低服务交互的复杂程度来鼓励使用可增加普遍性的模型。当服务特定的假设渗透到服务的公共外观时,很少有服务能够合理地模仿该外观并充当合理的替换服务。

自治服务这一概念还会对处理失败的方式产生影响。所部署的多个对象将作为商业应用程序在同一个执行上下文中运行。面向服务的设计假设这种情形是例外情况,而不是常规情况。因此,这些服务期望商业应用程序可以在没有通知(而且通常没有任何通知)的情况下失败。为了维持系统的完整性,面向服务的设计使用各种技术来处理部分失败模式。在面向服务的系统中,这些技术(如事务处理、持久队列、冗余部署和故障转移)相当普遍。

因为所部署的许多服务都会在公共网络(如 Internet)上运行,所以面向服务的开发不仅会假设传入的消息数据可能是错误的,而且会假设它可能是出于恶意目的而传输的。面向服务的结构可进行自我保护,方法是要求应用程序证明已被授予了所有必需的权利和特权,以使得所有消息发送者都承担举证责任。面向服务的结构与服务自治的概念一致,它总是依赖以管理方式托管的信任关系,以便避免使用传统 Web 应用程序中常见的每服务身份验证机制。

服务共享架构和协定(而非类)面向对象的编程鼓励开发人员用类的形式创建新抽象。现代的大多数开发环境不仅使定义新类变得不重要,而且随着类数量的不断增加,现代的 IDE 在指导您完成开发过程方面能够做得更好(如 IntelliSense® 之类的功能会为特定的方案提供一个更有针对性的选项列表)。

类是非常方便的抽象,这是由于它们会在单个的特定单元中共享结构和行为。面向服务的开发没有这样的构造。相反,服务仅基于架构(用于结构)和协定(用于行为)来进行交互。每个服务都会公布一个协定,该协定描述它可以发送和/或接收的消息的结构,以及针对这些消息的一定程度的排序约束。这种结构和行为之间的严格分离大大简化了部署,这是由于分布式对象概念(如按值封送)要求使用公共的执行和安全环境,这与自治计算的目标直接存在冲突。

服务本身不处理类型或类;而只是处理服务所支持的计算机可读取和可验证的合法“细节”说明。鉴于面向服务的应用程序的开发和部署方式内在的分布式特性,计算机可验证性和验证非常重要。与传统的类库不同,服务必须非常仔细地验证到达每个消息的输入数据。使得结构基于计算机可验证的架构和协定,不仅为开发人员和基础结构提供了保护单个服务完整性所需的提示,而且还为他们提供了保护整个应用程序的完整性所需的提示。

因为给定服务的协定和架构在广泛的空间和时间内可见,所以面向服务的设计要求协定和架构不会随着时间的推移而改变。通常情况下,不可能将架构和/或协定中的更改传播到曾经遇到过某个服务的所有各方。因此,面向服务的设计中所使用的协定和架构往往比面向对象的传统接口灵活。通常,服务使用诸如 XML 元素通配符(如 xsd:any)之类的功能和可选的 SOAP 头块,按照不中断已部署代码的方式来演变服务。

基于策略来确定服务兼容性 面向对象的设计通常会混淆结构兼容性和语义兼容性。面向服务的设计则单独处理这两种兼容性。结构兼容性基于协定和架构,并且可以由基于计算机的技术(如探测数据包、验证防火墙)来验证(如果不是强制的话)。语义兼容性则基于功能和要求策略形式的显式声明。

每个服务都以计算机可读取的策略表达式的形式来公布其功能和要求。策略表达式指出为了使得服务能够正常运行,必须满足哪些条件和保证(称作断言)。策略断言由一个稳定的全局唯一名称来标识,无论该断言应用于哪个服务,这个唯一名称的含义都不随时间和空间的变化而改变。策略断言还可以有一些参数,以确保断言会被正确解释。单个的策略断言对于系统基本上不透明,这使得实现能够应用简单的建议逻辑以确定服务兼容性。

什么是 Indigo?

Indigo 是一组基于 .NET 框架的用来构建和运行连接系统的技术。Indigo 是从 COM、COM+ 和 MSMQ 开始的演变过程中的下一步,并会通过 .NET Remoting、ASMX、System.Messaging 和 .NET Enterprise Services 继续这个演变过程。Indigo 包含了这些技术,并为使用任何符合 CLR 的语言开发服务提供了一个统一的编程体验。

MAissues0401Indigofig01

图 1 Indigo 结构

正如图 1 所示,Indigo 的主要子系统是 Indigo 服务模型、Indigo 连接器、宿主环境以及系统和消息服务。

Indigo 服务模型 Indigo 服务模型使用任何面向 CLR 的语言,使得面向服务的开发非常明确和简单。开发人员使用声明性属性来标记他们的类型的哪些方面应当形成服务的外部协定。Indigo 服务模型支持两种协定:数据协定和服务协定。数据协定在本质上是有关结构的,它描述 CLR 类型的外部格式。数据协定类似于 XML 架构定义。服务协定在本质上则是有关行为的,它描述用来与服务进行交互的消息交换的模式。服务协定类似于 WSDL portType 定义。Indigo 服务模型既支持代码优先的开发模型,也支持协定优先的开发模型,其中包括对于 Web 服务描述语言 (WSDL) 和 XML 架构导入/导出的支持。

Indigo 服务模型的主要功能是将用户定义的代码与传入的消息进行关联。这是通过使用 [ServiceMethod] 属性来完成的。用该属性标记的方法支持单向消息处理或相互关联的请求/答复消息处理,具体情况取决于 OneWay 属性的值。标记为服务方法的方法可被声明为从整个消息或(如果需要的话)类型化参数的角度来工作。消息本身可被视为强类型化对象或 XML 信息集 (infoset)。

Indigo 服务模型为服务提供实例和上下文管理。Indigo 会将传入的消息传送给用户定义的服务类型的实例。开发人员使用声明性属性来控制实例如何与传入的消息相关联,以及 Indigo 的会话管理功能如何将多个消息传送给一个公共会话对象。另外,Indigo 服务模型使开发人员能够显式控制执行上下文(因果性、调用上下文、事务处理)在协作服务之间的传播。

最后,Indigo 服务模型会提供可自动实现安全性和可靠性的声明性行为。当方法进入和离开服务时,这些属性会影响 Indigo 的行为,并强加一些最初由开发人员声明的策略,随后由管理员通过配置使其变得具体。

与 ASMX 一样,Indigo 使服务能够在不共享程序集或源代码的情况下使用。另外,Indigo 为 ASMX 兼容性提供了支持,ASMX 兼容性将允许用相对较少的修改使许多基于 ASMX 的 Web 服务变成基于 Indigo 的服务。

Indigo 连接器 Indigo 连接器是一个托管框架,它提供基于消息的安全可靠的连接。Indigo 连接器是一个分层的 I/O 框架,它基于 SOAP 数据和处理模型。Indigo 连接器允许您使用少量的概念(端口、消息、信道),构建独立于传输或目标平台且基于消息的应用程序,所有这些概念都将在本文的后面部分进行介绍。Indigo 连接器在设计上体积很小并且具有优异的吞吐量和执行等待时间特征,这使其可用在对性能敏感的系统中。

Indigo 连接器基于端口和信道。端口表示网络空间中的某个位置,并为它所承载的所有服务提供一个基本的统一资源标识符 (URI)。信道是一种 I/O 设备,服务使用它来通过服务端口收发消息。一些信道是将基础通信机制映射到 Indigo 的传输信道。其他一些信道是在一个或多个传输信道上提供特定消息处理规则或服务质量的消息处理信道。Indigo 连接器还包括一个消息编码器,该编码器允许向系统中添加编码,以便在抽象数据模型(由 Indigo 在内部使用)和字节序列(由特定传输使用)之间进行转换。序列化层采用各种 CLR 对象和类,并对它们进行提取,形成一个适合在各种服务边界之间进行共享的简单数据模型。

Indigo 连接器显式支持中间物(包括防火墙、代理和网关)。Indigo 连接器的中间模型与 SOAP 处理模型一致。尽管支持与服务之间的直接通信,但是 Indigo 假设直接连接是例外情况,而不是常规情况。相反,Indigo 通过调整配置、元数据和服务策略来为消息确定最佳路线,并会根据需要利用网关或代理。而且,Indigo 连接器通过提供消息验证、安全强制执行和基于内容的负载平衡,大大简化了应用程序级网关的创建,从而可以免除后端服务的工作负荷。

中间物的存在通常意味着在发送方和最终接收方之间没有端到端的传输连接。为了确保消息在这些情形下能够可靠传递,Indigo 使用 WS-ReliableMessaging 协议通过 SOAP 实现了可靠的端到端消息处理。Indigo 连接器提供两种可靠的消息处理模式:快速和持久。当消息从来源传递到目标时,快速消息处理使用内存中的缓冲区来保存传输中的消息。如果发送方在发送消息之后立即发生了崩溃,则无法保证快速消息会被发送。相反,持久消息使用磁盘上的缓冲区,即使在系统发生崩溃时,它也能提供传递保证。

Indigo 连接器为安全消息处理(安全消息处理会提供独立于消息传输的端到端保护)提供了内在的支持。基于消息的模式和基于会话的模式均受支持。基于会话的安全性是首选模型,这是由于签名和加密均可使用更轻量的、在建立会话的过程中进行协商的会话密钥。基于消息的安全性对于断开连接或数据报情形是必需的,在这些情形下,消息的接收方在传输时不可用。这两种模式都支持 WS-Security 系列的协议(WS-Trust 和 WS-Federation),这样可确保即使在面临中间物时,也能与非 Indigo Web 服务进行互操作。如有可能(例如,在发送方和接收方之间使用直接 TCP/安全套接字层 (SSL) 连接时),Indigo 还会使用传输级安全性。

宿主环境 Indigo 连接器和服务模型可被显式加载并用在系统上的任何 AppDomain 中。这使得 Indigo 非常适于将面向服务的外观呈现于任何托管代码段之前。如果在 AppDomain 中初始化 Indigo 连接器,则无论以哪种方式创建 AppDomain,都可为您提供基本的“服务拨号音”。事实上,如何启动 AppDomain 完全超出了 Indigo 连接器的角色范围。

为了使 Indigo 可被尽可能多的开发人员访问,已经对当前在 Windows 平台上使用的各种宿主系统(如 svchost.exe 和 dllhost.exe)进行了改编,以便为基于 Indigo 的服务提供专门支持。在这些宿主系统中,最有趣的是 ASP.NET 和 IIS。

随着 Indigo 的发布,ASP.NET 现在支持一个用来管理 AppDomains 和 OS 进程的系统范围的工具。这就允许基于 Indigo 的服务可根据到达任何正确配置的传输的消息进行按需启动。Indigo 为 TCP、HTTP 和进程间通信 (IPC) 传输提供内置的支持。Indigo 宿主允许应用程序能够像 IIS 模型一样与 URI 后缀相关联,以便将 URI 后缀映射到 VRoot。但是,与 IIS 不同的是,Indigo 不会与特定的传输协议或编程模型相关联。

使用 ASP.NET 宿主的基于 Indigo 的服务会获取另外一组管理服务,其中包括进程/AppDomain 级别以及单个服务级别的可配置回收和钝化。Indigo 还支持对进程和 AppDomai 进行基本的运行状况监视。对于 ASP.NET 的宿主工具来说,一个值得注意的新功能是服务代码能够参与服务管理(启动、停止)请求。这有助于具有中间工作的服务避免在不合适的时间处理回收。大多数基于服务器的新 Indigo 服务都将使用 ASP.NET 宿主功能,这将导致对于所有的托管中间层代码都拥有一个相同的配置和管理体验。

系统和消息服务 基于 Indigo 的系统有几个为所有服务提供重要功能的系统服务。标识联盟就是其中的一个系统服务。即将在 Windows 中推出的安全系统版本支持一个联盟服务,该服务允许对来自外部信任边界的标识进行管理和验证。Windows 联盟服务使用 WS-Federation 来安全地代理服务和相应信任机构之间的身份验证。

Indigo 还为事务编程提供了重要的支持。启用了 Indigo 的 Windows 版本支持一个基于服务的事务处理管理器,该事务管理器可通过 System.Transactions 框架或 WS-AtomicTransactions 协议来访问。新的 System.Transactions 框架使整个平台(它支持 SQL Server™、ADO.NET、MSMQ、分布式事务处理协调器 (DTC) 等)上的事务编程都变得简单高效。System.Transactions 既支持基于 ITransaction 接口的显式编程模型,也支持隐式的编程模型(Indigo 自动管理其中的事务处理)。这两个模型都可用于基于 Indigo 的应用程序。

System.Transactions 提供一个新的内存中事务处理管理器,该事务处理管理器允许高效地提交或回滚不稳定的事务处理(即,不涉及持久资源的事务处理),而不会导致任何磁盘 I/O。为了支持持久性,这个内存中的事务管理器将以透明方式把不稳定的事务提升为持久事务,方法是通过基于磁盘的事务处理管理器(如 DTC)来协调持久资源管理器将其自身登记到事务处理中的时刻。

System.Transactions 为编写不稳定资源管理器和持久资源管理器定义了一个简单的托管接口。System.Transactions 还支持任何可与 OLE 事务处理或广泛采用的 WS-AtomicTransaction 协议进行通信的资源管理器。为了通过事务处理高效访问文件系统,Longhorn 版本的 System.Transactions 对内核事务处理管理器 (KTM) 和事务处理 NTFS (TxNTFS) 均提供直接支持。

Indigo 还提供了消息处理应用程序中使用的许多公共抽象的实现。具体来说,Indigo 提供了简单但是可扩展形式的队列、事件处理和基于内容的路由,所有这些都可用在任何基于 Indigo 的应用程序中。

对 Indigo 进行编程

Indigo 模型基于四个简单概念。首先,消息是一段可传输的、遵循 SOAP 数据模型的数据。消息有零或多个头和一个主体,在 Indigo 中,消息由具体的 Message 类来表示。尽管 Indigo 允许具有任意方法的对象存储在消息中,但是所有的消息部分都可按照 SOAP 和 XML 数据模型以统一的方式交互。

第二,消息在端口之间流动。Indigo 端口是服务之间通信的端点。端口可以独立于传输来收发消息。端口有两种形式:匿名和命名。命名端口可独立于任何会话或连接显式寻址,匿名端口只能作为已建立的通信会话的一部分隐式寻址。匿名端口接收的唯一消息是对发自该端口的消息的响应。

第三,服务是端口后面的一段不透明代码。Indigo 是指作为服务驻留在端口后面的可执行代码。服务既有策略又有协定;策略指出服务的功能和要求,协定则指出服务可参与的、可接受消息交换。

第四,服务通过信道与它们的端口进行通信。服务可以创建和使用一系列信道来构建应用程序;信道的属性、策略和行为因信道所提供的通信服务的特征而异。例如,可以创建用来连接另一个服务(由 URL 来标识)的对话信道。对话信道提供如下功能:设置“仅一次,按顺序”传递的传输保证策略;使用这样的信道,可以确保消息可靠地传递。

图 2 中的图表显示了消息、端口、信道和服务之间的关系。

MAissues0401Indigofig02

图 2 关系

为了使这些概念具体化,最简单的方法就是查看一些代码。这一部分中显示的代码是针对 Indigo 的 PDC 版本编写的 — 很自然地,从现在到最终版本之前,细节有可能会发生更改。下面的代码是可能的最小 Indigo 服务,它是在 C# 中只使用 Indigo 连接器编写的:

using System;
using System.MessageBus;

class app {
  static void Main() {
// create and open a named port
    Port port = new Port(new 
                 Uri("soap://localhost/simple"));
    port.Open();
// wait forever
    Console.ReadLine();
  }
}

此服务创建了一个新的命名端口,该端口的相对 URI 是“/simple”,可通过所有已安装的传输来访问。Indigo 将 "soap:" URI 方案视为服务的独立于传输的地址。Indigo 连接器会自动将该地址映射到 TCP 和 HTTP 传输上,以便该服务可通过下面的任何 URI 寻址:

soap://localhost/simple
http://localhost/simple
soap.tcp://localhost/simple

除非应用程序需要显式控制传输选项,否则,soap: 是首选的 URI 方案,因此,本文的其余部分均使用该方案。

刚刚显示的服务极其灵活 — 您可以通过任何传输向该服务发送任何种类的消息,它将以静默方式轻松处理这些消息。编写涉及到消息的代码还需要另外一些少量的工作。图 3 显示的服务会在发送的消息内容为简单字符串时,将该字符串打印到控制台上。此服务假设消息的主体是简单字符串,如果消息的主体是任何其他内容,此服务将总是肯定会失败。

using System;
using System.MessageBus;

class MsgPrint : SyncMessageHandler {
  public override bool ProcessMessage(Message msg)
  {
    Console.WriteLine(
        msg.Content.GetObject(typeof(string))
    );
    msg.Close();
    return false;
  }  
}

class app {
  static void Main() {
    Port port = new Port(new 
                 Uri("soap://localhost/simple"));
    port.ReceiveChannel.Handler = new MsgPrint();
    port.Open();
// wait forever
    Console.ReadLine();
  }
}

图3

为了允许服务以更加面向数据的形式处理主体,Indigo 还使您能够使用 System.Xml 将消息内容作为原始的 XML 结构数据进行访问。图 4 中的服务会将每个消息的内容转储到 XML 文本文件中。


using System;
using System.Xml;
using System.MessageBus;

class MsgDump : SyncMessageHandler {
  public override bool ProcessMessage(Message msg)
  {
    string fn = String.Format("output{0}.xml",
                              DateTime.Now.Ticks);
    XmlWriter xw = new XmlTextWriter(fn, null);
    xw.WriteNode(msg.Content.Reader, false);
    xw.Close(); 
    msg.Close();   
    return false;
  }  
}

class app {
  static void Main() {
    Port port = new Port(new 
                 Uri("soap://localhost/simple"));
    port.ReceiveChannel.Handler = new MsgDump();
    port.Open();
// wait forever
    Console.ReadLine();
  }
}

图4

此程序利用如下事实:消息的主体总是可以通过 XmlReader 流接口来访问。此功能对于处理大型消息非常重要,这是由于 XmlReader 的只前进遍历模型允许在消息数据流入内存中时进行递增处理。


public class Message  {

// public constructors
  public Message();
  public Message(Uri action);
  public Message(Uri action, object obj);
  public Message(Uri action, MessageContent content);
  public Message(MessageException exception);

// create message targeted at sender
  public Message CreateReverse();
  public Message CreateReverse(Uri action);
  public Message CreateReverse(Uri action, object obj);
  public Message CreateReverse(Uri action, MessageContent content);
  public Message CreateReverse(MessageException exception);

// create correlated reply targeted at sender
  public Message CreateReply();
  public Message CreateReply(Uri action);
  public Message CreateReply(Uri action, object obj);
  public Message CreateReply(Uri action, MessageContent content);
  public Message CreateReply(MessageException exception);

// public operations
  public Message Clone();
  public void Close();

// public properties
  public Uri Action { get; set; }
  public MessageHeaderCollection Headers { get; }
  public MessageContent Content { get; set; }
}

图5

Indigo 连接器定义了一个托管类型的小内核来处理消息。这些类型的最核心部分是具体的 Message 类,如图5中所示。Message 类维护着一个头的排序列表,以及一个代表消息内容的主体。为方便起见,Action 头(由 WS-Addressing 以及 SOAP 的 HTTP 绑定来定义)作为可分辨属性来调出。每个消息头都实现 MessageHeader 抽象接口(如图6 所示),该接口会公开核心的 SOAP-ism(如 mustUnderstand 和 actor/role)。消息的主体必须实现 MessageContent 抽象接口,该接口既支持基于对象的访问,又支持基于 XML 的访问。

public abstract class MessageHeader {
// protected constructors
protected MessageHeader(bool mustUnderstand, Uri role);
protected MessageHeader(MessageHeader source);

// SOAP processing/data model properties
public abstract string Name { get; }
public abstract string Namespace { get; }
public bool MustUnderstand { get; }
public Uri Role { get; }
public bool DidUnderstand { get; set; }

// header serialization methods and properties
public abstract MessageHeader Clone();
public virtual XmlElement Element { get; }
public abstract void WriteTo(XmlWriter writer);
public abstract bool CanWrite { get; }
public virtual bool CanTransmit { get; }
}

public abstract class MessageContent {
// object I/O
public abstract object GetObject(Type type);

// XML I/O
public virtual XmlReader Reader { get; }
public abstract void WriteTo(XmlWriter writer);

// async XML I/O support
public virtual IAsyncResult BeginWriteTo(XmlWriter writer,
AsyncCallback callback,
object state);
public virtual void EndWriteTo(IAsyncResult result);

// public properties and methods
public virtual bool IsEmpty { get; }
public virtual bool IsException { get; }

public abstract MessageContent Clone();
public virtual void Close();

}

图6

除了 Message 类,最重要的消息处理类型无疑是 IMessageHandler。IMessageHandler 接口代表一段消息处理代码,并且有一个用于调用消息处理器的方法:ProcessMessage。

public interface IMessageHandler {
  bool ProcessMessage(Message msg);
// async support elided for clarity
}

除了主要的 ProcessMessage 方法,IMessageHandler 接口还提供了其他几个方法,这些方法用于与执行异步方法时使用的 IAsyncResult 用语进行交互。需要默认异步行为的实现(如这一部分开头显示的示例)通常从 SyncMessageHandler 抽象基类派生,并且只提供一个 ProcessMessage 方法主体。

还记得吗,消息通过消息的主体支持流 I/O,这表明遍历主体的消息处理程序会有效地呈现以后毫无用处的消息。因此,ProcessMessage 方法会返回一个布尔值,表明消息是否已被“使用”。如果主体尚未被使用,则处理程序从 ProcessMessage 中返回真,表明该消息仍可进行进一步处理。如果某个处理程序使用消息主体,则必须从 ProcessMessage 返回假,而且还必须针对该消息对象调用 Close,以便释放由该消息占用的任何基础资源。 下例阐释了如何通过使用一系列消息处理程序来使用消息:

static void
ConsumeMessage(IList<IMessageHandler> code, Message data) {
  bool live = true;
  for (int i=0; i < code.Count && live; i++)
    live = code[i].ProcessMessage(data);

  if (live)
    data.Close();
}

第三个也是最后一个核心消息处理类型就是 IMessageProducer。IMessageProducer 是一个为消息来源建立模型的简单接口,正如下面显示的那样,它只有一个公共成员,即 Handler 属性:

public interface IMessageProducer {
  IMessageHandler Handler { get; set; }
}

您已经了解了此接口的实现方法。用在前面服务示例中的 Port.ReceiveChannel 属性实现了 IMessageProducer 接口。对于要处理到达某个端口的消息的代码,只需通过接收信道的 Handler 属性将它们自身进行附加。

到现在为止,您只了解了如何接收消息。很显然,Indigo 还允许发送消息 — 此功能是通过端口的发送信道来呈现的。每个端口都有一个默认的发送信道(通过 Port.SendChannel 属性来呈现),使用发送信道可以发送使用 To 头(按照 WS-Addressing 规范)显式寻址的消息。此信道通常只用于发送由 Message.CreateReply 方法创建的答复消息:

static bool Process(Port port, Message request)
{
    Message data = request.CreateReply(null, 
                                       "Blue");
    IMessageHandler channel = port.SendChannel;
    if (channel.ProcessMessage(data)) 
      data.Close();
}

更常见的用法是,通过调用 Port.CreateSendChannel 方法来创建每地址发送信道,如下所示:

static void SendAMessage(Port source,
                         Uri target) {
{
    Message data = new Message(null, "Cyan");
    IMessageHandler channel = 
            source.CreateSendChannel(target);
    if (channel.ProcessMessage(data)) 
      data.Close();
}

在本例中,发送信道会首先向消息中添加必需的寻址头,然后再将它传递到下面的传输管理器。

 

 

版权所有:UML软件工程组织