软件架构Word文档下载推荐.docx
《软件架构Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《软件架构Word文档下载推荐.docx(46页珍藏版)》请在冰豆网上搜索。
服务以及相关的接口必须保持稳定,而且使它们可以被重新设置、整合以满足业务上的不停的变化。
服务通过标准的接口和定义明确的消息来维持稳定,换句话说,SOAP和XMLschemas用作消息的定义。
如果服务被设计成仅仅知道信息如何传递或得到,而且只执行简单的粒度较小的函数,那么它极有可能被一个更大SOA底层结构所重用。
正如早前所提到的,回想OO设计原理中的封装和接口设计会有助于我们设计和构建重用的网络服务。
通过进一步理解和掌握面向服务的四个原则,我们能将OO原理扩展到世界上所有的网络服务。
原则1:
明确清楚的分界线
服务通过在明确定义的分界点上的消息传递而相互作用,跨区域层次的服务可能因为地理位置、信任因素或者其他执行要素而花费很大的代价。
一个分界点就是在一个服务的公共接口和它内部的执行之间的边界,一个服务的分界点通过WSDL来发布的,而且也许包含了已有服务的期望声明。
跨区域的服务因为下面几点原因而被认为是一项昂贵的任务:
•目标服务的物理位置是个不可知因素;
•安全以及信任模式一般在跨越每个分界点时会发生变化;
•在一个服务的公共和私有部分之间,数据的编组和广播需要对于其他资源的信任,对于服务本身来说,其中的某些资源是外部
不可见的。
•当服务配置被构建以适应改变时,服务也被构建以持续下去。
这意味着,由于网络的重配置或者迁往其他物理位置时,一个原
本可靠的服务而突然发生改变甚至退化。
•用户一般都不知道内部流程实现的保密程度。
一个用户只能有限地控制所指定服务的运行。
面向服务集成模式告诉我们,广域服务受限于网络的潜伏因素,网络故障和分布式系统故障,但是一个本地服务不会出现这样的问题。
大量的错误检测和改正方案必须写出以处理使用远程对象接口带来的问题。
我们假定跨区域是花费很高的,但也必须处理一些本地方法的配置中出现的警告,这些本地方法的作用就是使得跨区域的可能减到最小。
一个只有单一本地方法和对象的系统也许会获得更高的性能,但是无法重用已经定义的服务(这种技术可比喻成OOP中的拷贝和粘贴,它在服务的版本上也遇到一样的问题)。
关于SOA的第一个原则,还有几点应该记住的:
清楚你的边界点。
服务有一个协约来定义它所提供的公共接口,服务的所有交互都通过公共接口发生,这个接口包括了公有流
程和公有数据形参,公共流程是进入服务的入口,而公有数据形参则代表了流程使用的信息。
如果我们使用WSDL来表示一个
简单的协约,那么<
message>
表示公共数据,而<
portType>
则表示公有流程。
《DataontheOutsidevs.DataontheInside》这篇文
章更加详细地调研了这些论点。
服务应该易用。
当设计一个服务时,开发者必须保证其他开发者很容易地使用它。
服务的接口(协约)也应该被设计得不用破坏
本身便可进一步扩展。
(这个主题将在此系列后面的论文中更详细地讲解。
)
避免RPC接口。
在类似RPC的模型中,外部信息传递应该是最主要的,它隔绝了用户与服务的内部实现,使服务开发者去进一
步发展他们的服务,尽量减少服务用户之间的冲突(通过使用公有消息而不是公有方法来进行封装)。
保持服务接口区域小。
一个服务提供的公共接口越多,这个服务也就越难被使用和维持,所以,应该提供很小的定义明确的接口
在你的服务中,这些接口应该相对简单,只是接收定义明确的输入消息以及返回定义明确的输出消息,一旦这些接口被设计完成,
它们应该是静态的,作为服务的内部实现的对外接口,这些接口满足服务所必须支持的恒定的设计需求。
内部实现细节不应该泄漏到服务分界点外,否则极有可能导致服务与用户之间更紧密的耦合。
服务的内部实现应该屏蔽,因为它
包括了版本和服务更新的选择。
这篇文章的反模式部分列举了此类问题的详细例子。
原则2:
服务是自治的
服务是实体,它们独立地配置、更新和管理。
开发者不应该对于服务边界之间的空间做出假设,因为这些空间会比服务边界本身变得还快。
比如,服务边界应该是静态的,将减小版本的更新给用户带来的影响。
服务的边界一般都是稳定的,而关于策略、地理位置或网络技术等服务配置选项则会经常地变化。
服务可以通过URI动态地设定地址,这使得地理位置、配置技术的改变或者不断地发展变化对服务本身只有很小的影响(这就好比服务的通道)。
这些变化对服务也许只能造成很小影响,但它们能会对基于这些服务的应用造成破坏性的作用。
假如你当前正在使用的一个服务明天将被迁移到新西兰的一个网络中时,该怎么办?
这种响应时间的变化也许会对用户造成不可预期或不可意料的影响。
对于服务如何被使用,设计者们应该持一种非乐观的态度,比如服务失败、服务相关的行为(服务级)要面对变化等。
异常处理和补偿措施的适当引入必须与任何一个服务点相关。
除此之外,用户也需要去改变以适应所使用服务的最小响应时间,比如,用户需要有关于安全、性能、事务处理以及其他因素的不同级别的服务。
一个灵活的策略可以使一个单一的服务支持SLAs(其他的策略也许关注于版本、定位或其他方面)。
在不同级别服务上的通信性能期望都是保守估计,因为一个服务没必要知道其他服务的内部实现。
不仅仅是用户需要以非乐观的态度来面对服务的运行,而且服务提供商也是如此。
有时不用服务本身来通报,用户应该对服务失败这种情形有所预料。
服务提供商也不能保证用户按照正常过程使用服务,比如,用户可能试图使用恶意的信息去通信或者违反正常服务交互的规则,服务的内部也必须试图去纠正这些不适当的使用,不管用户是什么意图。
服务被设计成自治的,但也没有服务是孤立的。
一个基于SOA的解决方案还未成型,它其中包括了许多为一个具体方案而配置的服务。
可以想到的是,在面向服务的环境中还没有可以用作指导的权威著作——一个管弦乐队指挥者的观念是不完善的(也就进一步意味着“回滚”的概念也是有缺陷的——这也是在后面论文中的议题之一)。
实现自治服务的关键在于隔绝和退耦,所设计的服务相互独立地进行配置,并且只能用协议约定的信息和规范来进行通信。
正如其他的服务设计的原则方法,我们能从过去OO设计的经验中学到很多。
PeterHerzum和OliverSims在业务构件制造上(BusinessComponentFactories)的工作提供了一些有趣的关于自治构件性质的见识。
当他们的大部分工作与基于构件的解决方案相吻合时,基本的设计原则也仍然适用于服务设计。
考虑到这些因素,这里有一些简单的设计要求来保证与SOA的第二原则相吻合:
在配置和使用这些服务的系统中,服务应该被相互独立地配置和更新。
协约应该根据“一旦被发布即不能更改”的假定来设计,这使得开发者不得不在他们的计划设计中考虑灵活性。
考虑服务所面临的最坏情况,并加以解决。
从用户的角度来讲,在服务的可用性和性能方面应该所有考虑或计划,从服务提供商的角度,则应该考虑到对服务的(故意)不适当使用,以及有可能出现的服务失败等情况。
原则3:
服务共享模式和协约,不是类
如先前提到的,服务的交互应该建立在服务的策略、模型和基于协约的行为上。
服务的协约一般都由WSDL来定义,服务集合的协约能用BPEL来定义说明(也就是说,BPEL使用WSDL来定义集合中每个服务的协约)。
在一个问题领域内,大部分开发者都定义类来代表各种实体(比如,顾客、订单、产品),类把数据(消息)和对数据的操作结合到一个单独的编程语言或具体平台构造中。
服务则打破了这种模型结构以求最大的灵活性和互动性,使用基于XMLschema的消息来进行服务通信的方式对于编程语言和平台是不明确的,它只能确保服务边界之间的互动性,Schema定义了消息的结构和内容,服务的协约则定义服务本身的行为模式。
总之,一个服务的协约包含了下列元素:
XMLSchema定义的消息相互交换格式
WSDL定义的消息交换模式
WS-Policy定义的功能需求
BPEL也能被用作业务流程级别的协约以集合多个服务
用户将依靠服务的协约去调用服务并与之交互,考虑到这方面的可靠性,服务的协约必须能保持稳定。
服务协约利用XMLSchema(xsd:
any)的扩展性质和SOAP过程模型(可选择报头),应该尽可能清楚地设计出来。
第三原则的最大挑战就是它的性能。
一旦服务的协约被公布,那么它将很难修改,因为要减小对现有用户的影响。
位于内部与外部数据之间的临界线对于服务是否能成功配置和重用是十分关键的。
公共数据(传递于服务之间的数据)应该基于统一的标准,以确保跨服务的访问,而私有数据(服务内的数据)被封装在服务内。
在很多方面,服务像是一个运行电子商务组织的较小个体。
就像一个组织必须将外部的订单转换成内部订单格式那样,一个服务也必须将协约规范的数据转换成其内部格式。
我们在面向对象的数据封装方面的知识说明了一个相似的概念——服务的内部数据只能通过服务的协约来操作。
PatHelland在DataontheOutsidevs.DataontheInside””中阐述了几个关于公共和私有数据的论题。
考虑到这些因素,这里有一些简单的设计要求来保证与SOA的第三原则相吻合:
确保服务协约的稳定,以减小对用户的影响。
在某种意义来讲,协约意味着公共数据表示(数据)、消息交换模式(WSDL)
和可配置的性能和服务级别(策略)
为了尽可能减小误解,协约必须定义清楚明确。
除此之外,它也能通过XML语法和SOAP过程模型的扩展性,来兼容以后
新版本的服务。
避免在公共和私有数据表示之间的模凌两可的状态。
服务的内部数据格式应该对用户隐藏,而它的公共数据模式是不变的(更
适宜基于一个统一的实际行业标准)。
当对于服务协约的更改不可避免时,要及时更新服务。
这样能减小现有用户执行所带来的破坏。
原则4:
基于策略的服务兼容
这个原则经常被考虑得最少,而它又或许是关于实现灵活网络服务的最重要因素之一。
只通过WSDL来通信服务互动的一些需求是不可能的,策略表达式可以从语义兼容(消息如何被传递或者传递给谁)中分离出结构兼容(什么被通信)。
操作要求能在机器可读的策略表达式中清楚阐述,策略表达式包含了一套能共同操作的语义来管理服务的行为。
WS_Policy规范定义了机器可读策略框架,它能表达服务级别的策略、并在执行时间找到它们执行,比如,一个国家安全服务也许需要一个策略去强化某个具体服务级别(举个例子,已满足指定标准的护照照片必须再被恐怖分子识别系统再次检查)。
与服务相关的策略信息能被很多其他实施不同检查点的服务所重用,网络服务策略不需要增加一行其他的代码就能加强这些需求,这一节也说明了策略框架如何提供其他的有关于服务需求方面的信息,也列举了用于服务定义和执行的一些已公布的编程模型。
一个策略的断言定义了一种行为,这种行为就是一个策略的必要条件。
(在上面一节中这个断言就是恐怖分子识别系统的检查)。
断言中有具体领域的语义,并最终在相互无影响的应用于各种行业的具体领域的详细说明中定义(建立WS-Policy框架概念)。
策略驱动的服务依然在不停地发展,开发者也应该保证他们的策略断言在服务期望和服务语义兼容上尽可能的清晰。
模式与反模式
既然你已经对SOA概念有了初步的了解(包括SO设计原则),那现在就开始应用我们所学到的,这篇论文的下面部分介绍了两种反模式和三种模式,每种反模式和模式都是基于先前讨论的概念来设计的。
为什么有模式和反模式?
人们趋向于在模式中思考和交流。
ChristopherAlexander已经写了几本关于模式语言的书,他定义模式是从一个具体中来的一个抽象,并且在具体的上下文中保持重现。
模式和模式语言用来描述实践、设计证明和获取供别人学习的经验,模式是一条途径去迅速理解应用这些模式的设计指导方针和各种上下文,而对于反模式,顾名思义,与模式相反。
在模式提供指导和实践时的地方,反模式则说明了公共设计缺陷,并且作为一种从其他错误中学习的方法,这篇论文的剩下部分介绍的所有模式与反模式能指导我们开发更有效的网络服务。
这篇论文中的反模式与模式都将使用下列格式:
上下文:
模式与反模式的简要背景描述。
有了上下文,读者才能去应用一个模式,以及在具体证明反模式之前就能识别一个
反模式的特征。
问题:
一个简单的声明。
被用来构成与模式或反模式相关的对象。
影响:
在应用已有模式和识别反模式时,一些额外的其他的因素也必须考虑进来。
解决方法:
已有反模式或者应用相关模式的必需步骤的详细描述。
征兆和结果:
反模式中,这些因素能生成反模式;
而在模式中,征兆和结果也许提到了其他因素和考虑点来应用相关模式。
反模式中有如何证明相关设计缺陷的附加建议。
代码样本
每个样本代码都被打包成安装文件(MSI)并且有README文件来说明如何安装和配置样本代码。
读者应该知道关于样本代码使用的警告。
样本代码只用作说明目的,读者可以学习,但是不应试图将任何样本代码应用到实
际应用环境中。
微软公司将不承担任何可能由此造成的损失。
反模式#1:
CRUD(Create、Read、Update、Delete)接口
要求你为新的企业SOA项目设计网络服务
你如何使用.Net来设计SOA服务
你是一个VB开发者并且知道如何创建构件
这是你的第一个SOA项目
其他平台的应用也能使用你的服务
就像设计以前的构件接口一样,来设计你的服务接口
创建一个服务,它能实现CRUD操作
列表1中列举了样本代码的一部分
列表1:
简单的VB.NetCRUD服务
<
WebMethod()>
_
PublicSubCreate(ByValCompanyNameAsString,ByVal
ContactNameAsString,ByValContactTitleAsString,
ByValAddressAsString,ByValCityAsString,ByVal
StateAsString,ByValZipAsString,ByValCountryAs
String,ByValTelephoneAsString,ByValFaxAsString)
PublicFunctionMoveNext()AsBoolean
EndFunction
PublicFunctionCurrent()AsObject
PublicSubUpdateContactName(ByValNewNameasString)
EndSub
PublicFunctionCommitChanges()
接口设计要有类似于RPC的行为、调用创建和移位等功能,而不是发送一个定义明确的用来指示采取某个行动的消息。
这违反了第一原则(良好的边界定义)和第三原则(只被模式共享)。
接口有可能交互频繁,因为用户也许需要去调用两三个方法来完成工作。
用一个提交来创建,当操作成功或失败时,用户可能不知怎么办。
设计服务时始终把用户的期望考虑在内——用户需要知道什么?
CRUD操作在网路服务的分解上是一个错误级别。
它也许在服务内或服务之间实现,但是不应该让用户直接接触。
这个例子就是说服务允许内部的实现出现在服务的公共接口中。
这个接口意味着正式的交互,比如列举(查看MoveNext和Current方法)
抽象类型(比如Current方法返回的对象)导致一个很脆弱的协约,这是例子违反了第三原则(只被模式共享)。
如果服务在不一致的状态下可以抛弃数据,那么服务是十分危险的。
如果用户增加一个新的访问(或更新已经存在的访问)并且没有调用CommitChanges方法时,将会发生什么?
正如先前提到的,服务提供商不能信任用户始终能做出正确的事情。
以下列方式改变服务能避免上面列举的这些风险条目:
用XMLschema改变服务接口的通信。
这个模式也包括了指定的服务行为(比如,NewContactSchema或者ChangeContactSchema)。
开发者应该优先考虑现有的行业标准而不是急于开发自己的模式,一个满足你需要的模式也许已经存在。
在私有方法后隐藏对数据的操作,只有使用公共接口的模式才能访问到。
确保用户收到一个包含请求状态信息的确认消息。
反模式#2:
LooseyGoosey
你能构建一个基于服务的解决方案
你的组织对服务重用很重视
如何使得服务接口有最大的灵活性和扩展性
你计划去提供在解决方案的各个层次上的结合点
其他组需要访问你的数据库
将服务接口设计成DataTiers(列表2中)
重视后期附加来设计高扩展性的接口
列表2.简单的VB.NETDataTier服务
PublicFunctionQueryDatabase(ByValDatabaseasString,
SQLQueryasstring)AsDataSet
PublicFunctionExecute(ByValCommandasInteger,
Argumentsasstring)AsBoolean
征兆和结果
事实上没有协约存在。
用户不知道如何使用服务(比如,什么是有效的命令、编码规则等)。
接口允许其所发生的错误。
协约既不清楚也具有很高的安全风险,很容易受SQL攻击的影响。
协约没有提供足够的信息让用户知道如何使用服务,如果为了让用户知道如何使用服务而让他必须看一些东西而不是服务的署名,那么应该要重新回顾一下服务的分解。
期望用户能够熟悉数据库和表结构而不是使用网络服务,这将导致服务提供商和用户之间更紧密的耦合。
由于依靠后期的附加绑定和同一服务内边界之间的编/解码,服务的运行性能将会很差。
用定义明确XMLschema改变服务接口的通信。
这个模式不应该透露任何有关于潜在的数据仓库的信息,这个模式的语义也应该能提供上下文给用户,使得他们能理解服务的目的。
(这个方法能提高服务的运行能力)
数据库交互应该被封装在私有方法中,不让用户知道数据库和与之相关的表的细节。
模式#1:
文档处理器(DocumentProcessor)
这个模式的样本代码可以下载获得
构建一个基于服务的解决方法
与SO设计原则相吻合
如何创建一个简单的定义明确的协约,并且与SO设计原则相吻合?
服务的协约应该支持以文档为中心的考虑。
协约应该明确定义服务的语义(避免LooseyGoosey反模式)。
协约应该在服务本身中封装其实现以支持松耦合(避免CRUD接口反模式)。
服务协约是是一个边界,它不应该泄漏任何有关内部实现的信息(记住第一原则)。
服务必须遵守WS-IBasicProfile,使得它能被任何支持网络服务的平台或编程语言使用。
服务应该作为一个完整单元的工作代表一个业务过程(避免一些假设,比如在CRUD接口反模式中)。
还是那句话,服务提供商不能信任用户能始终做出正确的事情,如果意外发生了,服务也不能依靠用户来调用其他的服务去恢复或维修它。
定义并重用一个XMLSchema来表示一个请求和响应消息,确保所有公共的互动使用这些模式。
(列表3中有一个样本代码片断)
直接从模式中产生对象以加快开发,这有时就是一种协约优先(Contact-First)的方法,你在开发实际的服务前定义服务的协约,此协约能被用来产生服务代码。
协约优先能减小互操作中的障碍,因为它建立在很多技术的经验之上(CORBA、COM和DCE都使用接口语言)。
网络服务有时采用协约优先的方法,因为SOAP经常不用WSDL来解决简单的问题。
不管怎样,许多开发环境提供了对协约优先的简单支持,许多工具,比如