Ulrich Roxburgh
Microsoft Corporation
2001 年 2 月
简介:本文探讨 Microsoft BizTalk Orchestration Services 中所提供的事务支持,并考察如何使用事务和异常处理支持来处理计划中可能出现的错误。另外还探讨如何调试计划以及计划中的各个组件。本文的对象是借助 BizTalk Orchestration Services 实施长期业务过程的设计人员和开发人员。
目录
简介
Orchestration 和事务:概略介绍
计划中的错误处理
调试计划
简介
借助 Microsoft® BizTalk™ Orchestration Designer,用户能够设计长期运行的业务过程,为构成这些过程的单个操作指定一个实施方案,并将该信息编译成一个可执行的 XML 表示,即 XLANG 计划。所创建的计划以一种松散耦合且可伸缩的方式跨时间、机构和应用环境分布。但是,由于这些过程具有高度分散的性质,在执行计划的过程中发生错误和异常的可能性比短期业务过程要大。
Orchestration Designer 提供一个可视的设计和开发环境,将正在开发的业务过程从该过程的实施方案分离开。借助该工具,开发人员可以为构成这些过程的单个操作指定一个实施方案,并可以将该信息编译成一个可执行的 XML 表示。BizTalk Orchestration Designer 提供了一套丰富的编程构造,其中包括事务和异常处理语义。
事务用于将操作集合组成一个单独的逻辑工作单元,以确保操作在组内进行的所有工作均得以完成,否则所有工作就被撤销。将操作归组提供了最水准的结构化和可靠性。这不但支持短期事务,还支持横跨多个长期业务过程的事务,以及定时事务。
异常处理提供了更多的逻辑,以在发生处理错误时,撤销事务的结果,或提供另一套要采取的操作。异常处理包括“按失败情况”和“补偿”处理,用于支持长期业务过程的错误处理。
最后,本文探讨对 BizTalk Server Orchestration Services 和 BizTalk Messaging Services 进行调试和纠错的各种方法。
Orchestration 和事务:概略介绍
何为事务?
人类从很早以前就从事各种事务(交易)。在一个典型情形下,一个买主和一个卖主为一些货物商谈合适的价格。假设达成一致,买主将钱款交给卖主,以换取货物。这里最重要的一点就是,整个事务或者继续,或者全部终止。如果卖主得到钱款而买主得到货物,则皆大欢喜。如果卖主没有得到钱款,他就不给买主货物,整个结局依旧可以接受。但是,如果买主付了钱款而没有得到货物,则买主就不高兴。 同样,如果卖主移交货物而没有得到钱款,则卖主就不高兴。
如果买主和卖主彼此不相信对方会履行各自的买卖承诺,则可以求助于双方信任的某个中介服务。为执行事务,买方将钱款交给中间人,而卖方将货物交给中间人。中间人然后就确保卖方收到钱款且买方收到货物。
这些与通过计算机进行事务有何关系?设想一下通过银行业务申请而进行款项汇转的过程:款项被从一个帐户提出(数据库中某个记录的值减少)并存入另一帐户(另一数据库中某个记录的值增加)。这一系列操作进行起来必须象一蹴而就(即就象单独一次操作)。这被称为一次事务(英语中的这个名词来自 "transformation action" [转换操作])。一次事务是一次操作或一系列的操作,将一个系统从一个一贯状态转换为另一一贯状态。
事务遵循一整套属性,称作 ACID 属性:
- 不可分割性:一次事务代表一个不可分的工作单元。一次事务中的所有修改或者全部得到执行,或者没有任何修改得到执行。
- 一致性:一旦得以完成,事务就必须保持数据在系统内的完整性。如果事务对某一数据库进行一项数据修改,而该数据库在事务开始之前在内部是一致的,则在事务完成时,该数据库必须依旧具有内部一致性。确保这一属性,大体上是应用程序开发人员的职责。
- 隔离性:并行事务所进行的修改必须与其它并行事务所进行的修改隔离开来。并行运行的隔离开来的事务将进行维持内部数据库一致性的修改,完全就象是各事务是串行运行的一样。
- 持久性:在一次事务得以完成后,所有的修改就永久性地存在于系统中了。即使发生系统故障,各个修改依旧存在。
就象对市场中的事务(交易)进行的模拟那样,通常事务中所涉及的各方,均对另一方没有控制(例如,在涉及对两个分开的数据库进行更新的事务中),因而两方中的任一方均无法保证过程的不可分割性。就象市场中的模拟情形一样,解决这一问题的方法就是引进一个第三方或中介,以确保或者两个操作均发生,或者两个操作均不发生。
在 Microsoft® Windows NT® 4 和 Microsoft Windows® 2000 上,该中介称为 Microsoft Distributed Transaction Coordinator (分布式业务协调器,MSDTC)。MSDTC 最初与 Microsoft SQL Server™ 6 一起发布,提供了一个基于对象的编程模型,用于创建、消灭、管理和监视各个事务。MSDTC 与某些帮助服务(也称作资源管理器)配合使用,以确保事务的 ACID 属性得到维持。
资源管理器拥有受到事务影响的对象,并负责持久存储资源对象。一项资源必须让一个资源管理器参与与 Distributed Transaction Coordinator 的事务。另外请注意,Distributed Transaction Coordinator 和资源管理器可以跨网络上的多个节点分布。
要协调一项事务的各个操作,并维持 ACID 属性,Distributed Transaction Coordinator 和资源管理器使用一种协议,称作两步完成协议。两步完成协议的算法是一系列复杂的操作,其复杂性随资源数目的增加(从而资源管理器的数目也增加)而增加。协议的最为重要的特性就是,在两步完成过程中,所要更新的记录被锁定。对数据库记录进行的这一锁定一直保持到事务被放弃或完成。这一因素对长期业务过程中的事务有着重要意义。
尽管 Distributed Transaction Coordinator 极大地使程序员能够更加轻松地将事务用于其应用环境,这一模型仍有一个很大的弱点。事务通常是在组件中实施的(初始、完成或放弃)。当构成一个完整事务的操作分散在多个组件时(即,在一个组件中初始,而在另一个组件中完成或放弃),就很难将这些组件重新用于实施由不同的组件组合构成的新的事务。
在图中,Transaction 1(事务 1)是在 Customer 组件中初始的,而是在 Invoice 组件中完成的。还有一个 Agent(代理) 组件,用于初始另一个事务。现在,如果对于某个业务过程,创建了另一个由 Customer 和 Agent 构成的事务--即 Transaction 2(事务 2),则无法轻松创建该事务,因为两个组件均初始一个事务,但两个事务又均不完成这些事务(完成或放弃事务)。
在 1997 年,Microsoft 发行了 Microsoft Transaction Server (并发行了 MSDTC 的一个新版本)。 该产品革命性地为事务编程提供了一个声明模型。现在,程序员不是在组件内为事务语义编程(即在本质上为事务构成进行硬件编码),而是将组件作为一个整体而为其声明事务属性,并从独立的 COM 组件编写事务,从而实施整套事务。
这一新的模型使得程序员能够以一种简单得多的方式编写其事务,从而极大地增加了事务性组件的重复利用。COM 组件成为业务事务的构件。Microsoft Transaction Server 所提供的所有服务现已转移到 Windows 2000,且作为 Windows 2000 下面的 COM+ 业务得到显著增强。
COM+ 提供五个级别的事务性支持:
- 已禁用(Disabled):该选择指定组件忽视 COM 事务管理。
- 不支持(Not Supported):该选择指定组件不参与事务,或者传播其它组件的事务。
- 支持(Supported):该选择指定,如果当前有一个事务正在运行,则组件将被包含在事务中。但是,组件将不初始一项事务。
- 必需(Required):该选择指定,如果当前有一个事务正在运行,则组件将被包含在事务中。如果没有任何事务正在运行,则将为该组件创建一个新的事务。
- 需要新建(Requires New):该选择指定总是为该组件创建一个新的事务。
BizTalk Orchestration 借助已有的各项 COM+ 服务,为开发复杂的业务过程提供一个精密的图形编程范例,其中带有事务编程和异常处理语义,提供与 COM+ 服务同一类的革命性的事务编程语义。
在 BizTalk Orchestration 中有多个级别的事务性支持。第一个级别的支持来自将整个计划当作一个 COM+ 事务性组件。而且,可以为计划中的一组操作指定事务性语义,具体方法是将这些操作包在一个事务形状中。这使得计划能够支持短期的 DTC 风格的事务(事务由 Distributed Transaction Coordinator 进行管理,并使用底层的 COM+ 服务),并能够支持长期事务(即运行超过较长一段时间的事务),以及定时事务(即可能会在较长一段时间后发生超时的事务)。计划还支持事务补偿和异常处理语义。
作为一个事务参与者的业务过程图
BizTalk Orchestration Services 所提供的第一个级别的事务支持使得整个计划被当作一个事务组件。计划的事务性支持是声明设定的,其具体方式类似于为 COM+ 组件声明事务性支持。计划然后就由一个 COM+ 组件初始,该组件可能已在一个事务性上下文中运行,也可能不是这样。在本质上,计划用于实施该事务性 COM+ 组件。
可以通过在计划开始时打开 Begin 形状的“属性”对话框,对计划的事务模型进行设置。其默认设置为“将事务包含在计划中”。要将整个计划当作一个事务性组件,请选择“将 XLANG 计划当作一个 COM+ 组件”。还可以设置计划的事务活性度:
- 如果 XLANG 计划不支持事务,请选择 Not Supported。
- 如果 XLANG 计划参与一个 COM+ 事务,请选择 Supports 。
- 如果 XLANG Scheduler Engine 与 COM+ 协作以确保计划所创建的所有 COM 组件均为事务性,请选择 Requires。
- 如果 XLANG 计划必须参与一个新的事务,请选择 Requires New。如果该设置被启用,COM+ 服务就自动初始一个新的与调用者的事务截然不同的事务。
借助这一机制,编制引擎有效地提供了单一 COM+ 组件内所实施的业务过程自动化功能。即,整个计划作为单独一个 COM+ 组件工作,且该 COM+ 组件能够支持上面所描述的各项事务。请注意,当将整个计划用作组件时,该计划自身就不能包含任何事务(计划中可以包含事务形状,但计划不会进行编译),且在计划内使用并行的执行流有一些限制—当在计划中使用 Fork 形状时,所有的事务性操作就必须在一个执行流中发生。
请注意,将计划用作一个自包含的事务性组件的机制依赖底层的 COM+ 服务来管理事务。如果事务被放弃,则所实施的与事务性组件有关的操作会被还原。事务性组件可以是 COM+ 组件、脚本组件或事务性 Microsoft 消息队列。
在本例中,计划已被配置为“将 XLANG 计划当作一个 COM+ 组件”(在 Begin 形状的“属性”对话框)。 其还被配置为要求一项事务。所实施的计划(未显示)从一个事务性消息队列(接收队列)读取一条消息,并将该消息写到另一个事务性消息队列(发送队列)。
如果该计划是通过从一个 COM+ 组件对其进行实例化而执行的,而该 COM+ 组件也被配置为要求一项事务,则该事务得以完成(组件调用 SetCommit),则将从接收队列读取一条消息并将其写入发送队列。但是,如果 COM+ 组件由于某个原因而放弃事务(调用 SetAbort),则从接收队列读取的消息会被重新放回队列,且没有任何消息被写入发送队列。
计划内事务的类型
如果某一计划的事务模型设为“将事务包含在 XLANG Schedule 中”(默认设置),则计划可以包含事务形状。要将事务添加到计划,从流程图调色板拖动 Transaction 形状,将其放置好,使其包含所有将要参与事务的操作。
还可以将一项或多项形状嵌套在一个外层事务形状内。短期事务将一系列操作聚集在其界限内,但其无法嵌套另一个事务。但是,长期事务和定时事务可以用于聚集任意的操作组合—短期事务、长期事务或定时事务。但是请注意,事务嵌套不得深过两层。
可以对事务的属性进行设置,方法是单击事务形状的“属性”,显示“事务属性”对话框。可以重命名事务,以及设置事务类型(定时、短期或长期)或其它事务属性。
另外,在适合的情况下,可以向计划添加“按失败情况”代码或“补偿”代码。“按失败情况” 代码在计划上创建一个新页(“按事务的失败情况”页),用于设计一个替换业务过程,用于对所选事务发生失败进行处理。该选项可用于所有的事务(请参见本文稍后部分的“事务失败情况的处理”)。“补偿”代码也在事务中创建一个新页(“事务补偿”页),用于设计一个替换业务过程,以撤销业已完成的某个嵌套事务中所执行的逻辑工作单元。该选项仅可用于嵌套事务(请参见本文稍后部分的“事务补偿处理”)。
可以设置的其它事务属性有:
- 超时:该属性设置一个时间,在事务运行这个时间之后就自动放弃或进行重试。 无法为长期事务设置该属性。
- 重试计数:该属性确定短期事务内某个过程在没有完成的情况下,将要运行的次数。对于每次重试,应用程序的状态均被重置为事务内过程的起始点。该选项仅用于短期事务。
- 后退时间:该属性确定每次尝试事务之间的时间间隔。后退时间与重试计数值一起,确定在进行下一次事务尝试之前需要等候的时长。后退值是指数。 2 秒钟的后退值导致每次重试之间有 2、4、8、16 等秒数的间隔。公式为 B**R (B 的 R 次方),其中 B=后退时间,而 R=当前的重试计数。如果某一具体事务重试尝试的的后退时间大于 180 秒,则 XLANG 计划实例会被立刻“脱水”放入 持久数据库。该选项仅用于短期事务。
- 隔离度:隔离度确定并行事务内的数据可以互相访问的程度。该选项仅用于短期事务。可供选择的包括:
- 系列化,用于防止并行事务进行数据修改,一直到所选事务完成。这是四个隔离度中限制性最强的。
- 读取未完成者,在所选事务完成之前,允许并行事务进行数据修改。这是四个隔离度中限制性最弱的。
- 读取已完成者,用于防止所选事务访问并行事务中的数据修改,一直到其完成。该选项是 Microsoft SQL Server 的默认设置。
- 可重复读取,要求读锁定,一直到所选事务完成。
- 系列化,用于防止并行事务进行数据修改,一直到所选事务完成。这是四个隔离度中限制性最强的。
短期(DTC 风格)事务
在计划上设置一个事务形状时,其默认为短期事务(事务框被填充为灰色)。该事务类型依赖于来自 COM+ 和 MSDTC 的底层事务支持。 短期事务允许从若干的离散和独立单元创建一个原子性(单一、不可分割)的工作单元。
尽管可以在“属性”对话框中设置事务的属性,以及聚集在事务形状内的各个操作所定义的事务界限,但短期事务依赖于为连接到该操作的实施端口所设置的事务属性,以及组件的事务属性,或者该实施端口所参照的脚本。
具体而言,这意味着事务形状所包含的针对操作的实施方案应当是支持事务的 COM+ 组件,标记为事务性的脚本,或者对事务性消息队列进行的读写,这样才有可能成功放弃这些操作。如果事务形状包含一个连接到一个并不支持事务的实施端口的操作的话,则在放弃事务时,该 COM+ 组件、脚本或队列所进行的工作就不会被还原。尽管如此,非事务性组件依旧可以用于实施作为事务一部分的操作。
计划展示短期事务中所包含的三个操作。在本计划的实施方案中(未显示),第一个操作是一条消息到达一个事务性消息队列(接收队列)。一个 COM+ 组件然后就被实例化,并对该组件调用一个方法。方法显示一个对话框,让用户选择完成或放弃事务(在方法内调用 COM+ 组件上 SetCommit 或 SetAbort)。最后一个操作获得原始消息并将其写入另一个事务性消息队列(发送队列)。在执行这一计划时,如果用户选择调用 SetCommit,将从接收队列读取消息,并将其放入发送队列。但是,如果用户选择调用 SetAbort,则消息依旧留在接收队列中。
最后需要注意的一点是,对于该计划的每个实例,会在短期事务启动时实例化 Query Abort 组件的一个新的实例,且在事务终止(放弃或完成)时消灭该实例。该实时激活模型与首次随 Microsoft Transaction Server 一起交付的实时激活模型相同。组件所维持的任何状态均丢失。
长期事务
在考察执行时间极限可能不确定的业务过程时,不可使用传统的短期事务。这是因为每个短期事务保持数据库锁和资源。鉴于在某一特定时间,计算机上可能有成千上万的业务过程在运行,所保持的资源的数目想必不切实际。因此事务类型被设为长期。除“隔离性”之外,长期事务具有前面所述的所有 ACID 属性。
隔离性的意思是,事务以外没有任何东西能够看到(更别说更新)事务内正在使用的数据。隔离的原因在于,事务的结果是未知的,一直到事务完成或放弃,因为当前多次数据可能有效,也可能无效。鉴于数据可能无效,不允许别的任何东西访问数据,以防被误用。隔离性是短期事务的一个属性(ACID 属性之一),是通过锁定数据库中的记录来实施的。
在分布式长期事务中,不能超长时间锁定数据库中的数据,也不能锁定跨机构分布的数据库中的数据(想象一下怎么能够说服另一机构的数据库管理员让您锁定他的数据库中的记录!)长期事务专门用于将操作集合聚集在更细的原子性工作单元中,这些工作单元能够跨时间、机构和应用环境存在。在长期事务中,其它事务能够看到正被该事务使用的数据。当然,长期事务也可以由本身为短期事务的操作构成(短期事务可以嵌套在长期事务中)。
例如,请想象一下在收到一个购买请求时初始一个业务过程的情形。请求被记录到数据库,然后被发给请求的审批者。这可能需要过一段时间(数周!)之后才能收到批准回应,此时回应也被记录到数据库,且购买定单被发给供应商。接收初始请求(并进行记录)和接收回应(并进行记录)各自均由多个操作构成(接收和记录)。
在这种情形下,短期事务被用于将相关的操作聚集为单独一个原子性事务(接收一条消息并将其连接到数据库)。但是,无法将接收购买请求消息和接收批准消息聚集到单独一个短期事务中,因为这会将数据库中的记录行锁定不定期的时间。想象一下 5000 个用户同时进行这一操作会怎样?因此,使用一个长期事务来聚集两个短期事务,而这两个短期事务可以分隔较长的一段时间。
现在想象一下执行这一业务过程时会怎样?首先,在一个短期事务中,接收购买请求并更新数据库。 如果出现任何问题,事务会被放弃,所有更改会被撤销;否则,事务得以完成。然后计划就等候批准消息的到来。在消息到来时,数据库再次得到事务性更新。
如果出现任何问题,“发至供应商”事务就被自动放弃。但是,无法放弃“接收 PO”事务,因为已被完成。这样,第一个事务必须提供一些代码,用于撤销其已进行的操作,以防事务已被完成后需要对其进行放弃。这称作补偿事务(请参见本文稍后部分的“事务补偿处理”)。在这一情形下,如果某些条件促使“发至供应商”事务放弃,则资源管理器和和 MSDTC 将负责撤销“发至供应商”事务所进行的所有工作。“接收 PO”事务所提供的“补偿”代码将撤销该事务所进行的已完成的更改。
将多个短期事务聚集(合成)成一个长期事务,在总体上是由长期事务控制的。通常,长期事务会包含多个嵌套的短期事务。根据 XLANG 计划图所描述的业务过程的各项需求的不同,整个业务过程(Begin 形状和 End 形状除外)均可以包含在长期事务内,如此处所示。
定时事务
定时事务用于触发放弃长期事务,如果其在所指定的时间内没有完成的话。长期事务不使用属性页上的超时属性。提前确定某个业务过程可能花的时间长度,通常十分困难。但是,对业务过程中某个具体操作可能花的时间进行合理估计是可能的,例如消息的到来。
因此,一个定时事务可以用于聚集短期事务,并在指定的一段时间内等候消息的到来。如果消息及时到来,定时事务完成;否则,定时事务放弃,并促使短期事务执行其“补偿”代码。
在示例中,短期事务被用于“汇款”。该事务聚集“取款”和“初始电汇”这两个操作。当“初始电汇”操作完成时,业务过程序列就流出嵌套事务。此时,嵌套事务完成;钱款被从银行帐户中提出,发往一个目的地。此时,业务过程序列流到外层事务中的“等待确认”操作。
在这一情形下,“电汇”事务被配置为一个定时事务。如果发送者未能在所指定的时间量内收到钱款收讫的确认,则外层事务将放弃。这一情况发生时,业务过程序列流到针对嵌套事务的“汇款补偿”页,然后流到针对外层事务的“按电汇的失败情况”页(请参见本文稍后部分的“事务补偿处理”)。
定时事务也可以通过在计划中有两个执行流来建模,其中一个流等待消息到来,而另一个流有一个计时器,会在指定的时间后发生超时。无论哪个事件最先发生(消息到来或计时器超时),事务均得以完成(即分别导致完成或放弃)。但是,以这一方式为一个定时业务过程建模会对计划“自我脱水”的能力施加限制,并且,定时事务总是要方便得多。
实施端口的事务属性
正如前面所示,计划中所使用的操作形状和这些形状在实施端口中的实施有区别。操作的事务属性依赖于底层实施方案的事务性属性。这意味着,仅有借助事务性组件实施的操作才将实际参与事务。
具体而言,这意味着事务形状所包含的针对操作的实施方案必须是支持事务的 COM+ 组件,标记为事务性的脚本,或者对事务性消息队列进行的读写,这样才有可能成功放弃这些操作。在将 COM+ 或脚本组件绑定连接到端口实施时,可以以为 COM+ 应用程序设置事务支持相同的方式设置对该实施端口的事务支持(禁用、不支持、支持、要求、要求新建)。
在带有非事务性实施端口的事务中实施操作是完全可接受的,但一旦发生事务放弃,则这些实施所进行的所有更改均不会还原。在任何情况下,事务在实施端口均得不到支持,除非这些端口与由资源管理器管理的资源打交道,而这些资源管理器又与 Distributed Transaction Coordinator 协作;这实际上意味着大多数的常见数据库和 Microsoft Message Queuing。 非事务性情形是借助“按失败情况”处理过程进行处理的(请参见本文稍后部分的“事务失败情况的处理”)。
可以在端口实施中设置的最后一个事务属性就是在该组件或脚本的处理过程中发生错误时放弃事务的能力。借助这一机制,可以通过从 COM+ 对象或脚本返回一个 COM+ 错误,从而可以放弃当前的事务。
什么导致了事务放弃?
事务怎么会被放弃?事务通常会执行到过程流出事务界限(事务完成和结束)或发生放弃。发生放弃有若干的原因:
- 在过程流中遇到 Abort 形状。
- 有来自 COM+ 组件的一个故障返回代码 (HRESULT),该代码指定在端口绑定中导致放弃。
- 任意的绑定技术均可以在系统级引入一个故障事件,放弃事务。例如,Message Queuing 可能会没能将一条消息放入一个队列。
- XLANG Scheduler Engine (负责执行计划实例的 COM+ 应用程序)遇到一个错误,该错误导致其放弃某一实例内的事务。例如,可能有一个 DTC 错误。
- 暂停一项计划可能会要求该计划内的所有事务放弃。
- 事务属性内发生事务超时。
发生放弃时,事务可能会从开始重试,这要取决于事务组的 Retry count 属性。如果在事务重新尝试超过所指定的次数之后,依旧失败,则会调用“按失败情况”业务过程。该“按失败情况”代码提供了一个用于处理事务失败的结构化场所。
计划中的错误处理
正如前一节所示,短期事务可以用于为计划中的某些操作提供自动还原或和恢复功能。但是,许多操作无法以事务性方式实施,因而要对错误条件进行处理,就必须使用其它形式的错误处理,诸如异常处理和补偿性事务。本节着重介绍如何将错误处理功能建入计划。
错误起因
首先考查一下计划中错误的可能起因,在 XLANG Scheduler Engine 正在运行的过程中,可能会发生三个级别的错误。 其严重性从从重到轻,分别为:
- 导致故障的错误。无法由 XLANG Scheduler Engine 捕获的系统错误可以导致引擎连同正在同一 COM+ 应用程序中运行的所有计划实例发生故障。此类故障的最为可能的起因就是有一个过程内、编制不良的 COM+ 组件。此类组件在被放入过程之前,应当很好地在过程外进行测试。
- 引起异常终止的错误,其中包括失去同步的 COM+ 组件,并不存在的消息队列,或并不存在的消息收发通道。
- 可以捕获的错误。
自然,在处理计划的过程中,需要相应地对错误进行检测和处理。可以在 XLANG 计划内捕获的错误包括返回各种故障 HRESULT 的 COM 组件(这适用于 COM+ 组件或脚本)和各种在册服务所导致的事务放弃(诸如失去到数据库的连接)。
处理错误
正如前一节所示,XLANG Scheduler Engine 可以捕获应用程序和系统错误。XLANG 计划可以设计为在运行时对错误作出反应,这或者通过某一决策规则来显式测寻某个错误结果,或者通过事务故障处理功能。
为使用到显式测寻错误结果的逻辑分支功能,要对调用 COM+ 组件后的返回值进行测试。该值存储在来自 COM 组件的 _out message 的 __Status__ 字段中(计划中的所有组件均是依据消息实施的;在 COM+ 组件的情形下,这意味着消息被传入到组件,然后另一个消息被从组件传出)。
要实施这一操作,紧接在需要测试其结果的操作之后,添加 Decision 形状,从而添加一条规则,以测试 COM 组件的输出(_out.__Status__ >= 0,其中负的 HRESULT 指示失败而正的 HRESULT 指示成功)。 还可以测寻具体的故障代码,如果适用的话。这些代码是在头文件 Winerror.h 中定义的。
错误也可以使用故障处理功能来进行处理。如果操作被包含在事务形状内,而操作是作为一个 COM+ 组件或脚本实施的,且该组件或脚本放弃事务,则所有参与事务的组件所进行的工作均将被撤销。 如果触发放弃的组件并非事务性的,则需要以其它某个方式触发事务放弃。在 COM Component Bind Wizard 内设置错误处理功能,以在方法返回一个故障 HRESULT 时放弃事务。
该选项仅在使用该端口的通讯操作是在事务的过程流内时才有效。在为 COM+ 组件或脚本进行如此设置,并返回了一个不良的 HRESULT 时,当前正在运行的任何事务均会被放弃。在计划中也可以达到同一功能,具体方法是借助 Decision 形状测寻不良 HRESULT,如果返回了不良 HRESULT,就执行 Abort 形状(但 COM Component Bind Wizard 中的错误处理功能要方便得多)。
借助 Message Queuing 或 BizTalk Messaging 实施技术进行处理故障,仅限于事务故障过程。事务性支持是在 Message Queuing Binding Wizard 中指定的,即指示该队列需要事务(对于 BizTalk Messaging,该操作是自动进行的)。 请注意,Message Queuing 的发送操作成功返回,指示消息已被成功地放到队列上,但并不指示消息已被递交。
事务失败情况的处理
将使用短期事务的单个操作聚集到更加细小的业务过程,很显示是防止计划出错的一个十分有效的机制。但是,对于长期的业务过程,则必须使用若干的其它机制来制定能够对错误进行适当处理的计划。
对于短期事务,事务的界限是通过 Transaction 形状以声明方式设置的,然后这些事务是通过调用事务性组件内的 SetAbort 或让组件返回一个可捕捉的不良 HRESULT 来放弃的。如果事务内的操作被绑定到事务性资源,则 Distributed Transaction Coordinator 将负责还原事务内所有在册操作。已进行的任何工作然后均就被完全撤销。
但是,在许多情况下,传统的短期事务不是不适当,就是无法完成所需的操作。这样,可以将“按失败情况”处理功能用于向计划添加另外的错误处理语义。“按失败情况”处理功能是作为计划内的独特而独立的流来实施的,即是在 BizTalk Orchestration Designer 中独立的处理页上实施的。在设定 Transaction 形状的各种属性时,业务过程设计人员可以选择添加用于“按失败情况”处理功能的代码。这就使得另一页“按事务的失败情况”被添加到计划。业务过程设计人员可以在此处添加另外的逻辑功能,用于对事务失败进行处理。如果事务放弃(在事务已放弃,且所有的事务性组件均已撤销其工作之后),将调用该代码。
现在,当短期事务放弃时,任何绑定到非事务性资源(例如发送电子邮件)均不会还原。可以将更多的操作添加到“按事务的失败情况”处理页,以撤销这些非事务性的操作(例如发送另一封电子邮件,声明第一封电子邮件应当被忽视)。
当然,“按失败情况”代码几乎可以作任何事情,而不必局限于撤销事务内所聚集的操作。与撤销非事务性操作一样,在事务放弃时,可能还需要进行其它工作。在一个典型的业务过程中,放弃事务并清理已进行的工作很少就到此为止。至少,可能需要对事务故障进行记录,但通常还需要进行其它操作,诸如让用户知晓事务的结果。一旦发生事务故障时,又一次可以将“按失败情况”处理功能用于实施这些操作。
“按失败情况”处理功能还可以用于长期事务或定时事务。例如,如果一个定时事务被设置为等待消息收据,则“按失败情况”处理功能可以用于在消息没能到来时,对相应的用户进行警示。
还有一种与“按失败情况”处理功能有微妙联系的情形,即任何在事务后面发生的操作,均无法确定该事务是完成了还是放弃了,除非通过消息将该信息传递给这些操作。例如,请考虑一个带有若干操作的计划,其中某些操作被聚集到事务中。一条消息(可能是一个 XML 文档)从头至尾穿过计划。
如果事务完成,则一切均运行正常,且消息被操作更新(操作所进行的工作得以完成)。但是,假设其中一个操作放弃了事务。在这种情况下,对消息进行的所有更改均将被还原。事务后面的操作将无法弄清事务是完成了还是放弃了(未传递该信息),且根本不知道哪些操作成功地处理了数据,哪个操作失败了,因为对消息进行的所有更改均被还原。
“按事务的失败情况”页又是实施这一方案的最佳方式。“按失败情况”代码将在事务放弃以后执行,而且重要的是,对消息进行的所有更改,均可以提供给“按失败情况”代码。另外,“按失败情况”代码可以在消息中设定字段,以指示已发生了事务放弃,以便后续操作可以采取相应的操作。
事务补偿处理功能
“按失败情况”处理功能是在单一事务中工作的。对于长期的业务过程和嵌套的事务(正如前面讨论过的,事务可以嵌套在长期运行或定时事务内),事务放弃处理功能变得更加复杂。
示例展示的是定时事务“电汇”,其中聚集了短期事务“汇款”。如果运行本计划,“汇款”事务将行(且大概会完成)。计划然后就等待指示电汇正确发生的确认。
如果确认没有在所指定的时间(为定时事务设置的超时时间)内到来,则定时事务 (“电汇”) 将放弃。但是无法放弃内层的 (“汇款”),因为其已经完成。即使为该事务提供了“按失败情况”代码,也无法调用它,因为内层事务尚未失败。
这一情形是通过为内层事务提供“补偿”操作来处理的。在嵌套事务的“事务属性”对话框中,可以添加“补偿”处理代码,从而 (就象“按失败情况”处理功能一样)导致将另一页,“事务补偿”,添加到计划。 可以在本页中添加代码,以补偿(业已完成的)内层事务。
借助嵌套事务,让多个“事务补偿”和“按事务的失败情况”处理页面存在于计划中,以及执行其中一个以上的操作来进行所需的错误处理,是完全可行的。 在上一例中,假设事务“汇款”具有“按事务的失败情况”和“事务补偿”这两页,且定时事务“电汇”具有“按事务的失败情况”页面。
本计划有可能发生两种失败情形。第一种是“汇款”事务放弃,因而执行“汇款”事务的“按失败情况”处理功能。通常,“电汇”事务的“按失败情况”处理功能不会执行,因为内层事务的结果并不影响外层事务的结果。在这种情况下,因为将不发送任何确认,外层的定时事务最终也将失败,从而将调用“电汇”的“按失败情况”代码。第二种情形就是“汇款”将完成,但在超时时间内没有收到确认消息,从而导致定时事务放弃。在这种情况下,将首先执行“汇款”的“补偿”处理功能,然后是“电汇”的“按失败情况”处理功能。
调试计划
与传统的开发系统一样,现在提供可视化调试实用程序已司空见惯,正如 Microsoft Visual Basic® 和 Microsoft Visual C++® 中的那些调试工具。Microsoft BizTalk™ Server 没有为编制计划提供图形调试实用程序。但是请记住,编制计划所代表的是一种不同于传统短期同步过程的可执行过程,因而单有传统的调试模型是不够的。
当用 BizTalk Orchestration Designer 设计一个编制计划时,计划图纸实际上是一张业务过程图。要表示一个过程,要适用三种技巧:
- 操作,这总是发送消息或接收消息之一。
- 消息,即所发送或接收的消息。
- 端口,向其发送或从其接收消息的地方。
另外,因为计划代表一个过程,所以就有从一个操作到另一个操作的编序概念。在编译然后执行编制计划时,通常会初始计划的多个实例,作为单个的(长期的)可执行过程。
要调试这些可执行过程,跟踪和常规调试相结合证明是有效的。对于调试计划的编序(从一个操作流到另一个操作),跟踪是有用的。对于调试单个操作的实施方案,可以运用传统的调试机制。将这两种手段结合起来,就可以最为有效地对计划进行调试。
跟踪计划
在运行计划时,计划是在 XLANG Scheduler Engine 的控制之下执行的,即它是一个 COM+ 应用程序。当安装 BizTalk Server 时,就创建了计划程序的一个单独的实例,名为 XLANG Scheduler (默认)。还可以创建包含 XLANG 计划的定制的 COM+ 应用程序。
这些 COM+ 应用程序(默认或定制)在执行计划时,会生成各种各样的可以捕捉和显示的事件。BizTalk Server 提供了一个工具,称作 XLANG Event Monitor,用于捕捉和显示这些事件。XLANG Event Monitor 可以订阅任意数量的分布式计算机上的主机应用程序所发布的事件,并可以存储这些事件以供日后分析。
XLANG Event Monitor 在启动时,就预订从本地计算机上的所有 XLANG 计划主机应用程序接收事件。主窗口展示主持 XLANG 计划的所有的 COM+ 应用程序,并且针对每个主机 COM+ 应用程序,展示当前正在运行或已完成的所有的计划实例,其编码方式如下:
- 绿点:代表一个正在运行的 XLANG 计划。
- 黑点:代表一个已成功完成的 XLANG 计划。
- 红点:代表一个出错完成的 XLANG 计划。
- 蓝色雪花:代表一个已“脱水”的 XLANG 计划。
- 蓝线:代表一个已被挂起(或暂停)的 XLANG 计划。计划停在该状态中,直到其被恢复或终止。
附加的每个实例均有一个针对所列实例的独特的标识符(实例 GUID)。 所列出的正在运行的计划实例,均可以从 XLANG Event Monitor 加以暂停或终止。
XLANG Event Monitor 还可以用于启动计划的一个新的实例,即选择要主持计划实例的 COM+ 应用程序,并选择相应的计划文件(.skx 文件)。
具体计划实例的所有事件,均可以通过双击 XLANG Event Monitor 中的计划实例来查看。 还可以对所展示的事件进行筛选,从而仅显示特定类别的事件,诸如事务或错误。一旦事件被捕获,就可以将其保存到磁盘,并在日后重新加载、显示。
调试计划中的组件
尽管计划本身不法加载到可视环境中进行调试,但可以对这些计划中的 COM+ 组件进行调试。这种作法与调试从客户端应用程序调用的标准 COM+ 组件完全相同(因为事实上计划是作为一个 COM+ 应用程序来实施的,后者实例化并调用这些定制的 COM+ 对象)。
要调试一个 Visual Basic 组件,象以往那样将工程载入 Visual Basic 并进行建立。请注意,必须将组件编译为“编译为本机代码”和“创建符号化调试信息”。在调试过程中,“无优化”复选框也应当被选中。然后可以设置断点,继而从 Visual Basic 集成开发环境运行组件。组件被执行之后,会正常运行,一直到其试图实例化组件并执行该组件上的一个方法。此时,会在所设置的断点处停止执行。此时可以象平常一样对组件进行调试。
注意:如果 XLANG Scheduler Engine 已经加载过 DLL,则将无法对其进行编译。如果是这样,就必须借助 Component Services 应用程序,将 XLANG Scheduler Engine 关闭。具体方法是,启动 Component Services 应用程序,找到 XLANG Scheduler Engine COM+ 应用程序,单击上下文菜单(用鼠标右键)上的“Close”。然后就应当可以对组件进行编译了。另外,如果 BizTalk Orchestration Designer 正在运行,则有一个菜单选项为“Shut Down All Running XLANG Schedule Instances”,可以用该选项来进行有关的关闭操作。选择该选项后,所有的 XLANG 计划均将关闭,释放对 DLL 的锁定,从而可以对其进行编译。
其它调试提示
除了借助 XLANG Event Monitor 跟踪计划的进度,还可以使用其它的系统监视器来检测正在运行的计划中的错误,以及跟踪计划的执行。XLANG Scheduler Engine 所引发的错误将出现在事件查看器的“应用程序”选项卡中。在事件查看器中,这些事件被标记为 XLANG Scheduler 错误。如有必要,还可以对事件查看器中所展示的事件进行筛选,从而仅展示这一事件类型。
事件查看器中的 WFBinding 错误组的意思是,在 BizTalk Messaging Services 和 BizTalk Orchestration Services 之间针对每个具体实例的消息队列接口中,发生了一个故障。BizTalk Messaging Services Messaging Port 向导中的 Orchestration 端口设置,要求您手动录入 Orchestration 端口名,因而一旦您将 Orchestration 端口名拼错,就会发生一个 WFBinding 错误。
另一常见错误就是分析验证错误。 这些错误经常是由实例文档不符合 BizTalk Editor 中所创建的文档规范所导致的。 在这种情况下,文档被提交到“挂起队列”,并将错误记录在事件查看器中。您用鼠标右键单击“挂起队列”中的项目,可以检查和将文档内容复制到剪贴板。通过将剪贴板内容粘贴到一个文本编辑器(诸如“记事本”)并保存文件,经常可以最为轻松地解决这些问题。现在您在文件系统上有一个文档实例了,您可以在 BizTalk Editor 中打开文档规范,并使用“工具”中的“验证实例”菜单项来对您已有的文档实例进行证实。请注意,即使对话框默认为 *.xml,您依旧可以对其它的文件类型进行证实,诸如 *.csv (如果您拥有一个平面文件架构的话)。您一旦成功地验证了文档,就可以将其保存到 WebDAV。
出于性能原因,在运行时,Microsoft BizTalk Server 2000 不读取文档定义或来自 WebDAV 的映射。这一方面显著地提高了性能,但同时也为开发人员带来了更多的可能会导致版本问题的工作。尤其是 BizTalk Messaging Services 并不将任何保存在 WebDAV 中的文件的内容刷新到运行时引擎。您在更改文档规范并将其存到 WebDAV 时,还必须开启 Messaging Manager,开启相应的文档定义,然后按“应用”按钮。这将促使 Messaging Manager 从 WebDAV 刷新其数据文件副本。同样,当针对封套的文档规范或通道中所用映射被更改时,您就必须手动更新封套和通道。
其它常见问题包括:
症状:文件被放入目录,但与其相关联的接收函数没有将其拾起。
其原因可能是:
- 文件有一个只读属性。这样的话,事件记录中会有一个事件,指出接收函数无法拾起某个名称的文件,因为它是只读的。
- 文件名与接收函数配置中所指定的掩码不匹配,这样的话,请对配置进行纠正。
- 接收函数配置中指定了一个不正确的目录。如果目录存在,则将没有任何出错症状。如果目录不存在,则接收函数会被禁用,并记录一个事件。纠正错误,并在属性页中重新启用接收功能。
- BizTalk 服务器已停机。每个接收功能均配置为在具体的 BizTalk 服务器上运行,而该服务器必须正在运行。
SQL 服务器已停机。接收函数如果无法将将文档放到 Work 队列上,则不会将其从目录去除。
症状:文件被从拾起目录去除,但后续处理并未发生。
可能起因:
- 无法对文档进行分析,或 Messaging Manager 尚未从 WebDAV 更新。
- 没有任何通道与接收功能属性中所指定的 Source Org、Destination Org 和 Doc Def 相匹配。请对这些属性进行验证,确保有一个通道与其相匹配。
症状:在提交了单独一个文档之后,同一计划似乎启动了多次。
可能起因:
- 一个通道可以连接到一个或更多的消息收发端口。在本例中,有多个通道连接到了对一个编制实例进行实例化的一个消息收发端口。
您可以将 Visual Basic 中的编程问题的复杂性细分为多个较小的可管理的部分;同样,您在使用 BizTalk Server 时,应当分离出,基础实施的哪个部分包含您希望解决的问题。例如,如果无法确定正要从 BizTalk Messaging Services 提交给 BizTalk Orchestration Services 的文档是否包含正确的实例数据,则将消息收发端口更改为输出到一个文件,以对文档的内容进行检查。
要跟踪正在运行的计划的执行情况,可以用 Performance Monitor 来显示计划的实施组件的结果。此类结果包括但不限于:
- 监视具体消息队列中的消息(来自 Microsoft Message Queuing Queue 对象)。
- Microsoft Message Queuing 传入和传出消息(来自 Microsoft Message Queuing Service 对象)。
- 所处理的 BizTalk Messaging 文档和数据交换(来自 BizTalk Server 对象)。
- BizTalk Messaging Suspended 队列(来自 BizTalk Server 对象)。
- 活动、已放弃或已完成的事务(来自 Distributed Transaction Coordinator 对象)。
与 Performance Monitor 一样,Component Services MMC (Microsoft Management Console) 也可以用于监视所初始且已完成或放弃的事务(也可以借助 SQL Profiler 应用程序)。 Component Services 应用程序也可以用于监视任意的安装为 COM+ 应用程序的 COM+ 组件的实例化情况。
本文档只是初步阐述作者观点,并在最终用于商业出版前可能会进行较大的修改。本文档仅用于提供信息,Microsoft 对本文中的信息不做任何明示或暗示的保证。本文档中的信息如有更改,恕不另行通知。使用本文档的全部风险和后果均由用户自行承担。本文中用作示例的公司、组织、产品、人名和事件均为虚构,与任何真实的公司、组织、产品、个人或事件没有任何直接或间接的关联。用户有责任遵从所有适用的版权法。在不限制版权的情况下,未经 Microsoft Corporation 明确书面许可,不得擅自将本文档的任何部分进行复制、存储在或输入可检索系统,或以任何形式或方式(电子、机械、影印、录制或其它方式)进行传播,或用作其它目的。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=3455