Microsoft Indigo 简介(3)

来源:互联网 发布:深度解析淘宝运营 编辑:程序博客网 时间:2024/05/01 20:09
选择宿主

实现 Indigo 服务的类通常编译到库中。按照定义,所有库都需要运行在宿主应用程序域和 Windows 进程中。Indigo 提供了两种实现服务的宿主库的方法。一种是使用宿主应用程序域和由 Windows 激活服务 (WAS) 提供的进程,而另一种是允许服务托管于运行在任意进程内的任何应用程序域中。本节将从 WAS 开始对两者进行介绍。

使用 Windows 激活服务托管服务

托管 Indigo 服务的最简单方式就是依靠 WAS。(请注意,在 Indigo 的第一个社区技术预览中不支持 WAS。相反,Indigo 服务可托管在 Windows Server 2003 和 Windows XP 上的 Internet 信息服务器中,虽然在该配置中只支持通过 HTTP 传递的 SOAP。)使用 WAS 与使用 IIS 提供的用于 ASMS 的托管机制很相似。其中,两者均依靠“虚拟目录”的概念,它是 Windows 文件系统中的实际目录路径的一个简短别名。

要查看 WAS 托管如何工作,假设前面所述的两个 Calculator 类中有一个类被编译到名为 calc.dll 的库中,然后放置到运行于 Windows Server 2003 上的系统的虚拟目录 calculator 中。要指示 calc.dll 中实现的 Indigo 服务需要使用 WAS 托管,开发人员应在 Calculator 虚拟目录中创建一个带有 .svc(当然是表示“服务”的意思)扩展名的文件。对于我们的简单示例,此文件可能称为 calc.svc,其整个内容可以是:@Service language=c# class="Calculator" %

一旦完成这一步,且如下一节所示定义了一个终结点,则客户端对 Calculator 服务的某个方法的请求将自动创建一个该类的实例,以执行指定的操作。该实例将运行在由 WAS 提供的标准进程中创建的应用程序域中。

在任意进程中托管服务

依靠 WAS 提供一个进程来托管 Indigo 服务无疑是最简单的选择。但应用程序经常需要从其自己的进程中公开服务,而不是依靠 Windows 提供的进程。幸运的是,这样做并不困难。下面的示例显示了如何创建托管前面定义的两种 Calculator 类的进程。

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

由于类 CalculatorHost 包含一个 Main 方法,因此它将作为一个独立的进程运行。要托管示例 Calculator 服务,此方法必须创建一个新的 ServiceHost 类的实例,在 Calculator 类中进行传递。(请注意,这种标准 Indigo 类属于“泛型”,通过括住其参数的 < 和 > 指示。泛型是版本 2.0 的 C#、Visual Basic .NET 及其他基于 .NET Framework 2.0 的语言中的一种新语言特性。)一旦创建此类的实例,则使服务可用所需的唯一操作就是对该实例调用 Open 方法。这时 Indigo 将自动将来自客户端的请求转到 Calculator 类中相应的方法。

要使 Indigo 服务能够处理来自其客户端的请求,托管它的进程必须始终在运行。对于 WAS 托管的服务这不是问题,因为 WAS 提供的标准进程可以确保这一点。然而宿主应用程序则必须自己解决这个问题。在这个简单的示例中,进程将按等待控制台用户输入的简单机制持续运行。

定义终结点

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

一个“合同”名称,指示该 Indigo 服务类通过该终结点公开哪个服务合同。一个标记有 ServiceContract 且未实现任何显式接口的类。(例如前面第一个示例中所示的 Calculator)只能公开一个服务合同。在这种情况下,所有终结点将公开同一合同。< 但是,如果一个类显式实现了两个或多个标记有 ServiceContract 的接口,则不同的终结点就可以公开不同的合同。

一个“地址”,指示该终结点位于何处。地址为标识一台计算机以及该计算机上的一个特定终结点的 URL。

一个“绑定”,决定如何访问该终结点。绑定决定可以使用哪些协议组合来访问该终结点,还决定了其他一些内容,如通信是否可靠以及可采用哪些安全机制。例如,假设一个服务的创建者希望允许客户端使用通过 HTTP 或 TCP 传递的 SOAP 来访问该服务。它们每个都是一个不同的绑定,因此该服务需要公开两个终结点,一个拥有 SOAP-over-HTTP 绑定,另一个拥有 SOAP-over-TCP 绑定。

绑定是实现通信的关键部分,为了使它们更易于使用。Indigo 包含了一组预先定义好的绑定集合,其中每个绑定指定一个特定的选项组。这个集合包括:

BasicProfileHttpBinding:遵循 Web Services Interoperability Organization (WS-I) Basic Profile 1.0,该规范定义了通过 HTTP 传递的 SOAP。这是未显式指定时,终结点的默认绑定方式。

BasicProfileHttpsBinding:遵循 WS-I Basic Security Profile 1.0,该规范定义了通过 HTTPS 传递的 SOAP。

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 通信。

上图显示了前面所示第一个 Calculator 服务的一个终结点的三个元素中每个元素的示例值。服务合同的名称为 Calculator,也就是实现该服务的类的名称,绑定则是 BasicProfileHttpBinding。假设此服务采用 WAS 托管,安装在前面所述的虚拟目录 calculator 中,并运行在一台名称为 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 元素。这个简单的示例仅有一个服务,因此只出现了一个 service。service 元素的 serviceType 属性标识了实现该配置所应用的服务的服务类,在本类中就是 Calculator。每个 service 元素可以包含一个或多个 endpoint 元素,其中每个元素指定一个可通过它访问此 Indigo 服务的特定终结点。在本例中,服务仅公开了一个终结点,因此仅出现了一个 endpoint 元素。终结点合同的名称为 Calculator,也就是实现它的类的名称。如果此配置文件用于前面所示的第二个 Calculator 服务,即使用显式接口定义其服务合同的服务,则 serviceType 属性的值将不变,但 contractType 的值将替换为 ICalculator,即该显式接口的名称。此处指定的绑定为 basicProfileHttpBinding,但由于它是默认设置,因此可以省略。假设 Calculator 是一个 WAS 托管的服务,则地址将自动生成,因此不需要在此配置文件中指定。

四、创建 Indigo 客户端

创建基本 Indigo 服务没有什么特别复杂的地方。创建 Indigo 客户端则更加简单。需要做的一切就是为服务创建一个称为“代理”的本地替身,它连接到目标服务的某个特定终结点,然后就是通过代理调用该服务的操作。下图显示了此原理。



 

 

 

创建代理需要准确知道目标终结点公开的合同,然后使用该合同的定义生成代理。在 Indigo 中,此过程是由一个称为 svcutil 的工具完成的。如果服务采用 Indigo 实现,svcutil 可以访问该服务的 DLL 以了解合同并生成代理。如果只有服务的 WSDL 定义可用,则 svcutil 可读取它以创建代理。如果只有服务本身可用,svcutil 可通过 WS-MetadataExchange 或一条简单的 HTTP GET 命令直接访问它,以获得该服务的 WSDL 接口定义,然后生成代理。

不管它是如何生成的,客户端均可创建该代理的一个新实例,然后通过该实例调用服务的方法。以下是 Calculator 类的一个客户端的简单示例:

using System.ServiceModel;using Indigo.Example; // 所生成的代理类的命名空间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 在任何时候都可以处理对该服务的多个客户端请求,每个请求运行于一个不同的线程上。与此类似,ServiceBehavior 的 InstanceMode 属性可用于控制如何创建和销毁服务的实例。如果 InstanceMode 设置为 PerCall,将为处理每个客户端请求创建该服务的一个新实例,然后当该请求完成时将其销毁。而如果设置为 PrivateSession,则将使用服务的同一实例处理来自某个客户端的所有请求。

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

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

与此类似,OperationBehavior 属性上的属性允许控制实现该操作的方法的模拟行为、其事务要求(将在后面讲述)以及其他内容。

消息传输选项

本文中所示的简单示例采用了同步远程过程调用 (PRC) 方法来实现客户端/服务交互。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 正在以一种可行且有效的方式实现这一目标。