鹰翔宇空

学习和生活

BlogJava 首页 新随笔 联系 聚合 管理
  110 Posts :: 141 Stories :: 315 Comments :: 1 Trackbacks
原文引自:http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx

第 4 章 — 偶尔连接的智能客户端

发布日期: 8/20/2004 | 更新日期: 8/20/2004

智能客户端体系结构与设计指南

David Hill, Brenton Webster, Edward A. Jezierski, Srinath Vasireddy and Mohammad Al-Sabt, Microsoft Corporation; Blaine Wastell, Ascentium Corporation; Jonathan Rasmusson and Paul Gale, ThoughtWorks; and Paul Slater, Wadeware LLC

相关链接

Microsoft® patterns & practiceshttp://www.microsoft.com/resources/practices/default.mspx

Application Architecture for .NET: Designing Applications and Services http://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp

摘要:本章讨论了您在设计和生成偶尔连接到网络的智能客户端应用程序时可能面临到的问题。本章讨论了连接性的概念,介绍了两种实现脱机功能的主要方法,并且讨论了您在使应用程序可供脱机使用时需要考虑的一些问题。

*
本页内容
常见的偶尔连接情况 常见的偶尔连接情况
偶尔连接设计策略 偶尔连接设计策略
使用面向服务的方法设计偶尔连接的智能客户端应用程序 使用面向服务的方法设计偶尔连接的智能客户端应用程序
使用基于任务的方法 使用基于任务的方法
处理依赖性 处理依赖性
小结 小结

我们生活在一个连接程度越来越高的世界里。然而,在许多情况下,我们不能无时无刻都依赖连接。您的用户可能需要旅行,他们可能会暂时失去无线连接,可能存在延迟或带宽问题,或者您可能需要拆卸网络的某些部分进行维护。即使用户的确具有良好的网络连接,您的应用程序也可能无法在所有时间都能访问网络资源。所请求的服务可能繁忙、停止运行或者只是暂时不可用。

如果应用程序有时无法及时地通过网络与服务或数据交互,则为偶尔 连接的应用程序。如果您能让用户在脱机时使用其应用程序富有成效地工作,并且仍然能够为其提供连接的应用程序在连接有效时所具有的好处,则可以提高用户的生产率和工作效率,并且提高应用程序的可用性。

智能客户端相对于基于 Web 的应用程序所具有的主要好处之一是:当应用程序无法连接到网络资源时,它们能够让用户继续工作。偶尔连接的智能客户端能够在未连接到网络资源时工作,然后在以后某个时间在后台更新网络资源。更新可能几乎立即发生,但有时可能发生在数天甚至数周以后。

要给予应用程序完整的偶尔连接功能,您需要提供一种基础结构,使用户能够在没有连接到网络资源时工作。该基础结构应该包括数据缓存,以便可以在客户端使用所有需要的数据;它还应该包括用户工作详细信息的存储,以便用来在用户重新联机时将客户端与网络资源同步。应用程序为支持偶尔连接的操作所需要的确切特性和功能取决于它的连接性、操作环境以及用户在联机和脱机时所期望的功能。但是,所有智能客户端应用程序都应该在未连接到网络时为用户提供某种体验,即使功能极其有限。在设计和生成应用程序时,应该始终避免因为服务器不可用而在客户端生成错误消息。

本章考察您在生成具有脱机功能的应用程序时所面临的问题。它将讨论设计脱机应用程序的不同策略,详细讨论设计注意事项,分析如何组织应用程序以使用任务,并且考察应用程序应该如何处理数据。

常见的偶尔连接情况

偶尔连接的智能客户端在许多常见的情况下都极其有用。许多脱机情况涉及到用户显式断开网络连接并且在没有网络连接的情况下工作,例如:

保险代理可能需要在离开办公室的时候创建新的保险单。他或她可能需要在无法连接到办公室中系统的情况下,输入所有相关数据,计算保险费,并签发保险单详细信息。

销售代表可能需要在现场(该销售代表无法在此处连接到服务器)与客户签订大订单。他或她可能需要咨询价格表和目录信息,输入所有订单数据,并提供交货预算和折扣级别,而无须连接。

维护技术人员在客户端站点处理服务呼叫时可能需要详细的技术信息。应用程序帮助他或她诊断问题,提供技术文档资料和详细信息,并且使该技术人员无须连接就能签下部件订单以及记录他或她的操作。

其他脱机情况涉及到间歇性或低质量的连接,例如:

遍布全球的客户呼叫中心和企业网络之间的连接可能不具有足够高的质量,以供全天候联机使用。应用程序应该提供脱机功能(包括数据缓存),以便维持应用程序的可用性。

携带 Tablet PC 旅行的医务人员可能在旅行途中经历网络连接中断的情况。当应用程序连接时,它应该在后台同步数据,而不应该等待显式重新连接。

应该将偶尔连接的智能客户端设计为最大限度地利用可用连接,确保应用程序和数据尽可能保持最新,并且不会对应用程序的性能造成不利影响。

返回页首返回页首

偶尔连接设计策略

在设计偶尔连接的智能客户端应用程序的体系结构时,有两种概括性的方法:以数据为中心 和面向服务。

使用以数据为中心的策略的应用程序具有一个在客户端上本地安装的关系数据库管理系统 (RDBMS),并且使用该数据库系统的内置功能将本地数据更改传回服务器,处理同步过程,并检测和解决任何数据冲突。

使用面向服务方法的应用程序将信息存储在消息中,并且当客户端脱机时将这些消息排列到队列中。在重新建立连接以后,排队的消息将被发送到服务器进行处理。

图 4.1 显示了以数据为中心的方法和面向服务的方法。


4.1 偶尔连接应用程序设计的面向服务的方法和以数据为中心的方法

本节将详细分析这两种方法并解释何时应该使用每种方法。

以数据为中心的方法

当您使用以数据为中心的方法时,通常情况下,服务器发布数据,而客户端创建它所需要的数据的预订,以便它可以在脱机之前将相应的数据复制到本地数据存储。当客户端脱机时,它将通过对本地数据存储的调用来对本地数据进行更改。当客户端重新联机后,数据存储会将对客户端上的数据所做的更改传回服务器。对服务器上数据所做的更改也可能被传回客户端。在合并阶段遇到的任何冲突都将按照业务分析师定义的自定义规则,由在服务器或客户端上指定的冲突解决规则处理。

在客户端和服务器之间合并更改的过程称为合并复制。更改可能在客户端和服务器以自治方式发生,因此不使用 ACID(原子、一致、独立、持久)事务。相反,当执行合并时,系统中的所有预订者都将使用发布者拥有的数据值。

以数据为中心的方法主要优点是所有更改跟踪代码都包含在关系数据库中。通常,这包括数据库的列级和行级的冲突检测代码、数据验证代码以及约束。这意味着您无须编写自己的更改跟踪代码或冲突检测与解决代码,尽管您的确需要知道合并-复制方案以便针对数据冲突和数据更新来优化您的应用程序。

在以数据为中心的模型中,数据库系统负责处理同步;因此,您无须自己来实现所有数据同步功能。用户定义哪些表要求数据同步,而数据库系统使基础结构可以跟踪更改并且检测和解决冲突。您可以通过使用 COM 对象或 Transact SQL (TSQL) 存储过程的自定义冲突解决程序来扩展该基础结构,以便提供自定义冲突解决或避免机制。而且,因为在整个系统中只有一个数据储存库,所以当同步完成时,能够保证在服务器和客户端之间执行数据会聚。

但是,以数据为中心的方法有一些缺点。在客户端上需要本地数据库意味着该方法在下列情况下可能不适合:

应用程序在小型设备上运行

需要轻触部署机制。

非管理员用户应该能够部署应用程序

Microsoft 提供了能够在 Windows® 客户端、Windows Server™ 和 Pocket PC 平台上运行的数据库软件,但它没有为 SmartPhone 设备提供数据库软件。

而且,服务器上的数据库与客户端上的数据库之间的紧耦合性意味着对服务器上数据库架构进行的更改对客户端具有直接影响。这可能使管理对客户端或服务器进行的数据库架构更改变得很困难。

对于大量客户端而言,需要提供一种可管理且可伸缩的方法来部署独特的数据集。合并复制支持动态筛选,这使得管理员能够定义这些脱机数据集并且以可伸缩的方式部署它们。您应该利用数据库提供的筛选机制来减少在客户端和服务器之间发送的数据量,并降低冲突的可能性。

使用本地数据库在本地存储和操作数据有许多好处。您可以使用数据库将本地更改传回服务器并帮助处理同步问题。

您应该在下列情况下使用以数据为中心的方法:

您可以在客户端部署数据库实例。

您的应用程序可以在两层环境中工作。

您可以通过数据架构定义和通讯协议将客户端紧耦合到服务器。

您需要内置的更改跟踪和同步。

您希望依赖数据库来处理数据协调冲突以及尽可能减少需要编写的自定义协调代码的数量。

您无须与多个截然不同的服务交互。

Windows 用户能够通过局域网 (LAN) 或虚拟专用网络 (VPN/IPSec) 直接连接到数据库。为 Pocket PC 平台编写的应用程序能够通过 HTTPS 同步 HTTP。

本指南没有深入讨论以数据为中心的方法。许多地方对它进行了更为完整的介绍,包括 Microsoft SQL Server 联机图书或 MSDN。有关以数据为中心的方法的详细信息,请参阅“Merge Replication”,网址为:http://msdn.microsoft.com/library/en-us/replsql/repltypes_6my6.asp

面向服务的方法

对于面向服务的方法而言,客户端可以与需要的任何服务交互。而且,客户端将致力于服务请求本身,而不是对本地保存的数据进行直接更改。服务请求可能在客户端或服务器上导致状态更改,但此类更改是服务请求的副产品。

面向服务策略的一个优点是在客户端上不需要本地关系数据库。这意味着可以将该方法应用于许多不同的客户端类型,包括那些具有少量处理能力的客户端,如移动电话。

当您的应用程序必须在 Internet 和 Extranet 环境中工作时,面向服务的方法尤其适合。如果您的客户端在防火墙外部工作并且与企业服务交互,则通过使用面向服务的策略,您可以避免由于某种原因而必须在防火墙中打开特定的端口,例如为了启用直接数据库或 Microsoft 消息队列 (MSMQ) 访问。

松耦合意味着您可以在客户端上使用与服务器上不同的数据架构,并且在客户端传输数据。实际上,客户端和服务器不需要知道对方。您还可以独立地更新客户端和服务器组件。

该方法的主要缺点是您需要编写更多的基础结构代码,以便存储和转发消息以及检测应用程序何时联机或脱机。这可以使您在设计中具有更多的灵活性,但通常意味着在创建脱机客户端时需要完成更多的工作。

智能客户端脱机应用程序块 (Smart Client Offline Application Block) 为您提供了支持脱机客户端的面向服务策略的代码。您可以使用该块来检测应用程序何时联机或脱机,并且存储消息以及将消息转发给服务器进行处理。有关该应用程序块的概述,请参阅 Smart Client Offline Application Block,网址为:http://msdn.microsoft.com/library/en-us/dnpag/html/offline.asp

面向服务的方法最适合于需要与许多不同服务交互的智能客户端。因为消息的有效负载被封装,所以传输层可以改变,而不会影响消息的内容。例如,原来要发往某个 Web 服务的消息可以同样容易地发往消耗消息队列消息的服务。消息与传输无关这一事实还使应用程序可以根据需要自定义安全性实现。

您应该在下列情况下使用面向服务的方法:

您希望消除客户端和服务器之间的耦合,以便进行独立的版本控制和部署。

您需要对数据协调问题拥有更多的控制和灵活性。

您具有编写更高级应用程序基础结构代码的开发人员技能。

您要求轻量客户端足迹。

您能够将应用程序组织为面向服务的体系结构。

您要求特定的业务功能(例如,自定义业务规则和处理、灵活的协调等等)。

您需要对客户端上所存储数据的架构进行控制,并且还要控制可能与服务器不同的灵活性。

您的应用程序与多个服务或截然不同的服务(例如,多个 Web 服务,或者通过消息队列、Web 服务或 RPC 机制提供的服务)交互。

您需要自定义安全方案。

您的应用程序在 Internet 或 Extranet 环境中工作。

尽管以数据为中心的方法和面向服务的方法都是有效的体系结构方法,但许多智能客户端应用程序无法在客户端上支持完整的关系数据库实例。在这种情况下,您应该采用面向服务的方法,并且确保您拥有适当的基础结构,以便处理诸如数据缓存以及冲突检测和解决等问题。

因此,本章的其余部分将集中讨论智能客户端开发人员在实现面向服务的方法时需要考虑的问题。

返回页首返回页首

使用面向服务的方法设计偶尔连接的智能客户端应用程序

当您使用面向服务的方法设计偶尔连接的智能客户端时,需要解决许多问题。这些问题包括:

优选异步通讯。

尽可能减少复杂的网络交互。

添加数据缓存功能。

管理连接。

设计存储转发机制。

管理数据和业务规则冲突。

与类似于 Create, Read, Update, Delete (CRUD) 的 Web 服务交互。

使用基于任务的方法。

处理依赖性。

本节将详细讨论这些问题。

优选异步通讯

应用程序在与位于网络上的数据和服务交互时,使用下列两种通讯方法之一:

同步通讯。应用程序被设计为在继续处理之前期待响应(例如,同步 RPC 通讯)。

异步通讯。应用程序通过使用消息总线或其他某种基于消息的传输来进行通讯,并且期待在请求和任何响应之间存在延迟,或者根本不期待任何响应。

注在本指南中,同步通讯是指在能够继续处理之前期待响应的所有通讯,即使同步调用是在单独的后台线程上执行的。

如果您要设计新的智能客户端应用程序,则应该确保它在与位于网络上的数据和服务交互时主要使用异步通讯。被设计为期待请求和响应之间存在延迟的应用程序非常适合于偶尔连接的用途,前提是该应用程序在等待响应的过程中能够提供重要且有用的功能,并且在响应延迟时不会妨碍用户继续完成他或她的工作。

当该应用程序未连接到网络资源时,您可以在本地存储请求,并且在应用程序重新连接时将这些请求发送到远程服务。无论是脱机还是联机,因为应用程序并不期待请求立即得到响应,所以都不会禁止用户继续使用该应用程序,用户可以继续工作。

使用同步通讯的应用程序(即使是在后台线程上)不太适合于偶尔连接的用途。因此,您应该在智能客户端中尽可能减少对同步通讯的使用。如果您要将使用同步通讯的应用程序重新设计为智能客户端,则应该确保它采用异步程度更高的通讯模型,以便它能够脱机工作。但是,在许多情况下,您可以在异步基础结构之上实现类似于同步的通讯(称为同步-异步模型),以便将应用程序设计更改保持到最低限度。

将应用程序设计为使用异步通讯可以为您带来超越偶尔连接用途的好处。大多数设计为使用异步通讯的应用程序都比那些使用同步通讯的应用程序更加灵活。例如,可以在异步应用程序正在执行任务的过程中将其关闭,并且当它重新启动时不会影响请求或响应的处理。

在大多数情况下,您不需要在应用程序中同时实现同步和异步行为以便在联机和脱机时使用。异步行为同时适合于联机和脱机使用;当应用程序联机时,请求会以接近于实时的速度进行处理。

尽可能减少复杂的网络交互

偶尔连接的智能客户端应该尽可能减少或消除与位于网络上的数据和服务的交互。当您的应用程序脱机时,它可能必须存储请求并在应用程序重新连接时发送这些请求,或者它可能需要针对响应等待一会儿。无论采取哪种方式,应用程序都不会立即知道请求是否即将成功或已经成功。

要使应用程序能够在脱机时继续工作,您必须就网络请求的成功或本地数据的更改进行某些假设。跟踪这些假设以及服务请求和数据更改之间的依赖性可能非常复杂。要减轻这一负担,您应该尽可能地围绕简单的网络交互来设计您的智能客户端应用程序。

通常,不返回任何数据的请求(“发后不理”请求)对于偶尔连接的应用程序来说不会成为问题;应用程序可以存储该请求并在重新连接时转发该请求。当应用程序脱机时,它不知道调用是否已经成功;因此,该应用程序必须假设调用已成功。这一假设可能影响后续处理。

如果请求返回应用程序继续工作所需要的数据,则应用程序必须使用暂定值或虚拟值,或者在没有相应数据的情况下工作。在此情况下,您需要将应用程序设计为跟踪暂定数据和已确认的数据,并且将用户界面设计为使用户知道暂定或挂起的数据。这使用户或应用程序能够基于数据的有效性进行明智的决策,并防止以后出现与数据冲突和损坏有关的问题。

如果用户已经在脱机状态下完成了一些不连续的工作单元,则应用程序应该允许各个工作单元单独成功或失败。例如,在允许用户输入订单信息的应用程序中,应用程序可以让用户根据需要输入任意多的订单,但该应用程序必须确保一个订单不必依赖于其他订单的成功。

当应用程序对于每个工作单元只能产生一个服务请求时,确保各个工作单元之间不存在依赖性是相对容易的。这使应用程序可以跟踪挂起的请求,并且可以在联机后处理这些请求。但是,在某些情况下,用户任务更为复杂,必须产生多个服务请求才能完成它们。在这些情况下,应用程序必须确保每个请求都与其他请求一致,以便保持数据一致性。

添加数据缓存功能

您的应用程序需要确保在它脱机时,客户端能够提供用户继续工作所需的所有数据。在某些情况下,您的应用程序应该出于性能原因在客户端缓存数据,但很多时候,您的应用程序必须缓存额外的数据以满足偶尔连接的用途。例如,您可能没有为联机使用的应用程序缓存不稳定的数据,但要使同一应用程序能够脱机工作,则需要在本地计算机上缓存这些数据。客户端和服务器端都必须进行相应的设计以考虑数据不稳定性,以便它们可以适当地处理更新和冲突。

当应用程序脱机时,您可能选择不从应用程序数据缓存中删除过时的数据,而是使用这些过时的数据以使用户能够继续工作。在其他情况下,应用程序可能需要自动从缓存中删除这些数据,以防止用户使用它并在以后产生问题。在后一种情况下,在通过同步过程获取新数据之前,应用程序可能停止提供所需的功能。

根据应用程序风格和功能的不同,缓存中数据的刷新可能以多种方式发生。对于某些应用程序,可以按照下列策略自动刷新缓存的数据:当缓存的数据到期时刷新;按照某个时间表定期刷新;当应用程序执行同步操作时刷新;或者当服务器更改了数据并将相应的更改通知应用程序时刷新。其他应用程序可能允许用户手动选择要缓存的数据,从而使用户能够在脱机状态下分析或使用这些数据。

其他数据缓存注意事项同样适用,如安全性约束和数据处理约束。这些问题并非只能在支持脱机操作的应用程序中遇到,第 2 章:处理数据中对它们进行了更为完整的介绍。

处理对引用数据的更改

引用数据是很少发生更改的数据。通常,应用程序包含大量这样的数据。例如,在客户记录中,客户名称很少更改。可以容易地将这种类型的数据缓存在客户端上,但有时您的引用数据将发生更改,而您必须具有某种将这些更改传播到智能客户端的机制。

您具有两种传播这些数据的选项:推模型和拉模型。

在推模型中,服务器抢先通知客户端并试图将数据推出。在以数据为中心的方法中,这由复制客户端数据存储中被刷新的数据的服务器数据组成。在面向服务的方法中,这可能是包含已更新数据的消息。(这要求客户端实现可供服务器连接到的终结点。)

在拉模型中,客户端与服务器联系以检查是否存在更新。客户端可以通过定期检查服务器做到这一点,也可以通过分析带有声明引用数据何时到期的原始数据的元数据来做到这一点。客户端甚至可能趁早从服务器拉数据(例如,价格表),并且仅当它变为有效时才使用它。

在某些情况下,您可能选择采用这样一种模型,即由服务器通知客户端更新可用(例如,通过在客户端连接时发送警报),然后客户端从服务器拉数据。

管理连接

当您设计偶尔连接的智能客户端时,应该从以下两个方面考虑应用程序的工作环境:可用的连接性,以及应用程序随着该连接性的更改而需要具有的行为。

应该将某些应用程序设计为能够在没有连接的情况下长时间地(数天,甚至数周)工作。而对于其他应用程序,则应该将其设计为总是期待连接,但能够优雅地处理连接临时断开连接的情况。某些应用程序在脱机时应该只提供功能子集,而其他应用程序则应该提供大部分功能以供脱机使用。

尽管许多偶尔连接方案都涉及到用户从网络显式断开连接,并在没有连接的情况下工作,但有时应用程序在未显式从网络断开连接的情况下脱机。可以将应用程序设计为能够处理其中一种方案或能够处理这两种方案。

手动连接管理

可以将应用程序设计为能够在用户决定脱机工作时正常工作。应用程序必须在本地计算机上存储用户可能需要的所有数据。在此情况下,用户在与应用程序交互时知道它处于脱机状态,而应用程序不会试图执行网络操作,直到它被明确告知联机并执行同步操作为止。

您还可以包括相应的支持,以便用户能够在使用连接成本较高的连接或低带宽连接(如商业无线热点、移动电话连接或拨号连接)时通知应用程序。在此情况下,可以将应用程序设计为对请求进行批处理,以便在建立连接时,能够最大限度地利用它。

自动连接管理

可以将应用程序设计为能够动态适应意外发生的连接性更改。这些更改可能包括下列情况:

间歇性的连接。可以将应用程序设计为能够适当地适应或处理那些网络连接临时丢失的情况。某些应用程序可能临时挂起功能,直至应用程序能够重新联机为止,而其他应用程序则必须提供完整的功能。

不断变化的连接质量。可以将应用程序设计为能够预测网络连接具有低带宽或高延迟的情况,或者可以动态确定这一情况并改变其行为以适应其环境。如果连接质量恶化,则应用程序可以更为积极地缓存数据。

不断变化的服务可用性。可以将应用程序设计为能够处理它通常与其交互的服务不可用的情况,并切换到它的脱机行为。如果应用程序与多个服务交互,并且其中一个服务变得不可用,则它可能决定将所有服务都视为脱机服务。

您可以通过使用 wininet.dll 来检测智能客户端应用程序是否具有连接。该 DLL 与 Microsoft Internet Explorer 用于确定用户是否连接到 Internet 的 DLL 相同。下面的代码示例显示了如何调用 wininet.dll。

   [DllImport("wininet.dll")] 
   private extern static bool InternetGetConnectedState( out int  
connectionDescription, int reservedValue ) ; 
   public bool IsConnected() { 
     int connectionDescription = 0; 
     return InternetGetConnectedState(out connectionDescription, 0); 
} 

设计存储转发机制

如果您将应用程序设计为使用面向服务的体系结构,则必须提供存储转发机制。通过存储转发,可以创建、存储消息并最终将其转发到它们各自的目的地。最常见的存储转发实现是消息队列。这就是面向消息的中间件产品(如 Microsoft 消息队列)的工作方式。当创建新的消息时,它们将被放入消息队列,并被转发到它们的目标地址。尽管还有其他存储转发替代机制(如 FTP 或在客户端与服务器之间复制文件),但本指南将专门讨论最常见的实现:消息队列。

智能客户端需要一种在脱机时保留消息的方法。如果您的应用程序需要在脱机时创建新消息,则您的队列必须具有一种保留它们以便以后与服务器进行更新的方法。这里,最显而易见的选择是将其写入磁盘。

您的设计需要包括能够确保将消息成功传递到其目的地的功能。您的设计应该考虑下列情况:

缺少有关消息已正确发送的确认。通常,您不应该只是因为消息已经离开队列就假设已在服务器收到该消息。

客户端与服务器之间的连接丢失。在某些情况下,因为客户端与服务器之间的连接丢失,所以您必须从队列返回消息。

缺少来自服务的确认。在此情况下,您可能需要发送独立的确认以通知客户端信息已收到。

存储转发机制还需要支持附加功能,如消息加密、优先级化、锁定和同步。

生成和设计可靠的消息处理体系结构是一项复杂的任务,要求有丰富的经验和技能。因此,您应该认真地考虑一下商业产品,如 Microsoft 消息队列。但是,Microsoft 消息队列要求在客户端上安装软件,这可能并非所有智能客户端的选择。

另一个消息队列管理选择是使用智能客户端脱机应用程序块,它可在 http://msdn.microsoft.com/library/en-us/dnpag/html/offline-CH01.asp 获得。

该应用程序块提供了相关的服务和基础结构,智能客户端可用来向它们的应用程序提供脱机功能。该应用程序块使用消息队列概念支持消息处理的存储转发方法。默认情况下,该应用程序块支持消息队列集成以及其他一些消息保留机制(内存、独立存储和 Microsoft SQL Server™ 桌面引擎 [MSDE])。

管理数据和业务规则冲突

以脱机模式在应用程序中进行的更改必须在某个时刻与服务器同步或协调。这增加了发生冲突或发生应用程序、用户或管理员必须解决的其他问题的可能性。当确实发生冲突时,您必须确保能够检测并解决它们。

与数据冲突不同,发生业务规则冲突的原因不是在两块数据之间存在冲突,而是在某个地方违反了业务规则并且需要纠正。数据冲突和业务规则冲突都可能需要由客户端应用程序或用户处理。

作为业务规则冲突的示例,假设您有一个订单管理应用程序,该应用程序缓存了一个产品目录,以便用户能够在脱机时将订单输入系统。然后,当该应用程序重新联机时,这些订单将被转发给服务器。如果某个订单包含一种位于缓存的产品目录中但在应用程序重新联机时已经停止生产的产品,则当订单数据被转发给服务器时,服务器将检查订单详细信息并且发现该产品已经停止生产。此时,应用程序可以通知用户该订单有一个问题。如果所述产品已被替换或废弃,则系统可以给予用户切换到不同产品的能力。这种情况不是数据冲突(因为数据没有与任何内容冲突),但这仍然是不正确的,并且需要纠正。

尽管业务规则异常和数据冲突是不同类型的异常,但大多数情况下,可以使用相同的基本方法和基础结构来处理它们。本节讨论如何在智能客户端应用程序中处理数据和业务规则冲突。

数据分区和锁定

任何允许多个用户访问共享数据的系统都有产生冲突的可能。当您设计智能客户端应用程序时,您必须确定它是否将数据分区以及如何执行锁定,因为这些因素有助于确定在应用程序中发生冲突的可能性有多大。

数据分区

当不同用户对数据的不同部分具有控制权时,可以使用数据分区。例如,销售代表可能具有一些只分配给他或她的帐户。在此情况下,您可以将数据分区,以便只有销售代表可以更改这些帐户。以这种方式将数据分区后,可以允许用户对数据进行任意更改,而不必担心遇到数据冲突。

将应用程序设计为使用数据分区通常会受到很多限制,因此在许多情况下不是好的解决方案。但是,如果数据分区对于特定应用程序是可行的,则应该认真地予以考虑,因为它有助于减少应用程序所产生的冲突的数量。

保守式锁定

保守式锁定意味着系统使用互相排斥的锁来确保一次只有一个用户对系统数据进行操作。对数据的所有请求都被序列化。例如,在出发之前,推销员可能访问数据库,并且逻辑地为特定地区的客户签出客户帐户。这一签出操作可能要求在办公室里更新一个电子表格,并且向其他人发送电子邮件以更新帐户状态。现在,当该推销员出差时,其余销售人员就会明白该推销员对这些客户文件具有独占的访问权,并且可以随意进行所需的任何修改。当该推销员回到办公室并将新数据与服务器数据进行同步时,应该没有任何冲突。在同步数据之后,该推销员将释放逻辑锁。

保守式锁定的主要问题是:如果多个用户需要同时对相同数据进行操作,则他们必须等待数据变得可用。对于偶尔连接的智能客户端,数据可能在某个客户端重新联机之前一直保持锁定,而这一时间可能很长。这就使得保守式锁定在数据完整性方面很好(因为没有发生冲突的可能性),但在并发性方面却很差。

实际上,保守式锁定仅适合于几种类型的偶尔连接应用程序。例如,在文档管理系统中,用户在处理文档时可能会有意地将文档签出一段持续很久的时间。但是,随着可伸缩性和复杂性的提高,保守式锁定正在成为一种不太可行的选择。

开放式锁定

大多数偶尔连接的智能客户端应用程序都使用开放式锁定,它允许多个用户并发地访问和操作相同的数据,并且假设各个用户对数据进行的更改将不会发生冲突。开放式锁定允许对数据进行高度并发性访问,其代价是数据完整性将会降低。如果发生冲突,则需要一种能够处理这些冲突的策略。

在大多数脱机方案中,您需要使用开放式锁定。因此,您必须预料到会发生数据冲突,并且必须在确实发生数据冲突时对它们进行协调。

跟踪未确认或暂定的数据

当您的用户脱机工作时,他们已经更改的任何数据都没有被确认为服务器上的更改。只有在这些数据已经与服务器进行合并而且没有任何冲突之后,才能真正地将这些数据视为已确认的数据。对未确认的数据进行跟踪是很重要的。在已经确认数据之后,可以对其进行标记并适当地使用。

您可能希望在应用程序的用户界面中以不同的颜色或字体显示未确认的数据,以便用户可以知道这些数据是暂定的。通常,在已经确认数据之前,您的应用程序不应该允许在多个任务中使用这些数据。这可以防止未确认的数据溢出到其他需要已确认数据的活动中。使用已确认的数据并不能保证将来不会发生冲突,但应用程序起码将知道数据曾经被确认过并且随后已经被某个用户进行了更改。

处理陈旧数据

即使数据尚未更改,它也可能因为不再是最新数据而变得不再正确。这样的数据称为陈旧数据。当您设计智能客户端应用程序时,您需要确定如何处理陈旧数据以及如何防止您的智能客户端使用陈旧数据。这对于偶尔连接的智能客户端而言尤其重要,因为数据在客户端首次脱机时可能是最新的,但在客户端重新联机之前可能变为陈旧数据。此外,在客户端上为最新的数据在到达服务器时可能成为陈旧数据。例如,推销员可能在某个星期五使用有效数据为各种项目创建了一份订单,但是如果他或她没有在下个星期一之前将该订单提交给服务器,则这些项目的费用可能已经更改。

如果在应用程序重新联机时,对服务请求进行排队并且做好发送准备,则该请求排队的时间越长,它遇到数据冲突或异常的可能性就越大。例如,如果您将某个服务请求(该请求包含许多项目的订单)排队,并且在很长一段时间内没有发送该请求,则您订购的项目可能停止生产或脱销。

您可以使用许多技术来处理陈旧数据。您可以使用元数据来说明数据的有效性并且显示数据的到期时间。这可以防止将陈旧数据传递到客户端。

在服务器上,您可以选择对来自客户端的任何数据进行检查,以确定它是否为陈旧数据,然后再根据情况决定是否允许它与服务器上的数据进行合并。如果该数据是陈旧数据,则可以在将该数据重新提交给服务器之前,确保客户端更新其引用数据。

对于偶尔连接的应用程序而言,陈旧数据的风险大于始终连接的应用程序。因此,智能客户端应用程序通常会执行附加的验证步骤以确保数据是有效的。通过向系统中添加额外的验证,您还可以确保您的服务对陈旧数据具有更高的容错性,并且在某些情况下,您或许能够在服务器上自动处理协调(即,将事务映射到新的帐户)。

有时候,陈旧消息是无法避免的。处理陈旧数据的方式应该以您要建模的业务的规则为基础。在某些情况下,陈旧数据是可以接受的。例如,假设为联机目录中的特定项目提交了一个订单。该项目具有一个目录编号,该编号由于联机目录更改已经陈旧。但是,该项目仍然可用且尚未更改,目录编号更改对系统没有影响,并且将生成正确的订单。

另一方面,如果您要在两个帐户之间执行财务事务,并且其中一个帐户尚未关闭,则您无法执行该事务。这里,数据的陈旧性确实很重要。

一个很好的一般性规则是让业务对象为您处理陈旧数据的情况。您的业务对象可以验证数据是否是最新的,如果数据是陈旧的,则可以不做任何事情、用等效的最新数据协调陈旧数据、将信息传回到客户端以便更新或者使用业务规则自动执行适当的响应。

陈旧数据的协调可能发生在客户端、服务器或这两者。在服务器处理协调可以使您的应用程序容易地检测到冲突。在客户端处理协调可以免除那些可能需要手动解决任何冲突的用户或管理员的一些职责。

没有处理陈旧数据的最佳方式。您的业务规则可能规定,如果客户端无法解决冲突,则服务器是处理陈旧数据的最佳位置。如果服务器没有足够信息以自动处理这种情况,则您需要要求客户端在与服务器进行同步之前清理它的数据。相反,您可能决定陈旧数据完全适合于您的应用程序,在此情况下您没有什么好担心的。

协调冲突

当您分析您所在组织的数据协调要求时,您应该考虑组织的工作方式。在某些情况下,因为不同的用户负责不同的数据元素,所以有可能发生冲突。在其他情况下,冲突将更为频繁地发生,您必须确保您具有相应的机制来处理它们。

无论您采取什么样的预防措施,客户端都有可能向网络服务提交将导致违反业务规则或数据冲突的数据。当冲突确实发生时,远程服务应该尽可能多地提供有关数据冲突的详细信息。在某些情况下,数据冲突可能不是重大问题,并且可以由应用程序或服务器自动处理。例如,设想有一个客户关系管理 (CRM) 系统,并且用户更改了某个客户的电话号码。在服务器上更新该更改时,发现另一个用户也已经更改了该电话号码。您可能选择对您的系统进行相应的设计,以便使最新的更改总是优先,或者您可能希望将冲突发送给管理员。如果管理员知道是谁在什么时候进行了这些更改,则他或她就可以做出有关保留哪一个更改的决策。重要的一点在于服务器和应用程序提供足够的详细信息以便进行自动处理,或者为用户或管理员提供足够的信息以便他或她能够协调冲突。

数据协调可能是一个复杂且与具体情况有关的问题。每个业务和每个应用程序都具有稍微不同的规则、要求和假设。然而,您具有三个一般性的数据协调选项:

在服务器上自动协调数据

在客户端上自定义协调

第三方协调

依次考察一下上述每个选项是有用的。

在服务器上自动协调数据

在某些情况下,您可以对您的应用程序进行相应的设计,以便服务器使用业务规则和自动过程来处理冲突,而不会影响客户端。您可以确保最新的更改总是优先,合并两个数据元素,或者采用更为复杂的业务逻辑。

在服务器上处理冲突对于可用性很有好处,并且使用户免于过多地涉及协调过程或忍受该过程带来的不便。您应该始终通过某种方法使客户端了解所采取的任何协调操作;例如,通过向客户端返回协调报告,并且解释冲突以及它的解决方式。这样就使客户端可以保持其本地数据的一致性并且将协调结果通知用户。

例如,假设应用程序允许用户为本地缓存的目录中的项目输入订单信息。如果用户订购的项目已经停止生产并且由较新但类似的型号取代,则订单服务可以选择用新项目替换原来的项目。然后,将向客户端通知该更改,以便它可以适当地修改它的本地状态。

在客户端上自定义协调

在某些情况下,客户端是执行协调的最佳位置,因为它了解有关原始请求的上下文的更多信息。应用程序或许能够自动解决冲突。在其他情况下,用户或管理员必须确定如何解决冲突。

要进行有效的客户端协调,服务应该向客户端发送足够的数据,以使客户端能够就如何解决冲突进行明智的决策。应该将冲突的确切详细信息报告给客户端,以便该客户端、用户或管理员可以确定解决问题的最佳方式。

第三方协调

在某些情况下,您可能希望第三方来协调任何数据冲突。例如,可以要求管理员或超级用户协调重要的数据冲突。他们可能是具有确定正确操作过程的职权的唯一用户。在此情况下,需要通知客户端决策已挂起。客户端或许能够通过使用暂定值继续工作,但通常它必须等待至基础冲突已经解决为止。当该冲突解决后,将通知客户端。作为替代方法,客户端还可以定期轮询以确定状态,然后在收到协调值时继续工作。

与类似于 CRUD 的 Web 服务交互

许多 Web 服务都是使用类似于 Create, Read, Update, Delete (CRUD) 的接口创建的。本节讨论几种用于创建消耗此类服务的偶尔连接应用程序的策略。

Create

在 CRUD Web 服务中,创建记录应该是一项比较简单的任务,前提是您能够正确地管理记录的创建。最重要的事情是唯一标识所创建的每个记录。大多数情况下,您可以通过使用唯一标识符作为记录的主键做到这一点。这样,即使在不同的客户端上创建了两个看上去完全相同的记录,在发生合并复制时也将把这些记录视为不同的记录。

在某些情况下,您可能不希望将记录视为唯一的。在这些情况下,您可以在两个记录冲突时生成异常。

您可以使用多种方法在脱机客户端上创建唯一标识符。这些方法包括:

将记录作为不带唯一 ID 的数据传输对象 (DTO) 发送,并且让服务器分配 ID。

使用客户端可以分配的全局唯一标识符 (GUID),如 System.Guid

在客户端上分配临时 ID,然后在服务器上用真正的 ID 替代它。

向每个客户端分配一组唯一的 ID。

使用用户的姓名或 ID 作为所有已分配的 ID 和句柄的前缀,并且在客户端上递增它们,以便它们在默认情况下是全局唯一的。

Read

读取操作没有数据冲突,因为读取操作按照定义是只读的。但是,对于偶尔连接的智能客户端中的读取操作而言,仍有可能发生问题。在客户端脱机之前,您应该在客户端上缓存所有需要读取的数据。该数据在客户端重新联机之前可能变为陈旧数据,从而导致客户端上出现不准确的数据,并且在与服务器进行同步时产生问题。有关处理陈旧数据的详细信息,请参阅本章前面的“处理陈旧数据”。

Update

数据更新最有可能导致数据冲突,因为多个用户可能更新相同的数据,从而在合并复制发生时导致冲突。您可以使用多种方法将发生冲突的可能性将至最低,并且在冲突确实发生时解决它们。有关详细信息,请参阅本章前面的“管理数据和业务规则冲突”。

Delete

删除记录是非常简单的,因为记录只能删除一次。尝试删除同一记录两次不会对系统产生任何影响。但是,在设计应用程序和 Web 服务以处理删除时,您应该记住几点。首先,您应该在客户端上将相关记录标记为暂时删除,然后在服务器上将删除请求排队。这意味着如果服务器由于某种原因无法删除该记录,则可以在客户端上撤消删除。

与创建记录时一样,您还必须通过使用唯一标识符来确保您引用了相关记录。这可以保证您总是在服务器上删除了正确的记录。

返回页首返回页首

使用基于任务的方法

基于任务的方法使用对象将工作单元封装为用户任务。Task 对象负责处理用户完成特定任务所需的状态、服务和用户界面交互。当您设计和生成支持脱机操作的智能客户端应用程序时,基于任务的方法尤其有用,因为它使您可以将脱机行为的细节封装在单个位置。这使用户界面可以专门致力于解决与 UI 有关的问题,而不是致力于解决处理逻辑。通常,单个 Task 对象封装(由用户)与单个独立工作单元相关联的功能。任务的粒度和详细信息将取决于确切的应用程序方案。任务的一些示例包括:

输入订单信息。

对客户联系人详细信息进行更改。

撰写并发送电子邮件。

更新订单状态。

对于上述每个任务,都将实例化一个 Task 对象并使用它来指导用户完成该过程,存储所有需要的状态,与用户界面交互,并且与任何需要的服务交互。

当应用程序脱机工作时,它需要将服务请求排队,并且可能使用暂定值或未确认的值进行本地状态更改。在同步过程中,应用程序需要执行实际的服务请求,并且可能进行更多的本地状态更改以确认服务请求的成功。通过将该过程的细节封装在单个 Task 对象(该对象将服务请求放入队列中,并且跟踪暂定的和已确认的状态更改)中,您可以简化应用程序的开发,隔离实现更改,并且能够以标准方式处理所有任务。Task 对象可以通过各种属性和事件提供有关任务状态的详细信息,包括:

Pending status。指示该任务是挂起的同步。

Confirmed status。指示该任务已经同步并且确认为成功。

Conflict status。指示在同步过程中发生了错误。其他属性将产生冲突或错误的详细信息。

Completed。指示完成的百分比或者将该任务标志为已完成。

Task availability。当应用程序联机或脱机时,某些任务将不可用;或者,如果任务是某个工作流或用户界面过程的一部分,则在已经完成作为先决条件的任务之前,该任务可能不可用。可以将该属性绑定到菜单项或工具栏按钮的启用标志,以防止用户启动不适当的任务。

基于任务的方法的另一项好处是:它使应用程序可以致力于满足用户及其任务的需要,从而可以产生更为直观的应用程序。

返回页首返回页首

处理依赖性

如果用户任务涉及到一个以上的服务请求,则需要非常小心地处理该任务,以便用户可以在脱机时完成整个任务。困难在于服务请求通常是相互依赖的。例如,假设您具有一个允许为客户预订假期的应用程序。为了预订假期,应用程序使用一些服务,按以下顺序执行整个任务的各个部分:

1.

预订汽车。

2.

预订旅馆房间。

3.

购买飞机票。

4.

发送电子邮件确认。

上述每个服务都可能由不同的系统(甚至可能由不同的公司)实现。在理想情况下,每个服务请求每次都能成功,以便您的用户可以成功地预订汽车、旅馆和飞机票,并且应用程序可以发送电子邮件以通知客户端预订了假期。但是,并非所有服务请求都是成功的,因此您的应用程序必须能够解决错误条件,并且管理能够影响它处理整个任务的方式的业务规则。为这种任务编写代码极为困难,因为该任务的各个部分(即,对特定服务的各个服务请求)都依赖于该任务的其他部分。

依赖性本身可能依赖于复杂的业务逻辑,这使影响整个任务的逻辑进一步复杂化。例如,您的假期预订应用程序可能允许在没有汽车的情况下预订假期,前提是成功预订了旅馆和航班。各个服务请求之间的依赖性可以是正向 和反向 依赖性:

正向依赖性。在同步过程中,如果第一个请求成功,但随后的请求失败,则您可能需要通过补偿事务反向第一个请求。这一要求可能大大增加应用程序的复杂性。

反向依赖性。如果应用程序正在脱机工作,并且将一个服务请求作为多服务请求任务的一部分提交,则它必须假设该请求将成功完成,以便它可以将后续的请求排队,并且不会妨碍用户完成该任务。在此情况下,所有后续请求都依赖于第一个请求的成功。如果第一个请求在同步过程中失败,则应用程序必须知道所有后续请求都需要删除或忽略。

在服务器处理依赖性

要降低与服务请求之间的依赖性相关联的复杂性,Web 服务应该为每个用户任务提供单个服务请求。这就使用户可以把将要在同步阶段处理的任务作为对 Web 服务的单个原子请求来完成。单个原子请求消除了跟踪服务请求依赖性(这可能使应用程序的客户端或服务器端实现大大复杂化)的需要。

例如,您可以不像下面这样将服务接口编写为三个单独的步骤:

BookCar() 
BookHotel() 
BookAirlineTickets() 

而是将它们合并为一个步骤:

BookVacation( Car car, Hotel hotel, Tickets airlineTickets ) 

以这种方式合并步骤意味着,就客户端而言,您现在拥有了一个原子交互而不是三个单独的交互。在该示例中,BookVacation Web 服务将负责在构成该服务的元素之间执行必要的协调。

在客户端处理依赖性

您还可以在客户端上跟踪服务请求依赖性。该方法提供了相当大的灵活性,并且使客户端可以控制任意数量服务之间的协调。但是,该方法难以开发和测试。基于任务的方法是在客户端上跟踪服务请求依赖性的好方法,并且提供了一种在一个位置封装所需的全部业务逻辑和错误处理的方法,从而简化了开发和测试。(有关基于任务的方法的详细信息,请参阅本章前面的“使用基于任务的方法”。)

例如,用于预订假期的 Task 对象将知道它必须执行三个服务请求。它将实现必要的业务逻辑,以便它可以在遇到错误条件时适当地控制服务请求。如果 BookCar 服务调用失败,它可以通过 BookHotelBookAirlineTickets 服务调用继续工作。如果 BookAirlineTickets 服务调用失败,则它将负责通过向每个服务创建一个补偿事务服务请求来取消任何旅馆或汽车预订。图 4.2 阐明了这一基于任务的方法。


4.2 带有相互依赖性服务的基于任务的方法

使用编排中间件

有时候,应用程序中的依赖性和相应的业务规则十分复杂,需要某种形式的编排中间件(如 Microsoft BizTalk? Server)来协调多个 Web 服务和一个客户端应用程序之间的交互。编排中间件位于中间层,并且提供了一个表面 Web 服务来与智能客户端交互。表面 Web 服务向客户端呈现了一个特定于应用程序的适当接口,通过该接口可以只为每个用户任务产生单个 Web 请求。当收到服务请求时,编排服务将通过启动并协调对必要 Web 服务的调用(可能首先对结果进行整合,然后再将其返回到客户端)来处理该请求。该方法提供了一种用于解决多个 Web 服务之间交互的、可伸缩性更高的方法。BizTalk 还提供了重要的服务,如数据转换和业务规则引擎,这些服务可在与截然不同的 Web 服务或旧式系统交互时提供极大的帮助,还可以在复杂的业务情况下提供极大的帮助。另外,该方法还提供了重要的可用性和可靠性保证,从而有助于确保多个服务之间的一致性。图 4.3 阐明了编排中间件的用法。


4.3 用于协调服务依赖性的编排中间件

返回页首返回页首

小结

智能客户端需要在连接到网络以及从网络断开连接时有效地工作。当您设计智能客户端时,您需要确保它们可以在这两种情况下有效地工作,并且能够在这两种情况之间无缝地转换。

有两种用于设计智能客户端通讯的概括性的策略:面向服务的策略和以数据为中心的策略。当您已经确定使用哪种策略之后,您需要进行一些基本的设计决策,以便使您的智能客户端能够脱机工作。在大多数情况下,应该将客户端设计为使用异步通讯和简单的网络交互。客户端需要缓存数据以便在脱机时使用,而且您需要一种在客户端重新联机后处理数据和业务规则冲突的方法。在许多情况下,脱机客户端允许用户执行一些相互依赖的任务。您需要处理这些依赖性,以防其中某个任务到达服务器时发生失败。您的智能客户端还可能需要与类似于 CRUD 的 Web 服务交互。

基于任务的方法可以显著简化将应用程序脱机的过程。请考虑在您的智能客户端中实现该方法;该方法还可以为您提供一种(在服务器和在客户端)处理依赖性的有效方法。

转到原英文页面

posted on 2006-02-10 14:53 TrampEagle 阅读(399) 评论(0)  编辑  收藏 所属分类: 技术文摘

只有注册用户登录后才能发表评论。


网站导航: