可管理的服务





5.00/5 (28投票s)
本文介绍了一种模型驱动的 WorkflowServices 的设计、实现和工具支持,该服务在存储库中逻辑集中,但在运行时物理分散投影。
目录
特点
- 松耦合设计模式
- 服务模型(契约 + 应用程序)
- 存储库中的逻辑集中模型
- 契约优先
- 服务虚拟化
- 逻辑终结点
- 消息与服务中介
- 部署推送模型(仅适用于 IIS/WAS)
- 部署拉取模型(Windows 服务上的自定义托管)
- 工具支持
- msi 安装
系统要求
- 支持的(已测试)操作系统:Windows Vista Ultimate Service Pack 1,Windows Server 2008 Standard
- 本次下载支持 32 位平台
- .Net 3.5 SP1
- MMC 3.0
- Microsoft SQL Server 2005/2008(默认安装为 SQLEXPRESS)
- Web Deployment Tool Release Candidate 1 (x86)(用于部署功能)
- Visual Studio 2008 SP1(用于开发功能)
引言与概念
最近,PDC 2008 大会展示了微软技术路线图,重点关注由元数据驱动的端到端解决方案,以及几乎可以运行在任何地方的托管。这种方法需要对应用程序架构和不同的开发思维进行许多垂直和水平的改变,它更侧重于“是什么”而不是“如何”去做。这是微软技术的一个非常重要的步骤,堪比 .Net 可管理代码技术的推出。
我们可以看到微软在此路线图上的技术目标,例如提供将业务模型映射到物理模型进行投影的技术,通过声明式而非命令式实现。将业务模型分解为小的业务活动(代码单元)并在元数据驱动模型中编排它们,使我们能够将业务与技术和基础设施解耦。在 PDC 2008 上,微软推出了 xaml
堆栈,作为运行时投影的元数据驱动模型的通用声明。通过 xaml
堆栈,组件可以在前端和后端根据托管环境(例如:桌面、移动设备、服务器或云)在运行时进行投影。声明式地将活动打包到服务中,使我们能够以 B2B 的方式通过业务逻辑模型使用我们的服务。
服务由逻辑连接(互操作性)表示,用于在元数据驱动的分布式架构中解耦业务模型。对于这种架构,服务模型需要基于元数据来管理连接和服务中介,换句话说,能够以声明式的方式管理终结点后面的业务。请注意,这个概念对服务中包含的内容没有限制,它可以是一个代表简单工作流的小业务模型,也可以是一个完整的应用程序(SaaS - 软件即服务)。
最后,PDC 2008 展示了服务模型在下一代微软技术中的重要性。例如:Windows Azure 平台、Dublin、Oslo Modeling platform、Dublin 等。该策略基于建模、在存储库中存储元数据以及部署元数据以进行运行时投影,因此 Model-Repository-Deploy
是企业应用程序即将到来的思维模式。最后一步,例如 Deploy
,将允许我们将逻辑集中模型部署到几乎任何托管环境的物理分散模型中。它可以是自定义托管、Dublin 或 Windows Azure 平台(云计算)。从服务角度来看,Windows Azure 平台代表了服务之间逻辑连接的服务总线。这对这些技术以及开发人员的(不同)思维过程是一个巨大的挑战。
下图展示了可管理服务的策略
业务模型被分解为由存储在存储库中的元数据描述的服务模型。存储库代表一个逻辑集中模型,它实际上是一个包含业务活动、组件、连接等的大型知识库。这个知识库是在设计时手动通过建模工具学习的,但也可以从其他来源动态学习以进行调优。根据容量,我们可以基于前一个模型构建下一个模型,依此类推,从而提高存储库中的知识水平。还有一个部署模型,允许我们将知识库部署到运行时环境。当然,存储库可以预置一些最小的通用知识,例如 WokflowServiceModel
、ServiceModel
、Dublin
、Azure
的部署等。
还有一件事,我认为存储库(作为模型的知识库)对企业应用程序具有巨大的未来潜力。将存储库托管在云服务器(例如 Windows Azure)上,模型可以被其他模型发布和使用,或者它可以被私有部署、转换为其他模型等。
可管理服务支持的架构允许我们将业务模型分解为服务,然后将元数据部署到可以托管在云、Dublin、IIS/WAS 或自定义主机中的托管环境中。下图展示了这一概念
基于建模部署的服务可以连接到服务总线,并被其他私有契约使用。从业务工作流的角度来看,服务代表了设计时建模的业务活动。将业务逻辑分解为位于业务工具箱(目录)中的活动,使我们能够服务中对其进行编排。这种策略的结果是一个非常灵活的驱动模型解决方案。
本文介绍了可管理服务的策略,该服务由存储在存储库中的元数据驱动,并利用了当前微软技术的优势,例如 .NetFX 3.5。该项目始于 WorkflowServices (.NetFX 3.5)
的引入,并在我过去在 codeproject 上发布的文章中有所体现,例如 VirtualService for ESB 和 Contract Model for Manageable Services。我推荐阅读它们以获取更多详细信息。本文侧重于应用程序模型(服务模型)。然而,本文包含了建模(工具)、存储、部署和投影元数据的所有部分。
让我们开始吧,我有很多东西要展示,希望您熟悉最新的微软技术,特别是 .NetFX 3.5。
首先,让我们关注如何将元数据从存储库部署到主机。注意,从通用角度来看,存储库保存模型;这些模型可以是存储在关系表中的其他模型的模型等。根据部署模型,应用程序模型必须以运行时投影仪可理解的资源形式导出到运行时。基本上,此功能中有两种部署的处理器,基于元数据如何从存储库导出。
1. 从存储库推送元数据
这种情况基于位于托管环境中的运行时元数据的本地存储库。下图展示了这种情况
Push
部署是部署过程的典型场景。此场景的第一步是以离线方式(应用程序未运行时)将资源(例如 config、xaml (xoml/rules)、xslt
等)推送到特定存储,通常是文件系统。下一步是启动应用程序,此时运行时投影仪会将资源加载到应用程序域,创建 CLR 类型、其实例并调用操作。在此过程中,本地存储库(例如,文件系统)对于任何更改都将被逻辑锁定,并且在资源更改后,应用程序域将被物理关闭。
Push
部署简化了部署过程,本地存储库与元数据源(存储库)完全解耦,换句话说,我们可以从 Visual Studio、脚本等推送元数据。在此部署期间,我们正在创建(缓存)元数据的运行时副本(资源),因此我们与建模/存储库完全隔离。这是这种情况的优势。
Dublin PDC 版本展示了通过 Dublin 扩展在 IIS/WAS 上托管服务的此功能。我们可以看到托管在 Windows Server 2008 或 Vista/Windows 7 计算机上的 IIS/WAS 中的服务的监控、管理等出色功能。Dublin MMC 允许在本地元数据副本上进行一些本地更改。我认为这对小型项目很有帮助,但对于企业应用程序来说,在生产服务器上管理本地元数据副本而不进行跟踪、版本控制和回滚不是一个好方法。此外,本地元数据是一个复杂资源,仅代表存储在存储库中的逻辑模型的一部分。因此,所有更改都应在集中式逻辑模型中进行,然后再次推送到本地存储库。我认为,使用建模工具或类似的工具来管理存储库中的服务应该是可行的方法,该工具可以处理逻辑模型中的更改,尤其是在部署到云时。
好的,现在让我们看看将元数据部署到主机的其他选项,例如从存储库拉取运行时元数据。
2. 从存储库拉取元数据
这种情况没有本地存储库来缓存中央存储库的运行时元数据。它基于位于应用程序域中的引导/加载程序组件从存储库中拉取运行时元数据。
当应用程序进程启动时,引导/加载程序将发布(广播)事件消息以获取元数据,或通过询问存储库来获取元数据。这种情况的主要优点是动态地即时管理存储库中的服务,这可以直接完成,也可以通过发现机制完成,并且还能够根据分析和处理规则等即时调整元数据。因此,不存在机器级别的服务可管理性,所有更改都必须通过中央存储库工具完成。请注意,此情况未在 PDC 2008 中引入。
现在,让我们关注存储在存储库中的元数据。可管理服务的概念在我之前的文章 Contract Model for Manageable Services 中已详细描述,服务虚拟化及其使用元数据进行管理的结果如下图所示
如您所见,服务虚拟化主要由两个模型描述,即契约模型和应用程序模型,换句话说 ServiceModel = ContractModel + ApplicationModel
。这两个模型在逻辑上是隔离的,并通过 EndpointDescription
连接。契约模型的职责是描述用于连接的 ABC(地址-绑定-契约)元数据。另一个模型,应用程序模型,描述终结点后面的服务活动。它不需要知道它的模型是如何连接到其他应用程序模型的。因此,这两个模型可以在设计时单独创建,并且只在最后的建模步骤(如托管)中,我们需要为应用程序模型中的特定服务分配物理终结点。
服务模型允许对连接、消息传递、MEP(消息交换模式)、消息转换、服务编排等进行完全中介,并且其结果存储在 SQL Server 关系表中以供后续编辑或部署。服务中介功能受限于 .NetFX 3.5 技术,特别是 WCF 和 WF 范例模型(WorkflowServices
),它们用于实现可管理服务。当前 .NetFX 3.5 技术集成了两个用于运行时的模型,称为 WorkflowServices
,但每个模型仍然需要使用自己的元数据,例如:config、xoml 和 rules 文件。在即将推出的 .NetFX(4.x)新版本中,我们实际上只有两个资源,如 config 和 xaml,用于在运行时 CLR 类型中投影 WorkflowService(或 Service)。
下图展示了元数据驱动服务的概念,其中设计工具负责在存储库中创建元数据,而部署工具则用于为特定主机(如 IIS/WAS 或自定义托管)的运行时投影打包元数据。注意,本文仅实现了部署元数据的选项到 IIS/WAS。当然,您也可以部署到 Dublin(PDC 版本),并与 .NetFX 4.0 并行部署。在这种情况下,您的 Dublin 功能将受到限制。
关于 Azure?此时(PDC 版本),我们无法部署任何自定义活动,包括根活动。工作流编排在云上需要一套预定义的云活动。我们将在 PDC 2009 大会上看到更多相关内容。
如上图所示,服务模型的复杂性由设计工具“编码”,该工具可以是相当复杂的,也可以很简单,仅使用 MMC 控制台。本文提供了一个使用 MMC 工具建模服务的解决方案,该工具是我为契约模型和应用程序模型构建的。需要对 WCF/WF(使用该工具在此级别使用)、xslt、XML 技术(如绑定、契约、工作流活动、xpath 等)有一定的了解。
总而言之,由契约模型和应用程序模型表示的 可管理服务
存储在一个称为存储库的集中式知识库中,并可以从中部署到物理服务器进行运行时投影和运行。
这种 模型优先
方法需要一个设计工具来高效地生成模型元数据。我们的建模生产力取决于我们的设计工具“烹饪”模型的能力。存储库显然是空的知识库,我们需要为其教授连接性和服务中建模的业务活动。每个服务模型都可以用作其他模型的模板。
好的,我们将在文章的下一节以略微不同的方式继续,因为目标是介绍服务、存储库和工具的建模,而不是如何实现它。我将从设计和实现的观点描述一些有趣的部分,请参阅 附录:设计与实现 段落。
存储库
存储库是可管理服务(由契约模型和应用程序模型描述)的中央存储。本文仅描述了 应用程序模型
。下图显示了其 SQL Server 表的架构
应用程序模型的核心是 Service
架构。该架构中有五个重要资源,例如 config
、xoml
、rules
、xslt
和 wsdl
。所有这些资源都具有运行时格式,投影仪可理解,因此在部署过程中不需要自定义提供程序。原因在于 Workflow Designer 支持,工作流编排的结果被导出为 xoml/rules 资源。在 Service 架构中还有两个附加资源,例如 xslt
和 wsdl
。第一个,xslt
资源用于消息中介,第二个用于元数据交换操作。这两个资源都是可选的。
注意,当将契约模型中的终结点分配给服务且契约是未类型化时,wsdl
资源会自动包含。
每个服务都可以分配 AppDomain,每个 AppDomain 都可以分配应用程序,这是应用程序模型中的最高级别。该模型支持资源版本控制。应用程序资源也代表了部署场景的入口点。注意,本文不包括用于在应用程序模型中管理自定义程序集的 GroupOfAssemblies 和 Assemblies
架构。
如您所见,上述架构看起来非常直接,并且可以转换为另一个建模平台(例如 Oslo),并且此迁移过程基本上取决于 xoml/rules 资源,例如工作流从 WF 3.5 迁移到 4.0 版本、工作流的复杂性等。
如我之前提到的,可以推送或拉取存储库中的元数据进行运行时投影。在拉取的情况下,存储库支持一个服务来从存储中获取资源。该服务的名称是 LocalRepositoryService
。
现在是时候展示用于将数据生成到 SQL 表的设计工具(元数据工具)了。
工具支持
可管理服务的设计工具实现在 MMC 管理单元中,其中一个中央窗格专用于特定的用户控件。左右窗格用于处理范围节点及其操作。下图展示了 MMC 中的两个窗格,即范围节点和工作流设计器的用户控件:
范围节点的详细信息显示在以下屏幕截图
如您所见,可管理服务有两种模型,即契约模型和应用程序模型,位于左侧窗格中。第一个用于创建终结点的模型,第二个是服务模型和服务托管。此示例展示了一个应用程序 Test
和域 abc
以及 3 个 Echo
服务。其中一个 Echo 服务是用于版本化同一域中 Echo
服务的路由器。我将在后面详细描述此服务。
通过选择特定的范围节点,我们可以在中央窗格中获取特定操作的用户控件。例如,当我们单击 Services
范围节点时,下图显示了所有服务的列表。
工具的第一步是创建服务。通过选择 Services 范围节点,我们可以选择创建新服务的选项。
添加新服务
服务创建器的第一页询问我们属性 IsTemplate?如果选中,则该服务可用于创建另一个模板和/或简单地删除现有模板等。
单击 Next
填充服务属性,如名称、主题等。注意,本文仅支持 XomlWorkflowService
作者。
单击 Next
选择我们新服务模板。在此示例中,选择了 EmptyIntegrator
,这是一个服务编排的基本模板。此模板只有一个用于以请求/响应方式接收消息的活动。
单击 Next
,将出现服务创建者向导的最后一页。最后一页总结了有关我们新服务的所有信息。单击 Finish
后,将在存储库中为此服务模板创建元数据。
下图显示了添加新服务的屏幕截图
如您所见,在 Test 主题组中有一个 MyService
。中央窗格显示了用于服务中介的工作流设计器。所有属性都应与服务名称相关,例如 MyService
。Microsoft Workflow Designer 已嵌入到 MMC 管理单元中,并进行了一些修改,例如使用 XmlNotepad 2007 用于 xoml 资源。此设计器负责以与 Visual Studio 中托管相同的方式创建 xoml
和 rules
资源。
如我之前提到的,可管理服务是使用 .NetFX 3.5 版本中的 WorkflowServices 范例实现的。以下代码片段显示了 xoml
资源,使用上面示例 MyService 中创建的类似的基本模板。请注意,为简化代码片段省略了命名空间。
<ns0:WorkflowIntegrator x:Name="MyService" Topic="Test" Version="1.0.0.0" MessageRequest="{x:Null}" MessageResponse="{x:Null}" ContextForward="InOut"> <ns1:ReceiveActivity.WorkflowServiceAttributes> <ns1:WorkflowServiceAttributes Name="MyService" ValidateMustUnderstand="False" ConfigurationName="MyService"/> </ns1:ReceiveActivity.WorkflowServiceAttributes> <ns1:ReceiveActivity x:Name="receiveActivity1" CanCreateInstance="True"> <ns1:ReceiveActivity.ServiceOperationInfo> <ns1:TypedOperationInfo Name="ProcessMessage" ContractType="{x:Type p7:IGenericContract}"/> </ns1:ReceiveActivity.ServiceOperationInfo> <ns1:ReceiveActivity.ParameterBindings> <WorkflowParameterBinding ParameterName="(ReturnValue)"> <WorkflowParameterBinding.Value> <ActivityBind Name="MyService" Path="MessageResponse"/> </WorkflowParameterBinding.Value> </WorkflowParameterBinding> <WorkflowParameterBinding ParameterName="message"> <WorkflowParameterBinding.Value> <ActivityBind Name="MyService" Path="MessageRequest"/> </WorkflowParameterBinding.Value> </WorkflowParameterBinding> </ns1:ReceiveActivity.ParameterBindings> </ns1:ReceiveActivity> </ns0:WorkflowIntegrator>
工作流活动(根)通过 WorkflowIntegrator
(派生自 SequentialWorkflowActivity
)自定义,该自定义活动用于保存服务信息,如主题和版本。工作流 Name
和 ConfigurationName
必须与服务名称相同。此外,还有用于绑定目的的 Request/Response 消息的依赖属性,范围是工作流。最后一个属性是 ContextForward
,这是一个用于绕过上下文头或清理上下文头的配置选项。其默认值为 InOut
。
请注意,上述 xoml 资源代表了接收请求/响应消息交换模式消息的最小(必需)元数据。此元数据由设计工具自动生成,但如果将 xoml 从另一个源直接拖放到 XmlNotepad 控件,则根活动必须是 WorkflowIntegrator
活动。
好的,现在我们可以进行服务中介了,换句话说,使用工作流工具箱中的活动来编排服务。
服务消息中介
基本上,服务中介是在服务中拦截和修改消息。中介支持的服务允许以松耦合的方式将逻辑业务模型分解为业务服务。在本章中,我将重点关注可管理服务(由 System.ServiceModel.Channels.Message
表示)中的服务消息中介。根据 MessageVersion
,它是业务部分(称为负载或操作消息)和一组附加上下文信息(称为头)的组合。头和负载在传输级别上物理隔离,这允许在与业务部分分开地反序列化网络流。换句话说,服务消息可以通过其头进行检查,而无需反序列化(消耗)其负载。例如,服务路由器(基于消息头中的上下文信息)可以在不知道业务正文的情况下将消息重新路由到适当的消费者。
基于以上,我们可以看到头可以通过由已知 CLR 类型驱动的中介原语轻松进行中介。消息的其他部分,如负载,需要使用松耦合的中介原语。这种类型的中介原语需要使用 xpath
和 xslt
等技术。
xpath 中介原语使用 xpath 1.0 表达式来标识消息中的一个或多个字段,用于基于其值进行过滤或选择。让我们看下面的 XPathIfElse
和 XPathInspector
自定义活动(中介原语)。
请注意,可管理服务的默认消息契约是未类型的,这意味着服务将收到序列化的头信息的原始消息,但负载未被消耗。
XPathIfElse 活动
XPathIfElse 活动是基于 Microsoft IfElseActivity
对 CLR 类型原语的特性的自定义活动。区别在于条件表达式,其中使用 xpath 表达式而不是 CLR 类型表达式。父活动(如 XPathIfElse 活动)通过每个分支活动(如 XpathIfElseBranch
活动)绑定到服务消息以进行检查。每个分支都有自己的 xpath 表达式用于在服务消息上进行评估。注意,分支操作的是服务消息的副本。XPathIfElseBranch 活动中有一个 MatchElement 来选择 xpath 表达式的消息元素,以提高性能。稍后您将看到我们如何最小化反序列化消息正文带来的性能影响。注意,XPathIfElse 活动不会更改消息内容;它是一个被动的中介原语。
下图是 XPathIfElse
自定义活动的屏幕截图,显示了其属性网格
MatchElement 是一个枚举类型,具有以下选项
public enum MatchElementType { None, // explicitne true or false Root, // by xpath expression (message is deserialized) Header, // by xpath expression (no deserialization) Body, // by xpath expression (message is deserialized) Action, // by value EnvelopeVersion, // by value IsFault, // by value IdentityClaimType // by value }
例如,如果分支条件需要仅针对特定操作执行,那么选择 Action
的 MatchElement 并输入操作的实际值作为 XPath 属性将在此中介过程中最小化性能损失。
XPathEditor 可以帮助查找服务消息中的特定 xpath 表达式。该编辑器在键入 xpath 表达式时具有内置的交互式验证。其概念基于消息版本和架构的已知信息。本文的版本仅限于 MessageVersion,并手动将元素拖放到 XmlNotepad 控件。完整版本允许从契约模型中选择架构,将所有类型公开到组合框中,然后将它们插入消息中。
XPathEditor 中有一个插入或编辑命名空间的选项卡,当 XPath 表达式需要使用额外的命名空间时。此命名空间集合存储在隐藏的 XPathIfElseBranch.Namespaces
属性中。
下图是 XPathEditor 的屏幕截图。双击 XPathIfElseBranch 活动也可以显示它。
XPathInspector 活动
XPathInspector 是一个自定义活动,用于检查服务消息的字段和/或值。有一个枚举属性可以指定检查范围,以最小化性能损失。布尔表达式的结果可以与其他活动绑定,例如 IfElseActivity
。
键入 XPath 表达式时,可以使用 XPathEditor,方式与上面部分所述相同。
TransformMessage 活动
设计工具负责创建用于运行时中介原语的 xslt 元数据。本文仅支持手动创建此元数据。XsltMapper Editor 不包含此功能来生成此 xslt 资源。
下图显示了 xslt 元数据的格式
如上图所示,有一组 mediators
,具有唯一的 name
(在本例中为“transform”)。每个 mediator
都有包含 parms
集合的选项,允许以字符串类型传递服务参数。在本例中,我们有两个参数,如 prompt
和 id
。
TransformMessage
是一个自定义活动,它是一个 xsl 中介原语,用于根据其名称定义的 xslt 中介器将 MessageInput 转换为特定版本的 MessageOutput。
下图显示了其图标和属性网格
TransformMessage 活动还可以在消息流中使用,以提取一些业务部分,创建一个新的根元素,使用 MessageVersion.None
用于服务内部使用。这种情况侧重于性能问题,以优化消息副本的数量。
以下示例显示了 TransformMessage
和 XPathIfElse
活动的用法,以优化消息中介期间的性能问题
上述服务中介将接收到的消息转换为基于内部服务架构的小型复杂元素,以便在后续具有更多分支的活动中进行简单快速的评估。此解决方案通过 TransformMessage 活动消耗传入消息的副本,而不是由每个分支活动消耗消息。选择分支后,会将原始消息传递给其业务处理。在上面的示例中,消息被发送到特定的服务。注意,中介者之间没有输入/输出消息的 MessageVersion。
让我们看另一个 TransformMessage 活动的示例。在此示例中,分支 V1000 在 ProcessMessage 活动之前和之后转换接收到的消息契约以用于其特定的目标服务。
如您所见,TransformMessage 是一个非常强大的活动。它取决于设计工具生成正确 xslt 元数据的速度。建议使用某些第三方工具进行更复杂的转换,例如来自 Altova 的工具来生成 xslt 资源,然后将其拖放到 XmlNotepad 控件中。
CreateMessage 活动
CreateMessage 是一个自定义活动,它是一个中介原语,用于根据 xslt 元数据和 MessageVersion 创建消息。下图显示了其屏幕截图,包括属性网格
上面活动中的所有属性都与 TransformMessage 具有相同的功能。您可以看到,CreateMessage 中没有 MessageInput,因为它是一个创建者,而不是消息的消费者。
以下是使用此自定义活动广播事件消息的示例
处理接收到的消息后,服务将生成一个通知消息,该消息由 CreateMessage 自定义活动中的中介原语创建。
CopyMessage 活动
CopyMessage 是一个自定义活动,用于生成消息的副本以供将来使用。下图显示了其图标和属性
TraceWrite 活动
TraceWrite 是一个用于诊断目的的自定义活动。消息和格式化文本显示在调试输出设备上,例如 DebugView for Windows。
在本章中,我重点关注终结点之后的服务中介,换句话说,在接收到消息之后。这种声明式中介使用由 WF 编程模型处理的中介原语,这些原语实现为自定义活动。
WorkflowServices 是 WCF 和 WF 模型的集成模型,因此可以在终结点层(模型)中进行服务中介。这种中介在我之前的文章 VirtualService for ESB 中已详细介绍,但是,以下代码片段展示了此功能
<endpointBehaviors> <behavior name='xpathAddressFilter'> <filter xpath="/s12:Envelope/s12:Header/wsa10:To[contains( ... )]" /> </behavior> </endpointBehaviors>
通过将地址过滤器注入终结点行为管道,我们可以中介前端的服务,例如拒绝传入消息、将消息路由到其他服务等。这种低级中介由中介原语 ESB.FilteringEndpointBehaviorExtension
使用 XPath 1.0 表达式实现。在使用此功能之前,必须将中介原语添加到扩展中,如下面的服务配置文件部分所示
<extensions> <behaviorExtensions> <add name='filter' type='ESB.FilteringEndpointBehaviorExtension, ESB.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'/> </behaviorExtensions> </extensions>
注意,本文不包含此类中介的视觉设计器,必须在 XmlNotepad 控件中手动完成配置元数据。
元数据交换终结点(MEX)
元数据交换终结点(MEX)是 WCF 编程模型中的一个特殊终结点契约,用于导出描述服务连接性的元数据。本文档在运行时构建,基于每个类型化契约的服务终结点描述。换句话说,如果契约是未类型化的(Action = "*"),则无法动态构建通用契约的元数据。
例如,假设我们有一个版本化路由器服务,如服务中介部分所示。消耗分支 V1000 和 V1001 的版本不同,但它们的终结点相同。因此,路由器终结点不能具有 MEX 功能,因此请求被转发给实际服务进行处理。这没关系,但如果目标服务终结点对于支持中介的服务也是未类型化的(通用契约)呢?
嗯,有一个解决方案,即在契约模型中准备此 MEX 文档,并将其交付到运行时中的应用程序(服务)模型。换句话说,设计时可以以契约优先的方式创建 MEX,将其存储在存储库中,然后在消费者需要查询其 MEX 文档的物理终结点时将其部署到运行时投影仪。
WSRT_Mex 活动
WSRT_Mex 是一个自定义活动,用于响应 WS-Transfer 方式的 MEX 文档请求。下图显示了其图标和属性网格
根据 Wsdl 属性,MEX 文档可以从存储库或本地从文件系统中获取。当服务托管在 IIS/WAS 上时,此资源将与 config、xoml、rules、xslt 和 svc 等其他资源一起位于站点内。
WSRT_Mex 自定义活动的使用非常简单。下图展示了一个业务服务(Imaging)的示例,其中 MEX 有自己的分支来处理 WS-Transfer Get 请求
操作将评估 Action 头以进行以下表达式,以处理 MEX 操作
System.ServiceModel.OperationContext.Current.IncomingMessageHeaders.Action == "http://schemas.xmlsoap.org/ws/2004/09/transfer/Get"
请注意,不必在服务配置中使用终结点 MEX。从传入消息的角度来看,mex 操作消息的处理方式与另一个操作一样是透明的。由服务中介负责在物理终结点提供此功能。
获取 MEX 文档的另一种首选方式是查询存储库。存储库预先知道模型的所有详细信息,因此物理服务不需要在线(已部署并运行)即可获取此信息。
下图展示了一个 Microsoft 实用程序 WCF Test Client,用于动态创建代理以调用存储库以获取 MEX 文档
如您所见,在再次查询之前已添加了 Echo
服务。存储库服务的访问可在配置文件(我们的示例是 net.pipe:///repository/mex?
)中配置。
关于存储库和 MEX 的最后评论。从存储集中式逻辑业务模型的 MEX 文档的发现非常强大,并为元数据/模型驱动的架构带来了更多挑战。最近的 PDC 2008 大会介绍了用于云计算和 Windows Azure 平台的微软技术。将存储库放在云上以交换终结点 MEX 和其他服务元数据模型怎么样?思考一下,并自己找出答案。
应用程序部署
如我之前提到的,应用程序模型可以被推送或拉取到托管在物理计算机上的运行时投影仪。设计工具内置了部署特定应用程序版本的功能。此选项仅适用于 IIS/WAS 托管。
部署过程分为两个步骤。第一步,将部署包(针对选定的应用程序)创建并存储在存储库中。在下面的示例中,我们可以看到 Application.Test (1.0.0.0) 包
单击 Create
按钮,将创建一个新包并将其存储在存储库中。包的内容(存储在存储库中)显示在用户控件上。我们可以看到 IIS/WAS 站点中托管的物理组织包。
下一步是在托管服务器上安装此包。单击 Install
按钮,将出现以下对话框,询问一些信息,例如服务器名称、站点等……
单击 Deploy
按钮,包将被安装在特定服务器上,并在物理路径中创建虚拟目录以用于此应用程序。注意,本文版本存在一些限制,例如仅支持 localhost 和 Default Web Site。
成功部署后,部署的应用程序中的可管理服务即可使用。应用程序或契约模型中的任何更改都必须存储在存储库中,然后刷新并重新部署。
安装
本文包含用于安装设计工具和存储库的 msi 文件。这为您提供了快速启动和最少的安装步骤,与源代码包(如编译、安装 MMC 管理单元、Windows 服务、创建数据库等)相比。注意,本文版本仅支持将存储库安装在您的本地计算机上。
安装过程分为两个步骤
- 安装设计工具(MMC 管理单元)
- 安装存储库
让我们逐步执行以下步骤,请注意您应该以 管理员身份运行
进行此过程
- 下载包含的 msi 文件
- 打开此文件
- 在所有页面上单击 Next 按钮
- 在 Installation Complete 页面上单击 Close 按钮以退出安装过程
之后,所有组件都已安装在文件夹:C:\Program Files\RomanKiss\ManageableServices\
中,并准备好管理存储库中的元数据,但第一次运行时会失败,因为创建存储库的过程不包含在 msi 安装中。此过程基于存储库子文件夹中的脚本手动调用。主要原因是避免在没有备份存储库的情况下发生任何意外的自动安装过程。
因此,安装的下一部分是创建存储库
- 以管理员身份打开 cmd 控制台
- 更改目录到
C:\Program Files\RomanKiss\ManageableServices\Repository
- 打开
setup.bat
文件以创建和加载存储库 - (打开 cleanup.bat 文件以删除存储库)这是在从头创建另一个存储库之前清理存储库的步骤
setup/cleanup 批处理文件使用预定义的数据库名称作为存储库,例如本地 SQLEXPRESS 服务器上的 LocalRepository
。如果您计划更改它,这就是该文件。
哦,还有一个地方,Windows 服务配置文件也需要更改,或者您可以停止 LocalRepositoryService
,在启动参数文本框中输入您的存储库名称,然后重新启动。
注意,WF SQL 脚本不是此安装的一部分。以下 connectionString 用于 config 资源
Data Source=localhost\sqlexpress;Initial Catalog=PersistenceStore; Integrated Security=True;Pooling=False
以上就是安装的全部内容。现在您可以在桌面上找到 Manageable Services
图标
并打开它。您将为每个 MMC 管理单元收到 ConnectionToLocalRepository
对话框的提示。
对于默认安装,单击 OK 按钮,然后您就可以开始设计可管理服务的模型了。
我建议您尝试使用契约和应用程序范围节点及其操作等,以了解其功能。不用担心在存储库中生成一些混乱;可以使用 clenaup.bat
文件非常轻松地清理并使用 setup.bat
文件重新创建。
还有一件事,如果 MMC 管理单元抛出异常,请退出 MMC。我很抱歉这个错误,只需关闭 MMC 并重新打开一个新的,然后继续。所有更改都以事务方式存储在存储库中(保存时接受)。请记住这一点,然后再关闭 MMC。
好的,现在是时候用一个简单的例子来完成从设计、部署到测试的全过程了。
可管理服务示例
这是一个简单的 Echo
服务示例,具有以下契约
[ServiceContract] interface IService { [OperationContract] string GetFullName(string firstName, string lastName); }
我将向您展示管理此服务模型的所有步骤,包括使用 Microsoft WCF Client Test 程序代表的虚拟客户端对其进行测试。
我将假定您已完成上述章节中描述的安装。为简化此示例,存储库已预加载了 3 个简单的 Echo 服务,例如两个版本的 Echo 服务和一个用于版本化路由的服务。
示例分为以下主要步骤(操作)
- 为我们的示例创建契约模型,例如
IService
- 创建 Echo 服务 - 此步骤已在安装过程中完成,服务已导入到存储库。
- 为我们的 Echo 服务创建应用程序模型并在 IIS/WAS 上托管
- 将应用程序部署到目标服务器
- 测试
IService 的契约模型
如我之前提到的,存储库是契约、服务、终结点等的知识库。如果这些知识不存在,我们必须手动添加或从其他地方导入,例如程序集、wsdl 终结点等。在我们的示例中(为简单起见),以下步骤演示了从程序集导入契约。
步骤 1 - 导入契约
选择 Contracts
范围节点,右键单击操作 Import Contracts
上面的用户控件用于从程序集或 URL 终结点导入契约。选中 FromAssembly
复选框,然后单击 Get
按钮。您应该会在我们的管理单元中看到上述屏幕截图。
选择契约 IService
,然后单击 Import
按钮。此操作会将所选契约的所有元数据导入存储库。
所以,现在我们的存储库有了更多的知识,您可以在 Schemas、Messages 和 Operations 范围节点中查看它,但在 Endpoints 中不行。这是下一步。
步骤 2 - 新终结点
选择 Endpoints
范围节点,右键单击操作 Add New Endpoint
并填充以下用户控件
- 名称:
Echo
- 地址:https:///Test/abc/Echo/Echo.svc
- 绑定:
wsHttpContextNoneSecure
- 描述:
type some description
- 版本:
1.0.0.0
- 选择
XmlSerializer
- 勾选
XmlFormat
- 选择契约
IService
- 选择
GetDocumentByKey
- 选择
GetFullName
完成上述步骤后,您的管理单元应与下图所示相同
现在,单击 Export
按钮。以下屏幕截图将在您的管理单元中显示
在此步骤中,我们有机会预览将要发布的内容,例如 wsdl
元数据。此外,我们还可以看到所有生成的契约类型。
下一步是填写信息,以便在存储库中以唯一密钥(如名称和主题)存储此元数据。因此,键入名称:Echo
和主题:Test
,勾选 ExportMetadata
并单击 Save
按钮。此操作的结果是以下屏幕截图
现在,此时,存储库拥有第一个元数据(终结点),可以以契约优先的方式发布,换句话说,客户端可以从存储库查询元数据(wsdl)。如上图所示,此功能已内置到管理单元中,因此我们可以成为新元数据的第一位客户端(测试者)。单击 Run
按钮,您应该会看到 svcutil 如何处理此虚拟客户端的 IService
契约。这是通过其服务 LocalRepositoryService
与存储库进行的真实连接。
当然,任何客户端,例如 Visual Studio,都可以从存储库中消耗此元数据。在我们的测试中,我们将使用 Microsoft SDK 中的虚拟客户端实用程序,例如 WCF Test Client
,因此启动该程序并从存储库添加服务,如下面的图片所示
单击 Run
按钮后,我们将获得该契约的客户端。注意,此客户端实用程序(.NetFX 3.5 版本)存在一些小错误,请忽略它并继续。
此时,我们已准备好运行时测试客户端,但没有 Test 应用程序的物理部署,其中包含 Echo
服务。这是下一步,因此让我们继续,并在应用程序模型中完成所有元数据连接。我希望您的模型设置与上面第一节的描述相同。
Echo 服务的应用程序模型
此步骤的目标是创建一个服务的应用程序模型,该模型根据业务需求分为不同的域。对于我们的 Echo
服务(目前我们只使用一个初始版本 1.0.0.0),我们将创建一个业务域 abc
来托管我们的 Echo
服务。
添加新域
选择 Domains
范围节点,右键单击 Add New Domain
操作,我们将看到以下屏幕截图
填充用户控件属性,如名称:abc
、版本:1.0.0.0
和描述(可选),并选择服务 Echo
版本 1.0.0.0
单击 Finish
按钮,将在存储库中创建一个新域。正如您稍后将看到的,可以根据需求修改域或创建其新版本等。
下一步是创建一个应用程序,使用与域相同的理念。我们需要一个逻辑应用程序来打包我们的业务域,以便在物理进程中对其进行隔离。
添加新应用程序
新的应用程序需要填充一些属性。在我们的示例中,属性如 HostName:Test
、ApplicationName:Test
、MachineName:localhost
、Description(可选)、Version:1.0.0.0
以及一个用于 IIS/WAS Hosting
的复选框。然后我们选择此应用程序中的所有域。在此示例中,您只看到一个域,如 abc
。
单击 Finish
按钮,新的应用程序将存储在存储库中。以后我们可以根据其他需求返回修改它,例如添加新域。
到目前为止,我们在存储库中有一个契约模型和一个包含 Echo
服务模型的应用程序。现在,是时候将这些模型连接在一起了,换句话说,我们需要将物理终结点分配给应用程序(特别是 Echo
服务)。
为服务分配终结点
在 Echo (1.0.0.0) 范围节点中选择 ServiceEndpoints
节点,右键单击并选择 Add New Endpoint 操作。此用户控件窗格将提供一个选项来选择此服务的所有终结点。在我们的示例中,我们可以看到 VirtualAddress
(因为我们决定使用 IIS/WAS 托管)。这实际上是存储库在单击 comboBox Name
时弹出所有终结点的过滤器。下图显示了该选择的结果
单击 Finish
按钮,当选择 config
范围节点时,config
资源将在存储库中更新。
为了完成此过程,我们必须为该应用程序创建一个部署包。注意,创建此包后,模型中的任何更改都不会对此包产生影响,换句话说,如果必须更改模型,则必须刷新特定资源并重新创建包。
创建包
对于 Push
部署选项,应用程序必须创建一个部署包。管理单元内置了仅针对 IIS/WAS(Dublin)的目标的选项。
选择 Test 1.0.0.0
范围节点及其 CreatePackage
操作,以下用户控件将在中央窗格中显示。如果应用程序已创建包并存储在存储库中,我们可以看到其内容。
要创建包(或新的空包),我们可以单击 Create
按钮。我们可以看到有关包、资源、程序集以及此应用程序的网站结构的所有详细信息。
现在,到了神奇的一步——部署到物理目标。我们辛勤工作才走到这一步,所以让我们为存储库完成最后一步,即安装包。
部署包
单击 Install
按钮,将出现以下对话框
您可以看到一些目标属性,请不要更改它们,此版本不支持完整功能,我们只能将示例部署到 localhost
服务器和 Default Web Site
。只需单击 Deploy
按钮,我们的示例就可以进行实际测试了。
测试 Echo 应用程序
如我之前提到的,测试服务使用的是 Microsoft 实用程序 WCF Test Client
。在此步骤中,我们已经设置了调用 GetFullName
服务操作的客户端。下图显示了调用此操作的结果
应用程序中的更改
好的,让我们继续我们简单的示例,向应用程序添加一个新版本的 Echo 服务。我们将在同一域中拥有两个 Echo 服务,而物理终结点没有改变。这种情况以透明的方式映射了新旧客户端之间的实际需求。为了解决这个问题,我们必须添加一个特殊的预/后服务,称为路由器服务。根据消息内容,路由器会将请求/响应转发到适当的目标。
我在服务中介示例中详细描述了这种情况。因此,首先,我们将通过 Modify Domain
操作向域 abc
添加两个额外的 Echo
服务:
单击 Finish
按钮,存储库将更新 abc
域中的此更改,请参见 abc
范围节点的下图
如上图所示,所有服务名称都相同。路由器服务的名称以“@
”字符开头,表示路由功能,用于创建 IIS/WAS 托管的部署包。
现在,我们必须转到契约模型并为
- 新
Echo
服务(版本 1.0.0.1)添加两个新的Endpoints
。在此示例中,我们不更改服务契约,它与IService
契约相同。您可以通过与创建Echo Endpoint
相同的方式完成此任务,唯一的区别是终结点名称和版本 - 使用
IGenericContract
服务操作的路由器服务。此契约可以从程序集ESB.Contracts
导入。注意,此契约是未类型化的,因此我们没有 wsdl 元数据,因此它不由存储库发布。
在此“存储库学习过程”之后,我们可以看到三个(3)Echo 终结点
一旦我们在契约模型中有了这些终结点,我们就可以在应用程序模型中使用它们。基本上,我们需要在 ServiceEndpoints 部分进行以下工具设置
- 将终结点
Echo
分配给 Echo 1.0.0.0 服务 - 将终结点
Echo1001
分配给 Echo 1.0.0.1 服务 - 将终结点
EchoRouter
分配给 Echo 路由器服务
在 ServiceEndpoints
范围节点中设置终结点是直接的,并且得到了契约模型的大力支持。另一方面,ClientEndpoints
有时需要对配置文件中的客户端部分进行一些手动调整。
注意,ClientEndpoints
中的终结点名称必须与 SendActivity
的 EndpointName
值相对应。
好的,现在我们已经完成了契约和应用程序模型中有关 Echo
版本控制的所有必需更改,因此我们必须创建一个新的部署包
如您所见,上图显示了应用程序包的新结构。第一个服务是路由器 - 版本 1.0.0.0 和 1.0.0.1 的消息转发器,位于同一(Test)网站中。
单击 Install
按钮,我们就可以将此版本部署到目标服务器进行测试。因为我们没有更改服务契约,所以我们可以使用我们已创建的客户端。
消费 Echo version 1.0.0.0
服务的后果
消费 Echo version 1.0.0.1
服务的后果
这个例子就到这里。我希望您能对存储库中的可管理服务工具有一个概念。工具过程是智能的,基于工具的能力。本文解决方案存在一些限制,当然,该工具可以根据需要以增量方式添加更多工具操作、验证等。
附录:设计与实现
可管理服务的设计与实现工具在我之前的文章 Contract Model for Manageable Services 中有详细描述。我将描述实现中的一些有趣部分,我想为以下第三方软件致谢,它们帮助我节省了一些实现时间,并使我能够更专注于元数据模型
- Microsoft XmlNotepad 2007 来自 CodePlex
- WizardBase 来自 Codeproject
- DotNetZip Library 来自 Codeplex
- Simple Popup Control 来自 Codeproject
- WorkflowDesignerControl 来自 Windows Workflow Foundation SDK Code Samples
该项目使用了许多技术,如 MMC 3.0、Linq Sql、Linq Xml、.NetFx 3.5 等。当然,没有 Reflector,这项任务将非常困难。要展示所有经过多年设计和实现的部分非常困难,实际上是从 WorkflowServices 在 .NetFx 3.5 技术中引入以来。
但是,这里有一些代码片段
public static void AddServiceToPackage(Package package, ServicePackage service) { if (package == null) throw new ArgumentNullException("package"); if( service == null) throw new ArgumentNullException("service"); foreach (PackageItem item in service) { string filename = string.Concat(service.Path, string.IsNullOrEmpty(item.Name) ? service.Name : item.Name, ".", item.Extension); Uri partUri = PackUriHelper.CreatePartUri(new Uri(filename, UriKind.Relative)); PackagePart packagePart = package.CreatePart(partUri, System.Net.Mime.MediaTypeNames.Text.Xml, CompressionOption.Maximum); if (string.IsNullOrEmpty(item.Body) == false) { using (StreamWriter sw = new StreamWriter(packagePart.GetStream())) { sw.Write(item.Body); sw.Flush(); } } } } public static Package OpenOrCreatePackage(Stream stream) { if (stream == null) stream = new MemoryStream(); return Package.Open(stream, FileMode.OpenOrCreate, FileAccess.ReadWrite); }
以下代码片段展示了 ServicePackage 的声明
public class PackageItem { public string Name { get; set; } public string Extension { get; set; } public string Body { get; set; } } public class ServicePackage : List<PackageItem> { public string Name { get; set; } public string Path { get; set; } }
上述代码片段是创建部署包并将其存储在存储库中的一部分。从存储库收集的元数据被放入特定路径的 ServicePackage
中,然后压缩并存储在存储库应用程序模型中以供以后安装。
另一个代码片段显示了 ApplicationVersionNode
范围节点的一部分,用于使用 Linq Sql 技术从存储库加载它。
internal void Load() { this.Children.Clear(); // repository context RepositoryDataContext repository = ((LocalRepositorySnapIn)this.SnapIn).Repository; var application = repository.Applications.FirstOrDefault(a=>a.id==new Guid(ApplicationId)); if (application != null && application.iisHosting) { this.Children.Add(new ApplicationConfigNode(new Guid(ApplicationId))); } using (TransactionScope tx = new TransactionScope()) { var domains = from d in repository.GroupOfDomains where d.applicationId == new Guid(this.ApplicationId) orderby d.priority select d; foreach (var domain in domains) { var query = repository.AppDomains.FirstOrDefault(e=>e.id==domain.domainId); if (query != null) { DomainVersionNode node = new DomainVersionNode(domain.domainId.ToString()); node.DisplayName = string.Format("{0} ({1})", query.name, query.version); this.Children.Add(node); } } tx.Complete(); } }
可管理服务的完整源代码包含在本文中。
附录:下一步?
本文是该项目的最后一部分,该项目始于 VirtualService for ESB,并在 Contract Model for Manageable Services(一年前)之后。当时 Windows Azure Platform 尚未向公众发布,因此该项目的第一部分已作为 Enterprise Service Bus 的虚拟服务编写,概念是“分布式 BizTalk”。不久之后,我发布了我的项目第一部分,我将逻辑连接的愿景从 ESB 调整为可管理服务,这使我能够管理和中介终结点后的业务流程。我引入了契约模型和应用程序模型这两种模型,并且 Contract Model for Manageable Services 文章详细描述了这种模型及其工具支持。
以虚拟方式管理契约而不拥有其物理终结点一直是该项目的主要关键,我认为当时这是一个很好的决定,因为它在概念上与微软最近两个月前在 CodePlex 上发布的 Managed Services Engine 新版本相匹配。
存储库中的逻辑集中式服务模型(契约 + 应用程序)使我们能够跨更多业务模型(企业应用程序)管理服务,并物理分散到运行时投影。随着 Windows Azure Platform 的即将推出,可管理服务在云中面临挑战,在那里它们可以成为 .Net Service Bus
的一部分。在我看来,这是从 ESB 到具有服务总线功能的内置云基础设施的一个巨大飞跃。
“下一步”的第二个愿景是对存储库的一个巨大挑战。存储库代表了连接性和服务的知识库,在学习过程中(工具化),这些知识根据需求不断增长。将存储库推送到云并连接到 .Net Service Bus
,我们也可以考虑不仅在终结点级别(mex)交换元数据,还在服务模型或更高级别交换元数据。
为了继续这一愿景,存储库中以元数据驱动的可管理服务面临着一个重大挑战。如我之前提到的,存储库通过设计时使用某些工具的手动过程来学习。添加在运行时调优存储库中模型的功能将开启事件驱动架构中的一个全新挑战,其中逻辑模型可以由服务本身进行管理。
好的,我将停止我对云的幻想,回到现实,简要谈谈“下一步”。答案是即将推出的基于 WCF/WF 4.0 和 xaml
堆栈的新微软技术,因此这就是可管理服务的“下一步”。这一挑战将允许将可管理服务定位在任何地方,包括云。当然,我们必须等待 .NetFx 4.0 的发布(或 beta)版本。
可管理服务的另一个功能是逐步开发更多工具支持功能,例如 XsltMapper Designer、XsltProbe、Model Validation、Service Simulator and Animator、Management for Assemblies、RepositoryAdmin 等。
好的,您可以看到,这个领域有很多可管理服务的挑战。另外,请记住,本文描述的可管理服务项目是在当前 .NetFx 3.5 版本上实现的,重点关注即将到来的模型/元数据驱动架构及其非生产版本。
结论
总之,本文描述了可管理服务的工具支持,该服务在存储库中逻辑集中,并在物理上分散到目标服务器以在应用程序域中进行投影。如果您一直跟我走到现在,您应该对由元数据驱动的服务支持的应用程序有了很好的理解。我希望您喜欢它。
参考文献