赞
踩
服务设计系列的法则已经发展到最佳通信实践和取样相关编码的程度。本文提供了设计和实现网络服务的基本原理,并且对面向服务的体系结构(SOA)的相关概念做了一个简要的回顾,以及有关于几种模式和反模式的详细讨论,当构建网络服务时,开发者可以利用它们。它可应用于能进行网络服务开发和配置的任何编程语言或平台。
关于SOA设计原理系列
这是一系列研究设计更有效的网络服务为目标的论文集的第一步。这第一篇论文是在微软的模式与实践小组的协助下完成的,以后的论文也可能会包含与其他组或个人的协作成果,不仅限于微软内部小组或个人。
读者应该记住关于样本代码的警告,这些代码只是为了SOA设计原理系列开发的,它们仅用作举例说明目的。建议读者学习这些样本代码,而不应该试图将这其中的任何代码用于实际的开发环境。如果读者决定将样本代码用到实际开发环境中,微软公司不承担任何可能由此引发的损失。
样本代码的系统配置要求:
如果发生不支持样本代码的情况,本文的作者期待你的反馈。
注意:SOA不断地迅速发展,在这系列中的论文和样本代码也许会被修正,使其更加成熟。
面向服务的体系结构(SOA)简介
SOA已经变成一个众所周知的缩写词,如果你问其他两人关于SOA的定义,你极有可能会得到两个完全不同,甚至可能相互矛盾的答案。一些人将SOA描述成了一个业务上的IT行业基础或底层结构,其他人则希望SOA能提高IT行业的效率。在许多方面,SOA有点象Godfrey Saxe写的盲人和大象的故事,每个人都各有不同地描述大象,因为他们中的每个人都被他们的个人经历所左右(比如,摸大象鼻子的人相信大象是一条蛇,而摸大象长牙的人则认为大象是一杆矛),Saxe的大象太容易描述了,因为它就是一个存在的实体。然而,SOA是很难去描述的,因为设计理论无法作为一个实体显示出来。
SOA是一种创建那些从自治服务中构建出的系统的途径,它使得整合变得可预见性而不是在事后进行计划,最终的解决方法很可能就是组合由不同语言开发的各种服务,这些服务以多种安全模式和业务流程运行在不同的平台。这个概念听起来是难以置信的复杂,但它已经不是新的了,一些人也许会认为,SOA是从那些基于已有技术的分布式系统的设计和开发中发展而来,有许多与SOA相关的概念,比如服务、发现以及后来的绑定,也与CORBA和DCOM密切相关。同样地,一些服务设计原理也很大程度上与早期的基于封装、抽象和接口的OOA/OOD技术有共同点。
SOA也提示了一个很明显的问题——严格地来讲,服务是什么?简单来说,一个服务是一段程序,能通过定义完整的信息交换机制来进行互动,服务必须设计得可用而且稳定,当服务配置等为了改变而重新构建时,服务也要随之构建并持续下去。灵活性经常被视为SOA带来的最大的利益之一,举个例子,一个机构的业务流程如果在一个松耦合的底层结构上运行,那么它肯定比那些束缚于潜在的单一的应用软件的机构组织更开放更能适应变化,因为后者需要几周时间去改变以适应最小的变化。松耦合的系统导致了松耦合的业务流程,因为业务流程不再受制于潜在底层结构的限制。服务以及相关的接口必须保持稳定,而且使它们可以被重新设置、整合以满足业务上的不停的变化。服务通过标准的接口和定义明确的消息来维持稳定,换句话说,SOAP和XML schemas用作消息的定义。如果服务被设计成仅仅知道信息如何传递或得到,而且只执行简单的粒度较小的函数,那么它极有可能被一个更大SOA底层结构所重用。正如早前所提到的,回想OO设计原理中的封装和接口设计会有助于我们设计和构建重用的网络服务。通过进一步理解和掌握面向服务的四个原则,我们能将OO原理扩展到世界上所有的网络服务。
原则1:明确清楚的分界线
服务通过在明确定义的分界点上的消息传递而相互作用,跨区域层次的服务可能因为地理位置、信任因素或者其他执行要素而花费很大的代价。一个分界点就是在一个服务的公共接口和它内部的执行之间的边界,一个服务的分界点通过WSDL来发布的,而且也许包含了已有服务的期望声明。跨区域的服务因为下面几点原因而被认为是一项昂贵的任务:
面向服务集成模式告诉我们,广域服务受限于网络的潜伏因素,网络故障和分布式系统故障,但是一个本地服务不会出现这样的问题。大量的错误检测和改正方案必须写出以处理使用远程对象接口带来的问题。我们假定跨区域是花费很高的,但也必须处理一些本地方法的配置中出现的警告,这些本地方法的作用就是使得跨区域的可能减到最小。一个只有单一本地方法和对象的系统也许会获得更高的性能,但是无法重用已经定义的服务(这种技术可比喻成OOP中的拷贝和粘贴,它在服务的版本上也遇到一样的问题)。
关于SO的第一个原则,还有几点应该记住的:
原则2:服务是自治的
服务是实体,它们独立地配置、更新和管理。开发者不应该对于服务边界之间的空间做出假设,因为这些空间会比服务边界本身变得还快。比如,服务边界应该是静态的,将减小版本的更新给用户带来的影响。服务的边界一般都是稳定的,而关于策略、地理位置或网络技术等服务配置选项则会经常地变化。
服务可以通过URI动态地设定地址,这使得地理位置、配置技术的改变或者不断地发展变化对服务本身只有很小的影响(这就好比服务的通道)。这些变化对服务也许只能造成很小影响,但它们能会对基于这些服务的应用造成破坏性的作用。假如你当前正在使用的一个服务明天将被迁移到新西兰的一个网络中时,该怎么办?这种响应时间的变化也许会对用户造成不可预期或不可意料的影响。对于服务如何被使用,设计者们应该持一种非乐观的态度,比如服务失败、服务相关的行为(服务级)要面对变化等。异常处理和补偿措施的适当引入必须与任何一个服务点相关。除此之外,用户也需要去改变以适应所使用服务的最小响应时间,比如,用户需要有关于安全、性能、事务处理以及其他因素的不同级别的服务。一个灵活的策略可以使一个单一的服务支持SLAs(其他的策略也许关注于版本、定位或其他方面)。在不同级别服务上的通信性能期望都是保守估计,因为一个服务没必要知道其他服务的内部实现。
不仅仅是用户需要以非乐观的态度来面对服务的运行,而且服务提供商也是如此。有时不用服务本身来通报,用户应该对服务失败这种情形有所预料。服务提供商也不能保证用户按照正常过程使用服务,比如,用户可能试图使用恶意的信息去通信或者违反正常服务交互的规则,服务的内部也必须试图去纠正这些不适当的使用,不管用户是什么意图。
服务被设计成自治的,但也没有服务是孤立的。一个基于SOA的解决方案还未成型,它其中包括了许多为一个具体方案而配置的服务。可以想到的是,在面向服务的环境中还没有可以用作指导的权威著作——一个管弦乐队指挥者的观念是不完善的(也就进一步意味着“回滚”的概念也是有缺陷的——这也是在后面论文中的议题之一)。实现自治服务的关键在于隔绝和退耦,所设计的服务相互独立地进行配置,并且只能用协议约定的信息和规范来进行通信。
正如其他的服务设计的原则方法,我们能从过去OO设计的经验中学到很多。Peter Herzum和Oliver Sims在业务构件制造上(Business Component Factories)的工作提供了一些有趣的关于自治构件性质的见识。当他们的大部分工作与基于构件的解决方案相吻合时,基本的设计原则也仍然适用于服务设计。
考虑到这些因素,这里有一些简单的设计要求来保证与SOA的第二原则相吻合:在配置和使用这些服务的系统中,服务应该被相互独立地配置和更新。协约应该根据“一旦被发布即不能更改”的假定来设计,这使得开发者不得不在他们的计划设计中考虑灵活性。考虑服务所面临的最坏情况,并加以解决。从用户的角度来讲,在服务的可用性和性能方面应该所有考虑或计划,从服务提供商的角度,则应该考虑到对服务的(故意)不适当使用,以及有可能出现的服务失败等情况。
原则3:服务共享模式和协约,不是类
如先前提到的,服务的交互应该建立在服务的策略、模型和基于协约的行为上。服务的协约一般都由WSDL来定义,服务集合的协约能用BPEL来定义说明(也就是说,BPEL使用WSDL来定义集合中每个服务的协约)。
在一个问题领域内,大部分开发者都定义类来代表各种实体(比如,顾客、订单、产品),类把数据(消息)和对数据的操作结合到一个单独的编程语言或具体平台构造中。服务则打破了这种模型结构以求最大的灵活性和互动性,使用基于XML schema的消息来进行服务通信的方式对于编程语言和平台是不明确的,它只能确保服务边界之间的互动性,Schema定义了消息的结构和内容,服务的协约则定义服务本身的行为模式。
总之,一个服务的协约包含了下列元素:
用户将依靠服务的协约去调用服务并与之交互,考虑到这方面的可靠性,服务的协约必须能保持稳定。服务协约利用XML Schema(xsd : any)的扩展性质和SOAP过程模型(可选择报头),应该尽可能清楚地设计出来。
第三原则的最大挑战就是它的性能。一旦服务的协约被公布,那么它将很难修改,因为要减小对现有用户的影响。位于内部与外部数据之间的临界线对于服务是否能成功配置和重用是十分关键的。公共数据(传递于服务之间的数据)应该基于统一的标准,以确保跨服务的访问,而私有数据(服务内的数据)被封装在服务内。在很多方面,服务像是一个运行电子商务组织的较小个体。就像一个组织必须将外部的订单转换成内部订单格式那样,一个服务也必须将协约规范的数据转换成其内部格式。我们在面向对象的数据封装方面的知识说明了一个相似的概念——服务的内部数据只能通过服务的协约来操作。Pat Helland在Data on the Outside vs. Data on the Inside””中阐述了几个关于公共和私有数据的论题。
考虑到这些因素,这里有一些简单的设计要求来保证与SOA的第三原则相吻合:
原则4:基于策略的服务兼容
这个原则经常被考虑得最少,而它又或许是关于实现灵活网络服务的最重要因素之一。只通过WSDL来通信服务互动的一些需求是不可能的,策略表达式可以从语义兼容(消息如何被传递或者传递给谁)中分离出结构兼容(什么被通信)。
操作要求能在机器可读的策略表达式中清楚阐述,策略表达式包含了一套能共同操作的语义来管理服务的行为。WS_Policy规范定义了机器可读策略框架,它能表达服务级别的策略、并在执行时间找到它们执行,比如,一个国家安全服务也许需要一个策略去强化某个具体服务级别(举个例子,已满足指定标准的护照照片必须再被恐怖分子识别系统再次检查)。与服务相关的策略信息能被很多其他实施不同检查点的服务所重用,网络服务策略不需要增加一行其他的代码就能加强这些需求,这一节也说明了策略框架如何提供其他的有关于服务需求方面的信息,也列举了用于服务定义和执行的一些已公布的编程模型。
一个策略的断言定义了一种行为,这种行为就是一个策略的必要条件。(在上面一节中这个断言就是恐怖分子识别系统的检查)。断言中有具体领域的语义,并最终在相互无影响的应用于各种行业的具体领域的详细说明中定义(建立WS-Policy框架概念)。
策略驱动的服务依然在不停地发展,开发者也应该保证他们的策略断言在服务期望和服务语义兼容上尽可能的清晰。
模式与反模式
既然你已经对SOA概念有了初步的了解(包括SO设计原则),那现在就开始应用我们所学到的,这篇论文的下面部分介绍了两种反模式和三种模式,每种反模式和模式都是基于先前讨论的概念来设计的。
为什么有模式和反模式?
人们趋向于在模式中思考和交流。Christopher Alexander已经写了几本关于模式语言的书,他定义模式是从一个具体中来的一个抽象,并且在具体的上下文中保持重现。模式和模式语言用来描述实践、设计证明和获取供别人学习的经验,模式是一条途径去迅速理解应用这些模式的设计指导方针和各种上下文,而对于反模式,顾名思义,与模式相反。在模式提供指导和实践时的地方,反模式则说明了公共设计缺陷,并且作为一种从其他错误中学习的方法,这篇论文的剩下部分介绍的所有模式与反模式能指导我们开发更有效的网络服务。
这篇论文中的反模式与模式都将使用下列格式:
反模式中有如何证明相关设计缺陷的附加建议。
代码样本
反模式#1:CRUD(Create、Read、Update、Delete)接口
列表1:简单的VB.Net CRUD服务
<WebMethod()> _ <WebMethod()> _ <WebMethod()> _ <WebMethod()> _ <WebMethod()> _ |
以下列方式改变服务能避免上面列举的这些风险条目:
反模式#2:Loosey Goosey
列表2. 简单的VB.NET Data Tier服务
<WebMethod()> _ <WebMethod()> _ |
以下列方式改变服务能避免上面列举的这些风险条目:
模式#1:文档处理器(Document Processor)
这个模式的样本代码可以下载获得
列表3. Sample C# Document Processor Serivce
[WebMethod()] public FindCustomerByCountryResponse FindCustomersByCountry( |
定义使用简单模式的服务与SO设计原则相一致:
- 将一个以文档为中心的网络服务映射到一个业务过程是比较容易的,因为用户倾向于将业务过程视为发送和接受文档的思维方式(注意,那些文档也许代表着业务事件和不真实的业务文档)。
- 服务边界作为一条临界线来实现公共数据和私有数据之间的转换。
- 服务的实现细节对用户应该是不可见的。
- 采用协约优先的方法确保服务有很高的互操作性。
- 以文档中心的服务更容易得到发展,因为所有的互操作都是通过消息而不是实际代码的RPC来实现的。
这种实际做法中还有一些次要的东西需要注意:
模式#2:多重复消息(Idempotent Message)
这个模式的样本代码可以下载获得
图 1:多重复消息模式
多重复消息是一个较难的论题,处理重复UOW IDs的三个选项中每一个都有几个考虑因素:
- 返回缓存副本——这个选项需要服务在一定的时间内保留一个缓存响应,决定时间值或缓存刷新应该被相关的业务过程所决定。这里还有一些其他的因素要考虑:
- ?当前值不同于缓存中的值吗?
- ?如果响应是错误的,怎么办?
- ?如果用户重用UOW IDs去做不相关的工作,怎么办?
- 再次发送消息——对于简单的读操作,这是无害的。但是对于写操作,这无疑是可怕的(比如,付清帐单)。如果第一次处理UOW ID就发生了错误了,怎么办?再处理一次可能导致相同的错误(这有时就是一个有害的消息循环)
- 抛出异常——用户也许没有收到服务的第一次响应并且简单地再次重发了同一个UOW ID(可能由于一个响应超时了)。如果最后一次发送也丢失了,用户将如何接收响应呢?
UOW ID应该作为响应模式的一部分,将重复请求的处理与相关业务过程联系起来,UOW ID也可被加入成一个自定义的SOAP报头,使得重复请求的处理成为整个消息处理基础的一部分,URI应该被包含以帮助探测重复的进入,一系列的修正措施也应该被自动保留,使修正跟踪能返回给UOW ID。最后,关于缓存刷新的论题,在响应被接收的时候反映响应时可被进一步讨论,缓存管理是一个另外的难题,并且已经超出了本论文的范围。
支持多重复消息机制将提高服务的自治性(第一原则),因为服务不再依靠用户是否做正确的事情,还有一些方面需要注意的:
模式#3:预留(Reservation)
这个模式的样本代码可以下载获得。
图2 预留模式
预留ID和时间终止戳应该是验证请求模式的一部分,将预留过程与实际业务过程联系起来。对于每个预留请求,服务为之产生预留IDs又生成时间终止戳,使服务能周期性地检查和终止不确定的预留,这种模式可以与多消息模式相结合,并进一步使服务与重复预留请求相隔离。
还有一些需要注意的方面:
总结
SO的四个原则提供了一系列基本的规则,它们能引导服务发展的方向,这篇论文所列举的模式和反模式都用来说明这些原则如何对服务设计产生的影响,我们也提供一些额外的指导来确保你们将来的服务设计和发展方向将会取得成功:
还有许多其他的设计理念与网络服务相关,这系列中以后的论文将会阐述有关于版本更新、服务因素和策略驱动的服务配置等方面。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。