Indigo 简介:早期外观

 

大卫·查佩尔
Chappell & Associates

2005 年 2 月

总结: 提供“Indigo”的体系结构概述,这是 Microsoft 用于构建面向服务的应用程序的统一编程模型。 本文介绍了 Indigo 与.NET Framework中现有分布式应用程序技术的关系、创建和使用 Indigo 服务的基础知识,以及 Indigo 的功能概述,包括安全性、可靠消息传送和事务支持。 (24 个打印页)

注意 本文基于 Indigo 第一个社区技术预览版的预发行版本 (CTP) 。 请注意,随着 Indigo 在产品周期中的进展,功能和实现细节可能会发生变化。

目录

什么是 indigo?
Indigo 提供的内容
创建 Indigo 服务
创建 Indigo 客户端
Indigo 的其他方面
共存和迁移
结论
关于作者

什么是 indigo?

选择用于生成软件的最佳抽象是一个持续的过程。 对象是当今生成应用程序业务逻辑的主要方法,但使用对象对应用程序到应用程序的通信进行建模并不成功。 更好的方法是将离散的软件区块之间的交互显式建模为 服务。 构建面向对象的应用程序已经有很多支持,但将服务视为一个基本的软件构建基块是一个较新的想法。 因此,明确设计用于创建面向服务的应用程序的技术尚未被广泛使用。

Microsoft 用于生成面向服务的应用程序的框架(代号为 Indigo)改变了这一点。 Indigo 允许使用 .NET Framework 创建面向对象的应用程序的开发人员也以熟悉的方式生成面向服务的应用程序。 为了让这些应用程序与 Windows 和其他平台上运行的软件有效交互,Indigo 实现了 SOAP 和其他 Web 服务技术,使开发人员能够创建可靠、安全和事务性服务,这些服务可与任何系统上运行的软件互操作。

Aa480188.introindigov1-001 (en-us,MSDN.10) .gif

上图显示了 Indigo 客户端和服务的简单视图。 Indigo 提供了一个基础,主要作为一组在公共语言运行时 (CLR) 上运行的类实现,用于创建客户端访问的服务。 客户端和服务通过 SOAP(Indigo 的本机协议)进行交互,因此,即使图中显示了基于 Indigo 构建的双方,这当然不是必需的。

Indigo 基于并扩展了计划于 2005 年发布的 .NET Framework 2.0。 Indigo 本身将作为计划于 2006 年发布的名为 Longhorn 的 Windows 版本代码的一部分提供,并且它还将在 Windows XP 和 Windows Server 2003 上提供。 此说明基于 Indigo 第一个社区技术预览版的预发布版本。 请注意,某些更改可能 (几乎可以确定,事实上,在最终版本发布之前) 。

Indigo 提供的内容

Microsoft 的许多人一生都致力于创建 Indigo。 如果它解决的问题很简单,或者他们的解决方案是显而易见的,那么这种努力就没有必要了。 因此,Indigo 是一项重要的技术。 然而,Indigo 最重要的方面有三个突出之处:它对多种现有 Microsoft 技术的统一、对跨供应商互操作性的支持以及明确的服务导向。 本部分介绍其中每一项。

统一 Microsoft 的分布式计算技术

.NET Framework的初始版本包括几种不同的技术,用于创建分布式应用程序。 下图列出了每种技术,以及开发人员通常使用该技术的主要原因。 例如,若要生成基本的可互操作 Web 服务,最佳选择是 ASP.NET Web 服务,通常称为 ASMX。 若要连接两个基于.NET Framework的应用程序,.NET 远程处理有时是正确的方法。 如果应用程序需要分布式事务和其他更高级的服务,则其创建者可能会使用企业服务(.NET Framework COM+ 的继任者)。 为了利用最新的 Web 服务规范(如 WS-Addressing 和 WS-Security),开发人员可以构建使用 Web 服务增强功能的应用程序 (WSE) (Microsoft 对这些新兴规范的初始实现)。 为了创建排队的基于消息的应用程序,基于 Windows 的开发人员将使用 Microsoft 消息队列 (MSMQ) 。

  ASMX .NET 远程处理 企业服务 WSE MSMQ 靛蓝
可互操作的 Web 服务     X             X
.NET – .NET 通信       X           X
分布式事务等。         X         X
支持 WS-* 规范           X       X
排队消息传送             X     X

所有这些选项都有价值,但多样性肯定让开发人员感到困惑。 为什么有这么多选择? 更好的解决方案是有一种技术来解决所有这些问题。 随着 Indigo 的到来,这项技术已经出现。 Indigo 允许开发人员创建分布式应用程序,而不是强制开发人员选择几种可能性之一,用于解决它所包含的技术所解决的所有问题。 虽然 Microsoft 仍将支持这些早期技术,但以前使用其中任何一项的大多数新应用程序都将基于 Indigo 构建。

与非 Microsoft 应用程序的互操作性

通过统一不同的技术,让 Windows 开发人员的生活更轻松是一件好事。 但是,通过供应商在 Web 服务方面的普遍协议,应用程序互操作性的长期问题也可以得到解决。 由于 Indigo 的基本通信机制是 SOAP,因此 Indigo 应用程序可以与在各种上下文中运行的其他软件进行通信。 如下图所示,基于 Indigo 构建的应用程序可以与以下所有项交互:

  • 在同一 Windows 计算机上在不同进程中运行的 Indigo 应用程序。
  • 在另一台 Windows 计算机上运行的 Indigo 应用程序。
  • 基于支持标准 Web 服务的其他技术(例如基于 Java 2 的应用程序服务器)构建的应用程序Enterprise Edition (J2EE) 。 这些应用程序可以在 Windows 计算机上运行,也可以在具有其他操作系统(如 Sun Solaris、IBM 的 z/OS 或 Linux)的计算机上运行。

Aa480188.introindigov1-003 (en-us,MSDN.10) .gif

Indigo 应用程序还可以与基于 Indigo 之前的一些.NET Framework技术(例如 ASMX)构建的应用程序进行互操作,如后面所述。

为了不仅允许基本通信,Indigo 还实现了一组更新的 Web 服务技术,统称为 WS-* 规范。 这些文档定义了向基于 SOAP 的 Web 服务添加可靠消息传送、安全性、事务等的多供应商方法。 所有这些规格最初由 Microsoft、IBM 和其他供应商共同定义。 随着它们变得稳定,所有权通常会传递给标准机构,如结构化信息标准促进组织 (OASIS) 。 Indigo 第一个版本支持的 Web 服务规范包括 WS-Addressing、WS-Policy、WS-MetadataExchange、WS-ReliableMessaging、WS-Security、WS-Trust、WS-SecureConversation、WS-Coordination、WS-AtomicTransaction 以及 SOAP 消息传输优化机制 (MTOM) 。

当 Indigo 应用程序与在非 Windows 系统上运行的应用程序通信时,使用的协议是标准 SOAP (可能具有某些 WS-* 扩展) ,以通常基于文本的 XML 编码在网络上表示。 但是,当一个基于 Indigo 的应用程序与另一个基于 Indigo 的应用通信时,优化此通信是有意义的。 提供了所有相同的功能,包括可靠的消息传送、安全性和事务,但使用的线路编码是 SOAP 的优化二进制版本。 消息仍符合 SOAP 消息的数据结构(称为 其 Infoset),但其编码使用该 Infoset 的二进制表示形式,而不是 XML 的标准尖括号和文本格式。

显式支持Service-Oriented开发

将应用程序视为提供和使用服务并不是一个新想法。 新增功能是明确关注与对象不同的服务。 为此,Indigo 的创作者在设计这项技术时牢记了四个原则:

  • 共享架构,而不是类:与较旧的分布式对象技术不同,服务只能通过定义完善的 XML 接口与其客户端交互。 不允许跨服务边界传递完整的类、方法和所有行为。
  • 服务是自治的:服务及其客户端在它们之间的接口上达成一致,但在其他方面是独立的。 它们可能以不同的语言编写,使用不同的运行时环境(例如 CLR 和 Java 虚拟机),在不同的操作系统上执行,并且在其他方面也有所不同。
  • 边界是明确的:分布式对象技术(如分布式 COM (DCOM) )的目标是使远程对象看起来尽可能像本地对象。 虽然此方法通过提供通用编程模型在某些方面简化了开发,但它也隐藏了本地对象和远程对象之间不可避免的差异。 服务通过使服务与其客户端之间的交互更加明确来避免此问题。 隐藏分布不是目标。
  • 使用基于策略的兼容性:如果可能,确定要在系统之间使用的选项应依赖于基于 WS-Policy 的机制。

面向服务是一个广泛的领域,包括面向服务的应用程序和面向服务的体系结构的更一般概念 (SOA) 。 Indigo 将是基于 Windows 构建的面向服务的应用程序的基础,因此它将成为许多组织的 SOA 工作的基础。

创建 Indigo 服务

如下图所示,每个 Indigo 服务都是由三项内容构建的:

  • 使用 C# 或 VB.NET 或其他基于 CLR 的语言实现 的服务类,可实现一个或多个方法;
  • 运行服务的 主机 环境(应用程序域和进程);
  • 允许客户端访问服务的一个或多个 终结点

Aa480188.introindigov1-004 (en-us,MSDN.10) .gif

与 Indigo 服务的所有通信都通过服务的终结点进行。 每个终结点指定一个 协定 ,用于标识哪些方法可通过此终结点访问,一个确定客户端如何与此终结点通信的 绑定 ,以及一个指示可在何处找到此终结点的 地址

了解 Indigo 需要掌握所有这些概念。 本部分介绍每个服务类,从服务类开始。

创建服务类

Indigo 服务类与其他任何类类似,但它有一些新增功能。 这些新增功能允许类的创建者定义此类实现的一个或多个 协定 。 每个 Indigo 服务类实现至少一个 服务协定,用于定义此服务公开的操作。 服务类还可以显式实现 数据协定,该协定定义这些操作传递的数据。 本部分从服务协定开始介绍这两者。

定义服务协定

每个 Indigo 服务类实现其客户端使用的方法。 服务类的创建者通过将哪些方法包含在服务协定中来确定其哪些方法作为客户端可调用的操作公开。 定义服务协定(事实上,一般情况下显式使用服务)在很大程度上是 .NET 世界的一个新想法。 Indigo 的创建者需要找到一种方法,将这一想法嫁接在 CLR 和基于 CLR 构建的编程语言之上。 幸运的是,CLR 的创建者预计需要这样的扩展,因此他们提供了对 属性的支持。 正如开发人员所见,属性是字符串(可能具有关联的属性),它们显示在类定义、方法定义和其他地方之前。 无论属性在何处出现,它都更改了与之关联的事物的行为的某些方面。

自首次发布以来,.NET Framework已将属性用于各种功能。 例如,若要在框架的 ASMX 技术中将方法标记为 SOAP 可调用的 Web 服务,该方法前面是 WebMethod 属性。 同样,企业服务使用 Transaction 属性来指示方法需要事务。 Indigo 将此理念应用于服务,定义一系列属性来定义和控制服务。

Indigo 中最基本的属性是 ServiceContract。 事实上,Indigo 服务类只是一个类,它本身使用 ServiceContract 特性标记,或者实现用此属性标记的接口。 下面是使用第一种方法的简单 C# 示例:

using System.ServiceModel;

[ServiceContract]
class Calculator
{
 [OperationContract]
 private int Add(int a, int b)
 {
   return a + b; 
 }
   
 [OperationContract]
 public int Subtract(int a, int b)
 {
   return a - b;
 }
   
 public int Multiply(int a, int b)
 {
    return a * b;
 }
} 

Indigo ServiceContract 使用的属性和所有其他属性都在 中 System定义。ServiceModel 命名空间,因此此示例以 using 引用此命名空间的语句开头。 服务类中可由客户端调用的每个方法都必须使用另一个名为 OperationContract的属性进行标记。 服务类中属性前面 OperationContract 的所有方法都会由 Indigo 自动公开为 SOAP 可调用的操作。 在此示例中, AddSubtract 都标有此属性,因此两者都公开给此服务的客户端。 服务类中未使用 OperationContract标记的任何方法(如上例中所示 Multiply )均不包含在服务协定中,因此无法由此 Indigo 服务的客户端调用。

两个完全独立的抽象(服务和对象)在 Indigo 中结合在一起。 重要的是要了解,两者都依赖于合同,无论是显式的还是隐式的,来定义它们向外部世界公开的内容。 由某个类指定的 对象有效地定义了一个协定,用于确定同一应用程序中的另一个对象可以调用其中哪些方法。 对这些方法的访问由语言关键字(如 和 privatepublic控制。 例如,在上面显示的 类 Calculator 中,同一应用程序中的其他对象可以调用 SubtractMultiply,该类的两个公共方法。 此类公开的对象 ** 协定仅包含这两种方法。

使用 Indigo 的属性, Calculator 还可以定义服务协定,如前所述。 此协定也有两种方法,但它们与其对象协定中的方法不同。 此 Indigo 服务的客户端是否可以调用方法由 OperationContract 属性(而不是 publicprivate 关键字)控制。 由于此属性仅在 和 SubtractAdd显示,因此客户端只能调用这两种方法。 对象协定和服务协定彼此完全不同,因此,等Add方法在仍携带 OperationContract 属性时可以是 private

刚才显示的示例演示了创建 Indigo 服务类的最简单方法:直接使用 ServiceContract标记类。 完成此操作后,类的服务协定将隐式定义为包含该类中用 OperationContract标记的所有方法。 也可以 (,并且在大多数情况下,) 使用语言 interface 的类型显式指定服务协定可能更好。 使用此方法时, Calculator 类可能如下所示:

using System.ServiceModel;

[ServiceContract]
interface ICalculator
{
 [OperationContract]
 int Add(int a, int b);

 [OperationContract]
 int Subtract(int a, int b);
}

class Calculator : ICalculator
{
 public int Add(int a, int b) // private methods aren't 
 {                            // allowed in interfaces
   return a + b; 
 }
      
 public int Subtract(int a, int b)
 {
   return a - b;
 }
      
 public int Multiply(int a, int b)
 {
   return a * b;
 }
}

在此示例中,ServiceContract和 属性分配给ICalculator接口及其包含的方法,而不是分配给CalculatorOperationContract本身。 但是,结果相同,因此此版本的服务公开的服务协定与上一个服务协定相同。 使用此类显式接口稍微复杂一些,但它也允许更大的灵活性。 例如,一个类可以实现多个接口,这意味着它也可以实现多个服务协定。 通过公开多个终结点(每个终结点具有不同的服务协定),类可以将不同的服务组呈现给不同的客户端。

最后一点:使用 ServiceContractOperationContract 标记服务类,还允许在 Web 服务描述语言 (WSDL) 自动生成服务协定定义。 因此,每个 Indigo 服务协定的外部可见定义可以作为指定该协定中的操作的标准 WSDL 文档进行访问。 尽管此处未进行介绍,但也可以直接从 WSDL 文档创建 Indigo 服务类,这种方法对于实现外部定义的 WSDL 接口特别有用。

定义数据协定

Indigo 服务类指定一个服务协定,用于定义向服务客户端公开其哪些方法。 每个操作通常都会传达一些数据,这意味着服务协定还意味着某种描述将交换的信息的数据协定。 在某些情况下,此数据协定被隐式定义为服务协定的一部分。 例如,在上面所示的 Calculator 类中,每个方法都采用两个输入参数(两个整数),并返回一个整数。 这些参数定义此服务交换的所有数据,因此它们构成了服务的数据协定。 对于此类服务,其中每个操作仅使用简单类型,最好在服务协定中隐式定义其协定的数据方面。 不需要任何其他内容。

但服务还可以具有更复杂的类型的参数,例如结构。 在这种情况下,需要显式数据协定。 数据协定定义如何将内存中类型转换为适合跨网络传输的形式,此过程称为 序列化。 实际上,数据协定是一种控制数据序列化方式的机制。

在 Indigo 服务类中,使用 DataContract 特性定义数据协定。 用 DataContract 标记的类、结构或其他类型可以有其一个或多个成员,前面是 DataMember 属性,指示此成员应包含在此类型的序列化值中。 下面是一个简单示例:

[DataContract]
struct Customer {
 [DataMember] public string Name; 
 int public age;
 [DataMember] private int CreditRating;
}

当此 Customer 类型的实例作为参数在标记为 OperationContract的方法中传递时,将仅传递用 DataMember 属性NameCreditRating标记的字段。

字段是标记为 还是publicprivate对字段是否序列化没有影响。 与方法一样, publicprivate 关键字是协定的一部分,用于定义如何由同一应用程序中的其他对象访问此类型。 DataMember与 一样 OperationContract,定义此类实现的服务的客户端如何访问类型。 再一次,两者完全不同。

关于 Indigo 协定的最后一点值得强调的是,默认情况下,任何内容都不会成为服务协定或数据协定的一部分。 相反,开发人员必须显式使用 ServiceContractDataContract 属性来指示哪些类型具有 Indigo 定义的协定,然后使用 和 DataMember 属性显式指定向此服务的OperationContract客户端公开这些类型的哪些部分。 其设计人员的一个原则是服务应该有明确的边界,因此 Indigo 是一种选择加入的技术。 服务提供给其客户端的所有内容均在代码中明确指定。

协定和定义它们的属性是 Indigo 的一个主要方面,此简短说明仅涵盖要点。 特性 OperationContract 可用于定义 单向 操作,例如,对服务的调用没有回复。 还可以定义交互,其中双方可以充当客户端和服务,每个调用操作并公开对方调用的操作,方法是创建所谓的 双工 协定。 属性 DataContract 还具有多个其他选项,甚至可以使用名为 MessageContract的属性以本机方式直接处理 SOAP 消息。 合同用于表达 Indigo 提供的大部分内容,因此它们是其最基本的概念之一。

选择主机

实现 Indigo 服务的类通常编译到库中。 根据定义,所有库都需要主机应用程序域和 Windows 进程才能运行。 Indigo 提供两个选项用于托管实现服务的库。 一种是使用 Windows 激活服务提供的主机应用域和进程 (WAS) ,另一种是允许在任意进程中运行的任何应用域中托管服务。 本节从 WAS 开始介绍这两者。

使用 Windows 激活服务托管服务

托管 Indigo 服务的最简单方法是依赖 WAS。 (请注意,Indigo 的第一个社区技术预览版不支持 WAS。相反,Indigo 服务可以托管在 Windows Server 2003 和 Windows XP 上的 Internet 信息服务器中,尽管此配置仅支持 SOAP over HTTP。) 使用 WAS 非常类似于使用 IIS 为 ASMX 提供的托管机制。 此外,两者都依赖于 虚拟目录的概念,虚拟目录只是 Windows 文件系统中实际目录路径的较短别名。

若要查看 WAS 托管的工作原理,假设前面显示的任 Calculator 一类已编译为名为 calc.dll 的库,然后放置在运行 Windows Server 2003 的系统上的虚拟目录 计算器 中。 为了指示在 calc.dll 中实现的 Indigo 服务应由 WAS 托管,开发人员在计算器虚拟目录中创建一个扩展名为 .svc (的文件,当然,该文件代表“service”) 。 对于我们的简单示例,此文件可能称为 calc.svc,其整个内容可以是:

%@Service language=c# class="Calculator" %

完成此操作并定义终结点(如下一部分所示)后,从客户端向服务的方法之 Calculator 一发出请求将自动创建此类的实例以执行指定的操作。 该实例将在 WAS 提供的标准进程中创建的应用程序域中运行。

在任意进程中承载服务

依靠 WAS 提供托管 Indigo 服务的过程当然是最简单的选择。 但是,应用程序通常需要从自己的进程中公开服务,而不是依赖于 Windows 提供的服务。 幸运的是,这并不难做到。 以下示例演示如何创建一个进程来承载前面定义的任一 Calculator 类:

using System.ServiceModel;

public class CalculatorHost
{
  public static void Main()
  {
    ServiceHost<Calculator> s1 = 
      new ServiceHost<Calculator>();
    s1.Open();
    Console.Writeline("Press ENTER to end service");
    Console.Readline();    
  }
}

由于 类 CalculatorHost 包含方法 Main ,因此它将作为不同的进程运行。 若要承载示例 Calculator 服务,此方法必须创建 类 ServiceHost<T>的新实例,并 Calculator 传入 类。 (请注意,此标准 Indigo 类是一个泛型,由 < 指示,并>包含其 参数。泛型是 C# 版本 2.0、Visual Basic .NET 和基于 .NET Framework.) 2.0 版的其他语言中的一种新语言功能。Open Indigo 现在会自动将来自客户端的请求定向到 类中的 Calculator 相应方法。

若要允许 Indigo 服务处理来自其客户端的请求,托管它的进程必须保持运行状态。 这不是 WAS 托管服务的问题,因为 WAS 提供的标准进程可确保这一点。 但是,托管应用程序必须自行解决此问题。 在此简单示例中,该过程通过等待控制台用户输入的简单机制保持运行。

定义终结点

除了在 Indigo 服务类中定义操作并指定运行这些操作的主机进程外,Indigo 服务还必须公开一个或多个终结点。 每个终结点指定以下三项:

  • 一个 协定 名称,指示此 Indigo 服务类通过此终结点公开的服务协定。 标记为 ServiceContract 且不实现任何显式接口的类(如 Calculator 前面所示的第一个示例)只能公开一个服务协定。 在这种情况下,其所有终结点都将公开相同的协定。 但是,如果类显式实现两个或更多个用 标记的 ServiceContract接口,则不同的终结点可能会公开不同的协定。
  • 一个 地址 ,指示可在何处找到此终结点。 地址是标识计算机和该计算机上的特定终结点的 URL。
  • 确定如何访问此终结点的 绑定 。 绑定确定可用于访问此终结点的协议组合以及其他事项,例如通信是否可靠以及可以使用哪些安全机制。 例如,假设服务的创建者希望允许客户端使用 SOAP over HTTP 或 SOAP over TCP 访问该服务。 其中每个终结点都是不同的绑定,因此服务需要公开两个终结点,一个具有 SOAP-over-HTTP 绑定,另一个具有 SOAP-over-TCP 绑定。

绑定是实现通信方式的关键部分。 为了使它们更易于使用,Indigo 包含一组预定义绑定,每个绑定指定一组特定的选项。 此数据集包括:

  • BasicProfileHttpBinding:符合 Web 服务互操作性组织 (WS-I) Basic Profile 1.0,后者指定 SOAP over HTTP。 如果未显式指定任何绑定,则这是终结点的默认绑定。
  • BasicProfileHttpsBinding:符合 WS-I 基本安全配置文件 1.0,该配置文件指定 SOAP over HTTPS。
  • WsHttpBinding:支持使用 WS-ReliableMessaging 进行可靠的消息传输、使用 WS-Security 进行安全性,以及使用 WS-AtomicTransaction 进行事务。 此绑定允许与同样支持这些规范的其他 Web 服务实现进行互操作性。
  • WsDualHttpBinding:与 WsHttpBinding 一样,但也支持使用双工协定的交互。 使用此绑定,服务和客户端都可以接收和发送消息。
  • NetTcpBinding:直接通过 TCP 发送二进制编码的 SOAP,包括对可靠消息传输、安全性和事务的支持。 此绑定只能用于 Indigo 到 Indigo 的通信。
  • NetNamedPipeBinding:通过命名管道发送二进制编码的 SOAP。 此绑定仅适用于同一 Windows 计算机上的进程之间的 Indigo 到 Indigo 通信。
  • NetMsmqBinding:通过 MSMQ 发送二进制编码的 SOAP,如后文所述。 此绑定只能用于 Indigo 到 Indigo 的通信。

Aa480188.introindigov1-005 (en-us,MSDN.10) .gif

上图显示了前面所示的第 Calculator 一个服务的终结点中三个元素中的每一个的示例值。 服务协定的名称为 Calculator,它是实现此服务的类的名称,绑定为 BasicProfileHttpBinding。 假设此服务使用 WAS 进行托管,如前所述安装在虚拟目录 计算器 中,并在名为 qwickbank.com 的计算机上运行,则其地址可能是 http://www.qwickbank.com/calculator/calc.svc

与协定不同,终结点不是使用属性定义的。 虽然可以通过编程方式创建终结点,但最常见的方法可能是使用与服务关联的配置文件。 WAS 承载的服务使用 web.config 文件,而独立托管的服务使用与它们在中运行的应用程序关联的配置文件的服务 (通常称为 app.config,但实际文件名) 不同。 如果仅用于前面所示的第一个 Calculator 服务类,则此配置文件可能如下所示:

<configuration>
  <system.serviceModel>
    <services>
      <service serviceType="Calculator">
           <endpoint 
          contractType="Calculator"
          bindingType="basicProfileHttpBinding />
      </service>
    </services>
  </system.serviceModel>
</configuration>

Indigo 应用程序实现的所有服务的配置信息都包含在 元素中 system.serviceModel 。 此元素包含可以包含一 services 个或多个 service 元素的元素。 这个简单示例只有一个服务,因此 只有一次出现 serviceserviceType元素的 service 属性标识实现此配置适用的服务的服务类,在本例中为 Calculator。 每个 service 元素可以包含一个或多个 endpoint 元素,每个元素指定一个特定的终结点,通过该终结点可以访问此 Indigo 服务。 在此示例中,服务只公开一个终结点,因此只显示一个 endpoint 元素。 终结点协定的名称为 Calculator,这是实现协定的类的名称。 如果此配置文件适用于前面所示的第二 Calculator 个服务类,后者使用显式接口定义其服务协定,则 属性的值 serviceType 将保持不变,但 的值 contractTypeICalculator改为 ,此显式接口的名称。 此处指定的绑定是 basicProfileHttpBinding,尽管这是默认绑定,因此可以省略它。 假设 Calculator 是 WAS 承载的服务,则会自动创建一个地址,因此无需在此配置文件中指定一个地址。

创建 Indigo 客户端

创建基本的 Indigo 服务并不复杂。 创建 Indigo 客户端更简单。 只需为连接到目标服务上的特定终结点的服务创建本地备用服务器(称为 代理),然后通过此代理调用该服务的操作。 下图显示了其外观。

Aa480188.introindigov1-006 (en-us,MSDN.10) .gif

创建代理需要确切地知道目标终结点公开的协定,然后使用此协定的定义来生成代理。 在 Indigo 中,此过程由名为 svcutil 的工具执行。 如果服务是使用 Indigo 实现的,则 svcutil 可以访问服务的 DLL 来了解协定并生成代理。 如果只有服务的 WSDL 定义可用,则 svcutil 可以读取此定义以生成代理。 如果只有服务本身可用,则 svcutil 可以直接使用WS-MetadataExchange或简单的 HTTP GET 来获取服务的 WSDL 接口定义,然后生成代理。

无论生成如何,客户端都可以创建代理的新实例,然后使用它调用服务的方法。 下面是 类的客户端 Calculator 的简单示例:

using System.ServiceModel;
using Indigo.Example; // namespace for generated proxy class

public class CalculatorClient
{
  public static void Main()
  {
    CalculatorProxy p = new CalculatorProxy();
    Console.WriteLine("7 + 2 = {0}", p.Add(7, 2));
    Console.WriteLine("7 - 2 = {0}", p.Subtract(7, 2));
    p.Close();
  }
} 

还有一件事需要由客户端指定:它希望调用操作的确切终结点。 与服务一样,客户端必须指定终结点的协定、其绑定和地址,这通常在配置文件中完成。 事实上,如果有足够的信息可用,svcutil 将自动生成目标服务的相应客户端配置文件。

Indigo 的其他方面

服务和客户端的基础知识是每个 Indigo 应用程序的基础。 然而,其中大多数应用程序也将使用该技术的其他方面。 本部分介绍 Indigo 为基于它构建的应用程序提供的一些附加功能。

控制本地行为

Indigo 的许多方面(例如协定、绑定等)都与服务与其客户端之间的通信相关。 然而,服务的行为也有一些本质上是局部的。 例如,如何控制服务实例的生存期,以及如何管理对该实例的并发访问? 为了允许开发人员控制此类行为,Indigo 定义了两个主要属性,这两个属性都具有多个属性。 其中一个属性 ServiceBehavior可以应用于也用 特性标记的 ServiceContract 类。 另一 OperationBehavior个 ,可以应用于服务类中也标有 OperationContract 特性的方法。

属性 ServiceBehavior 具有各种属性,这些属性影响整个服务的行为。 例如,一个名为 ConcurrencyMode 的属性可用于控制对服务的并发访问。 如果设置为 Single,Indigo 将一次只向此服务传递一个客户端请求,即服务将是单线程的。 如果此属性设置为 Multiple,Indigo 将一次向服务传递多个客户端请求,每个请求在不同的线程上运行。 同样, ServiceBehaviorInstanceMode 属性可用于控制如何创建和销毁服务实例。 如果 InstanceMode 设置为 PerCall,则会创建服务的新实例来处理每个客户端请求,然后在请求完成时销毁。 但是,如果它设置为 PrivateSession,则服务的同一实例将用于处理来自特定客户端的所有请求。

例如,假设其创建者决定类 Calculator 应是多线程的,并且对来自特定客户端的每个调用使用相同的实例。 然后,类的定义将如下所示:

using System.ServiceModel;

[ServiceContract] 
[ServiceBehavior(
  ConcurrencyMode=Multiple, 
  InstanceMode=PrivateSession)]
class Calculator { ... } 

同样,属性上的 OperationBehavior 属性允许控制实现此操作的方法的模拟行为、其事务要求 (稍后) 描述以及其他事项。

消息传送选项

本文中所示的简单示例假定对客户端/服务交互 (RPC) 方法进行同步远程过程调用。 Indigo 支持此选项,但它不是唯一的选择。 SOAP 是一种面向消息的协议,这意味着它可以支持各种编程模型。 事实上,Indigo 支持多种可能性,包括:

  • 传统的 RPC,带有带类型化参数列表的阻塞调用;
  • 异步 RPC,具有包含类型化参数列表的非阻塞调用;
  • 传统消息传送,具有携带单个消息参数的非阻塞调用;
  • 基于消息的 RPC,具有带有单个消息参数的阻止调用。

即使绝大多数分布式应用程序都需要它,SOAP 规范也不考虑可靠性。 确保可靠性的一种常见方法是仅在点到点方案中使用 SOAP,依赖于 TCP 来保证请求和响应的传递。 在某些情况下,这已经足够了,在使用 BasicProfileHttpBinding 时,它就是要完成的。

然而,在很多情况下,这还不够。 例如,如果通过多个 SOAP 中介访问服务,该怎么办? 在这种情况下,TCP 提供的可靠性保证不足以确保端到端可靠性。 为了解决此问题,Indigo 实现了 WS-ReliableMessaging 规范。 通过选择使用 WS-ReliableMessaging 的 WsHttpBinding 等绑定,服务及其客户端可以保证可靠的端到端通信,即使通过多个 SOAP 中介也是如此。

安全性

公开网络上的服务(即使是内部网络)通常需要某种安全性。 服务如何确定其客户端的标识? 如何保护发送到服务或从服务发送的消息免受恶意更改和窥探? 如何才能将服务的访问权限限制为仅有权使用它的用户? 如果没有解决这些问题的某种解决方案,公开多种服务太危险了。 然而,构建安全的应用程序可能会变得复杂。 理想情况下,应该有简单的方法来解决常见安全方案,以及针对需要它的应用程序进行更精细的控制。

为实现此目的,Indigo 提供了身份验证、消息完整性、消息机密性和授权等核心安全功能。 Indigo 对前三种方法的方法主要依赖于绑定,开发人员的选择包括:

  • 选择支持安全性的标准绑定。 例如,只需要基于传输的安全性的应用程序可以使用 BasicProfileHttpsBinding 等绑定。 此方法足以满足从客户端直接发送到服务的请求,而无需遍历任何中介,例如 HTTP 代理或其他 SOAP 节点。 对于通过多个 SOAP 中介的消息需要端到端安全性的应用程序,可以改用支持 WS-Security 的绑定,例如 WsHttpBinding。
  • 选择支持安全性的标准绑定,然后通过更改其一个或多个默认值对其进行自定义。 例如,如果需要,可以更改绑定(如 WsHttpBinding)使用的身份验证机制。
  • 创建完全提供开发人员所需的安全功能的自定义绑定。 这样做不是为了微弱的心脏,但它可能是一些高级方案的正确解决方案。
  • 选择不提供安全性支持的标准绑定,例如 BasicProfileHttpBinding。 虽然不使用安全性通常是一件有风险的事情,但在某些情况下,它仍然是最佳选择。

Indigo 服务还可以控制哪些客户端有权使用其服务。 在大多数情况下,Indigo 仅支持.NET Framework中的现有授权机制。 例如,服务可以使用标准 PrincipalPermission 属性来定义允许访问它的人员。

过去,让开发人员在不让应用程序面临巨大复杂性的情况下生成安全应用程序已证明具有挑战性。 Indigo 既为最常见情况提供简单方法,又为更复杂的情况提供精细控制,旨在以可用且有效的方式达到此目标。

事务

处理事务是构建多种业务逻辑的一个重要方面。 然而,在面向服务的世界中使用事务可能会有问题。 分布式事务假定参与者之间的高度信任,因此通常不适合事务跨服务边界。 不过,在某些情况下,将事务和服务组合在一起是有意义的,因此 Indigo 包含对应用程序设计这一重要方面的支持。

.NET Framework 2.0 中的事务

Indigo 中的事务支持基于 .NET Framework 2.0 版中提供的增强功能。 此即将发布的版本包括 System.Transactions,这是一个专注于控制事务行为的新命名空间。 开发人员通常会将 System.Transactions 的服务与执行上下文(.NET Framework 2.0 版中的新增构造)结合使用。 执行上下文允许指定通用信息(如事务),该信息适用于定义范围中包含的所有代码。 下面是应用程序如何使用此方法将一组操作分组到单个事务的示例:

using System.Transactions;

using (TransactionScope ts 
   = new TransactionScope(TransactionScopeOption.Required)) {
   // Do work, e.g., update different DBMSs
   ts.Complete();
}

块中的所有 using 操作都将成为单个事务的一部分,因为它们共享它定义的事务执行上下文。 此示例中的最后一行(调用 TransactionScopeComplete 方法)将导致在退出块时提交事务的请求。 此方法还提供内置的错误处理,在引发异常时中止事务。

Required指定新的 TransactionScope,如本示例所示,意味着此代码将始终作为事务的一部分运行:联接调用方事务(如果存在),如果不存在,则创建一个新事务。 与在企业服务中一样,还可以指定其他选项,包括 RequiresNewSupportedNotSupported

与企业服务及其前身 MTS 和 COM+ 不同,Systems.Transactions 完全专注于控制事务行为。 例如,事务与对象的内部状态之间没有必需的连接。 虽然企业服务要求在结束事务时停用对象,但 Systems.Transactions 不会提出此类要求。 由于 Indigo 基于 Systems.Transaction 构建,Indigo 应用程序还可以自由地独立管理事务和对象状态。

Indigo 中的事务

Indigo 应用程序可以显式使用 System.Transactions,也可以使用依赖于 System.Transactions 的属性来控制事务。 一个选项是,对于用 ServiceContract 属性标记的类内的方法,以便使用 TransactionScope在事务中包装其工作,如前所述。 例如,此方法可能包含一个 using 语句,用于建立事务范围,然后更新该事务中的两个独立的数据库。

服务的方法还可以使用 特性控制事务行为。 服务可以使用前面所述的 属性,而不是显式使用 OperationBehavior System.Transactions。 下面是一个示例:

using System.ServiceModel;

[ServiceContract]
class XactOperations
{
 [OperationContract]
    public int Add(int value1, int value2)
    {
       return value1 + value2;
    }

 [OperationContract] 
 [OperationBehavior(RequireTransaction=true,
                    AutoCompleteTransaction=true)]
 int Update(int value1, int value2)
 {
       // Insert value1 and value2 into
    // two different databases
 }
}

此示例 Add中的第一个方法 不使用事务,因此其简单操作将像以前一样发生。 但第二个方法 Update前面是 OperationBehavior 属性 RequireTransaction 设置为 true 的属性。 因此,在此方法中完成的所有工作都将在事务内发生,就像在前面所示的块的 using 事务范围内一样。 由于 AutoCompleteTransaction 还指定了 属性,因此如果没有引发异常,事务将自动投票以提交。

如果调用此方法的客户端未在事务中运行,则 Update 该方法将在其自己的事务中运行-没有其他选择。 但假设客户端在调用 Update时已是现有事务的一部分。 方法完成 Update 的工作是加入客户端的事务,还是仍会在其自己的独立事务中运行? 答案取决于此服务是否可以接受客户端传递的 事务上下文 ,该上下文是使用 TransactionFlowAllowed 属性的 OperationContract 属性控制的选项。 如果未 TransactionFlowAllowed 附加到服务中的方法(如上例所示),则此方法中完成的工作永远不会加入现有事务。 但是,如果存在此属性,则方法能够加入其客户端的事务。

还值得强调的是,基于 Indigo 构建的应用程序可以参与包括非 Indigo 平台上运行的应用程序的事务。 例如,Indigo 应用程序可能会启动事务、更新本地SQL Server数据库中的记录,然后调用在 J2EE 应用程序服务器上实现的 Web 服务来更新另一个数据库中的记录。 如果此服务是事务性的,并且运行它的平台支持WS-AtomicTransaction规范,则这两个数据库更新都可以是同一事务的一部分。 与安全性和可靠的消息传送一样,Indigo 事务在 Web 服务实现的异类世界中工作。

队列

使用 WsHttpBinding 等绑定,Indigo 应用程序可以可靠地与基于 Indigo 构建的另一个应用程序或实现 WS-ReliableMessaging 的任何其他 Web 服务平台进行通信。 但是,尽管此规范定义的技术可以保证 SOAP 消息的可靠端到端传递,但它不能解决消息队列问题。 使用队列,应用程序将消息发送到队列,而不是直接发送到另一个应用程序。 接收应用程序准备就绪后,它可以从队列中读取消息并对其进行处理。 例如,当消息的发送方及其接收方可能未同时运行时,允许这种交互非常有用。

因此,Indigo 提供对消息队列的支持。 此支持基于 MSMQ 构建,这意味着与 Indigo 的大多数其他方面(如可靠的消息传送、安全性和事务)不同,Indigo 队列不会直接跨供应商边界 (互操作,尽管MSMQ-MQSeries网桥) 可用。

若要使用 Indigo 队列,开发人员会创建一个标准 Indigo 服务类,并照常使用 ServiceContract进行标记。 但是,此类的服务协定中的操作有一些限制。 具体而言,它们必须全部标记为单向,这意味着不会返回任何响应。 这并不奇怪,因为调用排队操作会将消息发送到队列而不是最终接收方,因此等待立即响应没有太大意义。 与其他任何服务类一样,排队的 Indigo 应用程序会公开终结点。 这些终结点使用绑定,例如 NetMsmqBinding(允许与其他排队的 Indigo 应用程序通信)或 MsmqIntegrationBinding(允许排队的 Indigo 应用程序与不使用 Indigo 的标准 MSMQ 应用程序进行互操作)。 Indigo 队列还支持排队环境的其他传统功能,例如死信队列和有害消息处理。

队列是一组大量分布式应用程序的正确方法。 Indigo 支持这种通信风格,使开发人员无需学习完全独立的队列技术即可生成排队的应用程序。

共存和迁移

Indigo 是一种在可靠、安全和事务性服务时代创建分布式应用程序的新式方法。 但是,需要了解的一个关键点是,安装 Indigo 不会中断任何现有应用程序。 在 ASMX、.NET 远程处理和其他功能由 Indigo 所包含的技术上运行的当前代码将继续运行,因此无需迁移到 Indigo。 但对于对当前 Microsoft 技术进行投资的组织,一个明显的问题依然存在:使用 Indigo 之前的技术编写的现有代码会发生什么情况?

对于未来受到 Indigo 出现严重影响的每种当前技术,开发人员需要了解两件事:基于此技术构建的应用程序是否会与 Indigo 上构建的应用程序进行互操作,以及将应用程序从此技术移植到 Indigo 环境需要多少工作。 以下是每种技术如何解决这些问题的简短说明:

  • ASP.NET Web 服务 (ASMX) :使用 ASMX 生成的 Web 服务将与 Indigo 应用程序互操作。 由于 ASP.NET Web 服务和 Indigo 都支持标准 SOAP,因此这并不奇怪。 将现有 ASP.NET Web 服务代码移动到 Indigo 需要一些机械工作,但应仍然简单明了。 这两种技术的基本结构非常相似,因此在大多数情况下,只需要更改属性和配置文件。 但是,更高级的功能(如 SOAP 扩展)不会直接移植到 Indigo。 相反,需要使用 Indigo 提供的扩展性选项重新编写它们。
  • .NET 远程处理:基于 .NET 远程处理构建的应用程序不会与基于 Indigo 构建的应用程序互操作,其线路协议不兼容。 将现有的 .NET 远程处理代码移动到 Indigo 需要一些工作,但这是可能的。 但是,构建自定义 .NET 远程处理扩展(如通道和接收器)的任何人都会发现,此代码不会移植到新世界。 Indigo 中可以有类似的扩展,但用于执行此操作的接口与 .NET 远程处理中的接口不匹配。
  • 企业服务:若要允许现有企业服务应用程序 (或其他基于 Web 服务的软件) 与 Indigo 客户端互操作,开发人员可以准确确定应公开该应用程序中的哪些接口。 使用 Indigo 提供的工具,可以为这些接口自动创建服务协定,并通过 Indigo 公开。 对于不是基于 .NET Framework (构建的企业服务应用程序的现有客户端,以及) 的其他纯基于 COM 的客户端,提供了 Indigo 名字对象以允许直接访问 Web 服务。 移植直接在 Indigo 上运行的现有企业服务应用程序所需的工作量与移植 ASMX 应用程序所需的工作类似。 大部分工作(虽然不是全部)对属性和命名空间进行直接的机械更改。
  • Web 服务增强 (WSE) :WSE 是 Microsoft 的战术解决方案,用于实现需要 WS-* 规范提供的部分或全部函数的 Web 服务应用程序。 使用 WSE 1.0 和 WSE 2.0 生成的应用程序不会与基于 Indigo 构建的应用程序进行互操作。 但是,基于 WSE 3.0 构建的应用程序(将在 Indigo 发布之前交付)将与 Indigo 应用程序互操作。 在可移植性方面,情况与已描述的技术类似:将现有代码从 WSE 移动到 Indigo 需要花费一些精力,但对于使用最终 WSE 版本的应用程序,这一工作将最小化。
  • MSMQ:由于 Indigo 的队列函数是在 MSMQ 上构建的,因此基于 Indigo 构建的排队应用程序可与直接在 MSMQ 上生成的排队应用程序进行互操作。 从原始.NET Framework提供的 System.Messaging 命名空间移植应用程序需要一些工作,因为此早期接口与 Indigo 提供的接口不同。 Indigo 交付后,开发人员应使用它而不是 System.Messaging 来创建大多数基于 MSMQ 的队列应用程序。

在 Indigo 可用之前,不需要排队的分布式 .NET 应用程序的最佳技术选择可能是 ASMX。 它易于使用,还提供最流畅的 Indigo 迁移路径。 对于需要其提供的功能(例如分布式事务)的应用程序,企业服务也有意义,而 MSMQ 仍然是排队应用程序的正确选择。 但是,.NET 远程处理应主要用于同一进程中两个应用程序域之间的通信。 在大多数其他情况下,ASMX 是应用程序到应用程序的直接通信的更好选择。

引入新软件始终会对现有内容产生影响。 Indigo 为构建面向服务的应用程序提供了共同的基础,为开发人员提供了更简单、更一致的平台。 虽然这种变化会产生一些痛苦,但 Indigo 的创建者的目标是使过渡尽可能顺利和简单。

结论

Indigo 代表了开发人员如何创建软件的重要演变。 随着面向服务的应用程序成为常态,Indigo 将成为 Windows 软件开发人员的主流技术。 其他 Microsoft 产品也将更改,以利用 Indigo 提供的功能。 例如,BizTalk Server将在 BizTalk Server 2006 发布后的某个时候添加对 Indigo 的支持作为通信选项。 由于 Indigo 为面向服务的软件提供了标准基础,因此它将是大部分 Windows 通信的基础。

这项技术的影响不会小。 在 Windows 上构建分布式应用程序(尤其是必须与其他平台上的应用程序互操作的应用程序)的任何人都应密切关注。 Indigo 将显著改变他们的世界。

 

关于作者

大卫·查佩尔是位于加利福尼亚州旧金山的查佩尔 & 协会 (www.davidchappell.com) 的负责人。 本文摘自他即将出版的《英迪格》一书,由艾迪森-韦斯利出版。