分享

在面向服务的世界里开发软件

 bylele 2013-07-17

软件开发又一次走到了十字路口。分布而相互连接的系统已成为了开发中的常态, 以至于“应用”这个词汇都得重新定义。松耦合(loosely coupled)结构, 如面向服务的架构(Service-oriented Architectures -SOA)使得相互连接 的系统独立进行变化和演进成为可能。同时,模型驱动架构(Model-driven Architectures-MDA)致力于对系统进一步的抽象和大面积地代码自动生成 以简化软件开发。这让我们想探究这些因素对于在面向服务的世界里开发软件 意味着什么。难道这意味着业务分析师可以用图形工具把相关的软件部件连到 一起就了事吗?这意味着开发人员是用元-元-模型(meta-meta-model) 加上某个特定应用域的语言来编程吗?开发人员需要一些崭新的编程思路与 范式吗?我们如何准备好以迎接挑战?

目录

1 右击,生成Web Service
2 Web或是服务或是架构?
3 架构师的美梦,开发者的恶梦?
4 面向服务世界里人的因素
5 新工具
6 新的程序设计范式(New Programming Paradigms)
6.1 对象与文档间的映射(Object-Document Mapping)
6.2 宣示型程序设计(Declarative Programming)
6.3 基于事件的程序设计(Event-based Programming)
6.4 异乎寻常的异常处理(Exceptional Exception Handling)
6.5 图件(Doodleware)
7 结语
关于作者
参考资料

1 右击,生成Web Service

毫无疑问,Web Services 标准的出现使得原来很枯燥的编程变得非常容易并且 更有效率。例如,我最近开发了一个小应用程序,用来检索Amazon上面我的著作 的销售排行榜并把这个信息电邮到我的手机上。自然,这个应用程序需要通过 Internet到外面一个组织里去检索数据。开发这样一个程序却是再容易不过了, 我只需把我的IDE对准Amazon提供的WSDL,客户端的代理类代码就会自动生成。 我只要在代码中加入几行,我的程序就能启动运行了。这个应用不仅含有所有 的时髦的名词 - Web Services, XML, WSDL, SOAP,你还可以加上一些 - 并且它还是能做一些有用的事情(提升一个作者的虚荣心)。

同样地,把一个系统中存在的功能暴露成Web Services也已经变得简单。例如,让开发 人员指定一个功能调用(method),然后从弹出的菜单中选择“生成Web Service”, 许多开发工具离这个要求大致都不远了。另外,设想把我的这个小程序改进一下, 升级成一个公共的Web Service,让用户提交书号(ISBN)和电邮地址,这个服务 就会让用户定期收到销售排行数据。这个改进只需一些琐碎的工作,并不困难。

2 Web或是服务或是架构?

不过,就象一只燕子并不能创造夏天一样,建立一个服务并不意味着建立面向服务的架构。 我的简单应用程序是建立在统称之为Web Services的一套技术上,但是却几乎不能说这是 一个面向服务架构的例子。在我之前许多人已经讨论过Web Services(一组相关的技术的 集合)和面向服务的架构(一种架构风格)[1],所以我在此不重复。可是,让我们仔细 看看到底是什么让一个架构是面向服务的。

许多流行的架构风格似乎都是在克服其前任的缺点基础上而形成的。说到底,系统开发的风格 总是会演进的,今天的系统会是明天遗产。这同样适合于SOA。SOA后面一些最主要的 驱动因素是解决开发人员和架构师在前一个架构,即分布式部件架构(Distributed Component - DC)中遇到的问题。特别是如下问题:

  • 厂商锁定(vendor lock-in)。许多DC架构是基于私属(proprietary)协议和实现的。 与此相反,基于标准的协议支持(至少在理论上)能让不同制造商的产品之间实现互操作。
  • 紧耦合(tight coupling)。DC架构中,部件的连接一般都是直接的,这样使得系统很 脆性。而一个异步的(asynchronous)、面向文档的(document-oriented)的互动风格 能容忍部件间更多的独立变化空间。
  • 透明幻象(transparency illusion)。分布式部件给开发人员承诺隐藏远程通讯,使得 远程性变得“透明”。当然,从程序文本上看,远程交互可以由被包裹起来的代理对象 (proxy object)来进行,但结果是,对部分失败(partial failure)、响应时间、 远程异常(remote exception)的处理却不能对开发人员隐藏起来。其实,90%的透明 比没有透明更为糟糕,因为它带给开发人员的是虚假的舒适。
  • 复杂性。DC 架构在(单维)连线上实现分布对象间的交互,但这种交互是丰富(多维)而 复杂的。一个对象可以控制远程对象的生存时间,可以传递对象的指称(reference), 可以依靠多态性(polymorphism)和继承(inheritance)。分布式架构内在的复杂性 更增加了这种模型的复杂性。
  • 调用堆栈(call stack)。调用堆栈对传统应用程序是个极大的便利,但对于松耦合 的分布式应用来说,它可能很快就会成为一个麻烦。等待一个远程调用完成任务后回来 再继续往下执行会使得一个分布式架构系统响应性很差,又很脆性。
  • 连接性(connectivity)。许多DC架构都要求部件间有可靠的、永久性的连接。在宽域 (wide-area)、可间断的网络中,如Internet或移动(mobile)网络,这种系统并不能 很好地工作。

面向服务的途径是如何来解决这些问题的呢?它是使用了简化的、面向文档的交互模型, 该模型是基于技术中立(technology-neutral)和标准化的协议。在面向服务的架构下, 灵活的文档格式、明确的契约、服务注册(service registries)、和异步交互风格使得 服务(部件)间交互的耦合被减至最小。

甚至于我的那个简单的程序都能受益于这些SOA的特性。例如,我的程序是用C#写的,它去 造访Amazon的Web Service却毫无问题,尽管服务端不太可能是用同一种语言写的。另外, 我也不需要用一个“远程工厂”(remote factory)去创建一个远程对象(remote object) 来执行我的查询请求。其实,我只要把一份XML文档(由stub产生)传递给远程的服务就行了。

但另一个微妙却很重要的方面在我的例子中还没有显现出来。例如,在 远程服务处理请求的时候,客户端程序一直把持着一个与远程服务的TCP连接。而代理对象 (proxy object)也是在同步地(synchronously)等待着结果,在此期间,客户端程序 不能去做其他任何事情。这个简单的程序模型绝对是“远程过程调用”(remote-procedural call)。 尽管这种模型是很方便的,但它并不适合远程面向服务的交互。

那么,到底是什么能让我们从建立一个Web service的演示程序这种琐碎事务中更进一步 成为一个能够开发松耦合的面向服务架构系统的真正的开发人员呢?

3 架构师的美梦 - 开发者的恶梦?

在“企业应用架构模式”(Patters of Enterprise Application Architecture) 一书中 [2], Martin Fowler 警告说,松耦合的分布式系统架构在白板上看上去 很漂亮,却很容易成为“架构师的美梦 - 开发者的恶梦”。让一个系统成为分布式 是增添了整个一层的复杂性,这点是没什么疑问的。例如,配置和部署(configuration and deployment)变得更困难了,你还得来对付响应时间、一些新的失败情形如网络 中断或部件间的版本号失配。

如上所述,松耦合架构的出发点是减少部件间交互的复杂性和减少它们之间的耦合。 耦合可以粗略地用一个部件对交流方的假设条件数目来测量。例如,松耦合架构 一般对各部件的实现技术不做假设。同样地,当数据格式有小变化时,如 增加一些新的数据项,这种架构也不失其稳固性。正是减少了假设条件数使得 交流各方能够独立演变 - 这是松散耦合后面的关键驱动因素之一。

但是,假设条件的减少又意味着开发人员的安全毯变薄了。强耦合使得编译器能够 捕捉到交流部件间的许多失配情况。如功能方法(method)名的误拼、缺失的参数、 不匹配的数据类型都可以在编译时发现。在松散耦合架构中,这些都不存在了。结果是, 对一个紧耦合并具备调用堆栈(call stack)和共享存储空间的应用系统的查错调试 比在一个异型的(heterogeneous)、异步的(asynchronous)、分布式的环境中 查错调试要容易得多。

4 面向服务的世界里人的因素

许多人认为Web Services 是过度炒作,因为我们可以用CORBA或类似技术来建造很好 的面向服务的应用系统。当然这种观点有一定的道理,不过却有些偏离了要点。 要知道在开发人员的头脑中,除了要清楚知道可使用的技术与API方面的变化,也要了然架构风格 方面的变化。架构风格是基于共同的“意向”(intention),正确的平衡,以及观念上的 一致。选择一个合适的技术可以帮助实现需要的架构风格,但这还远远不够。

好在历史会时常地重复,特别是在系统架构方面。我们可以作如下的类比:从分布式 部件结构到面向服务架构的演进,可以比之于从过程式程序设计到面向对象的演进,可以比之于 从文字用户接口到图形用户接口的演进。最初对新技术的采用是在新技术或新的API中使用 老的架构风格,这通常称之为“猪鼻子插葱--装象”。例如,用Web Services 替代RPC (remote procedure call)实际上是在视窗环境下使用文字接口的应用系统的 翻版。当然,你可以说这个系统是使用了新的GUI(图形用户接口)技术,你可以改变 窗口的大小,还可以把它移来移去。但应用本身却很难说是使用了新环境中真正的新 特性。学习如何建造真正GUI应用需要使用新的工具、新的思维方式、新的设计模式以及 一套新的设计指引。有意思的是,新的API在这种用户接口的转型中其实只占了很小的比重。

同样,我相信在向面向服务的转型中,SOAP只会占相对较小的比重。特别是在近期, 面向服务系统的设计中会更多地依赖于惯例/约定(convention),而不是技术。例如, 在功能调用中,传递的参数不使用私属(proprietary)数据类型便是这样一个约定。 不去假设一个服务在什么地方也是一个约定。还有,我们如何来避免一个服务是过“粗” (too coarse grained)或过“细”(too fine grained)呢?如何来确定把多少业务逻辑 放在服务中,多少逻辑放在编排部分(orchestration layer)?目前,还没有工具能 帮助我们决定或实行这些指引。它们只不过是开发组内大家对某一特定的架构 风格和意向的共同理解和约定。

对约定和惯例的依赖在松耦合的分布式系统中似乎显得特别突出。松耦合意味着减少 部件间的假设条件。假设条件的减少意味着编译时检查和验证的弱化。因此,充分的验证 必须通过人与人之间的交流与约定来实现。

那么,对于开发松耦合的、面向服务架构的应用系统的开发人员来说,这意味着什么呢? 这意味着人与人之间的交流显得比以往更加关键。在文档中记录下全局图像和设计意向对于 保持架构的完整性和一致性是至关重要的。近期来看,这意味着开发人员的工作会更加 辛苦,而非更轻松一些。这意味着,近期来看,在面向服务的架构中最有用的工具会是 Word或PowerPoint(或是OpenOffice Writer 和 Impress)。

5 新工具

新工具在面向服务的应用开发转型中是非常有用的。有趣的是在每次程序设计机制或技术转型时, 新工具的出现似乎都是分成几个特定的阶段。我大致观察到有三代工具支持新的技术:

第一代:后溯(Retrofit,向后看齐)。第一代工具一般来讲是让老的应用看上去象新的。 这些工具把大型主机(mainframe)的绿色屏幕接口转化成GUI,或是把一些老的COBOL程序 转化成Web service。这当然是很有用的,但需指出的是,这些工具只是简单地转换了技术, 而非架构风格和意向上(intent)的转换。

第二代:简单工具对付简单问题。这代工具让开发人员能使用新技术和新风格快速地 建立一些解决方案。可是,这代工具瞄准的是一些简单的问题,这些问题可以用 (过分)简单化(或片面化)的途径来解决。例如,许多早期的GUI工具可以从数据 库的数据结构定义(database schema)中直接生成结构图。这种途径只适合于简单的 应用系统(如CRUD型的应用),却不适合具有复杂业务逻辑的应用。

第三代:有效的工具对付复杂的问题。这一代工具已超越了简单的问题。尽管它们不能 将复杂问题魔法般的琐碎化,它们却提供了有效的工具来帮助开发人员应付复杂的问题。 Java IDE, 如 Eclipse 和 IntelliJ IDEA 就是这代工具的典型代表。

第三代工具在市场上出现需要相当的时间。原因有多种。首先,建造复杂的工具自然需要 花费长一些的时间。更重要的是,要想建造有效的工具来对付复杂的问题,你得花时间 去观察开发人员是如何处理复杂问题的。这需要时间和经验,同时也需要一批铁杆的开发 人员,他们愿意采用最前沿的技术,使用不是那么完备的工具来解决复杂问题。

今天许多 Web Service 的工具都还是第一或第二代的。我们可以找到很多Web Service的“门面” (facade)工具,或是可以让简单的应用开发变得容易的工具,如象,“右击,生成Web Service”。 那么,第三代工具会是什么样的呢?我相信,我们会看到复杂完善的测试和查错工具, 象SOAPScope。我们也会看到监控和管理工具的迅速演进。这些工具能够在系统运行时(run-time) 发掘出服务和服务之间的交流,然后生成服务间的动态依赖关系图。它们也能让我们把规则 (policy)即时地、同时地实施到多个分布式的服务中去。

我相信这些发掘和视像化的工具会在第三代SOA工具中扮演重要的角色。松耦合的一个基本 着眼点是要允许各个部件可以独立地变化演进。这意味着我们不能(我们也不想)事先对系统 预设一套自顶向下的条条框框,我们应该允许系统以事先不能预测的方式演变。为了跟踪 系统的变化,我们需要有工具来检测系统当前和以前的状态,并以直观的、易读的方式显示 出来(如图形)。这些工具目前已在市场上出现,我认为我们会看到更高级完备的工具会 在今后一两年出现。

6 新的程序设计范式(New Programming Paradigms)

学习新工具只是在向面向服务架构的应用的开发中的一小部分。我们已经讨论了采用SOA 是需要对系统部件间交互方式具备一种新的思路。它同时也需要开发人员去适应新的 程序设计模型和范式(思维方式)。这里,我想着重介绍几个主要的模型,当然下面所列绝非完全。

6.1 对象与文档间的映射(Object-Document Mapping)

首先,以文档为中心的途径和面向对象的途径有着微妙而重要的区别。面向对象的系统 的一个特征是细粒的(fine-grained)的对象、对象间是用对象指称(reference)或 指针(pointer)来高度连接和交互的。以文档为中心的接口则是用来表达文档,而非行为。其 表达方式通常是树型结构。因为文档需要在多个系统间传送,要管理对象指称(reference) 会是非常困难、又很不方便的。文档一般是粗粒的(coarse-grained),这样可以很好 地支持部件间的封装(encapsulation),减少网络“聊天”(chattiness)。它们也 没有继承(inheritance)和多态性(polymorphism)的概念。

这些差别很微妙,但对于要成功地开发可维护的SOA却很重要。它们在某种意义上 象是重现了OO系统与关系数据库的“阻抗失配”(impedance mismatch)。O-R 映射是很微妙的,虽然这个问题已经出现很长时间了,但是新的映射工具却仍然以 可观的速度出现。许多当前的Web Service工具可以比之于两梯次(2-tier)的 数据开发工具,它们可以从数据库的结构定义(database schema)中生成GUI。如果 应用模型与后面的数据模型很接近的话,这些工具可以工作得非常之好,但它们 通常不能支持开发那些有复杂的业务逻辑的应用。同样的,许多目前把OO系统中 的一个功能方法(method)暴露成Web Service的工具也有这个问题。当你想在 大尺度上把具有复杂应用逻辑的部件暴露成良好定义的服务的时候,这些工具 很快就会现出它们的局限性。其结果是,开发人员需要建造他们自己的映射层 来实现对象-文档间的映射,或是等待更高级完备的工具的出现。

6.2 宣示型程序设计(Declarative Programming)

有两个常见于服务架构或Enterprise Service Bus (ESB)中的核心功能,一个是变换 (transformation),另一个规则引擎(rule engine)。描述这些功能的程序设计模型一般 是宣示型的(declarative),这与大多数开发人员所熟悉的顺序过程型(sequential-procedural) 风格相反。例如,许多转换工具是基于XSLT的,它是一种模式匹配(pattern match)的途径。 XSLT处理是用一组规则去比对输入的文档,并选择最特定(而非宽泛)匹配的规则执行之。 有些工具会用“拖-扔”(drag-drop)接口把XSLT隐藏起来,但程序模型是基本一样的。如果 你是从OO程序设计转到XSLT,你大概会注意到这两种情况:你的第一个XSLT文本在执行时, 要么可能生成文档的全部,要么什么都没有,这是因为你的模式匹配全错了。另外,你的下意识 会诱惑你使用许多 <xsl:call-template>,因为这个构造子是最接近于传统的功能调用的 语义的。但它也是让你的XSLT文本成为最糟的罪魁。

宣示型的程序设计系统可以导致一个很紧凑的解决方案。但这也需要不同的思维模式,另外, 查错也较困难,因为执行路径(execution path)是在运行时(runtime)确定的,而非在设计 时决定的。随着转换和规则引擎变得越来越普遍,开发人员需要学习如何设计与查错这样的 宣示型子系统。

6.3 基于事件的程序设计(Event-based Programming)

许多业务过程都是对事件作响应,例如发来的订单、付款或用户来的电话。这意味着一个 功能的执行,再也不是线性、顺序的,而是需要在某个事件发生时作出反应。这种风格非常 不同于传统的应用开发,那里,开发者可决定什么事情可以以什么顺序发生。

因此,事件驱动的心理模式对多数开发人员来说需要一定时间来形成。从根本上说,事件驱动 意味着抛弃“调用堆栈”(call stack)。没有了堆栈,执行流(execution flow) 再也不是由“压入-弹出”、同步(synchronous)调用、和局域变量来控制了。取而代之的 是,当事件发生时,程序员需要显式地(explicitly)来管理运行的连续性和状态。许多 过程和编排(orchestration)工具都提供了一些诸如“关联数据集”(correlation sets) 和“使者”(convoy)的机制来帮助开发人员应付这些问题。但是,开发人员仍然需要改变 心理模式以便能更有效地使用这些构造子。

6.4 异乎寻常的异常处理(Exceptional Exception Handling)

如果说,开发对事件响应的软件已是有足够的挑战性,那么系统中出错时又该如何处理呢? 传统的两阶段认可事务处理(two-phase commit transaction)适合于可预见的、 相对紧耦合的模型,这是不言而喻、自然而然的。这种事务处理方式也需要在事务 发起者(initiator)、各个事务资源(resource)和事务协调者(coordinator)之间 有一套复杂完备的两阶段的交互模型。在面向服务的松耦合的环境里,这种风格的交互是 不存在或不需要的。

在面向服务的世界里,更为普遍的错误处理策略是重试(retry)补偿(compensation)。 这些都不是新概念(例如,我们知道 Sagas 已经超过15年),许多工具如BPEL引擎就支持 长时运行的事务和补偿行动。可是,如何有效地使用这些新的异常处理,包括模式和最佳 实践指引,还没有广泛地为开发社群所知。就象基于模式的设计在OO中的传播一样,(面向 服务系统的)开发社群可能还要花上许多年才能采用这些新的交互模型。

6 图件(Doodleware)

分布式系统开发中,另一个前沿的程序设计风格是图形(视像)化的编程环境,有时被 称之为“图件”(doodleware)。用图像来表达时间上并行的活动,或是依赖关系是非常 非常有用的,这也广泛地被用于过程和编排(orchestration)模型中,不过这也需要一些 时间来适应。例如,要把系统扩放到一个大的尺度就有些困难。有些开发人员已经建议,销售 图形过程建模工具(软件)时应该带一个显示器作为必要的实物(为大尺度的应用编程提供方便)。 另外,常用的文字处理工具如“diff”或“find-replace”(寻找-替换)基本上都不存在于 图形世界中。查错会更加困难,尽管最近我已看到一些很不错的图形调试工具出现。我想, 要在图形世界里出现象文字世界中的Eclipse或IntelliJ那样的成熟的开发环境,还需要相当 的时日。

7 结语

在面向服务的世界里的软件开发在相当长的一段时间内都会是一个很有意思的课题。Web service 的出现的确是消除了“EAI”中的一些苦涩成份,并且使分布式系统开发成为一项主流活动。 但是,对于新的架构风格的普遍理解和相关工具的演进只能在今后几年中逐步实现。

许多第一代的SOA工具都有些欺骗性。开发一个成功的SOA并非是把一些小图标(icon)拖 过来扔下去那么简单。对开发人员来说,面向服务意味着学习新的程序设计模型和技术。 改变思维方式以有效地利用这些新模型可能是成功采用SOA的最大障碍。

About the Author

Gregor Hohpe leads the Enterprise Integration practice at ThoughtWorks, Inc., a specialized provider
of application development and integration services. Gregor is a widely recognized thought leader on
asynchronous messaging architectures and co-author of the seminal book "Enterprise Integration
Patterns". Gregor speaks regularly at technical conferences around the world and maintains the Web
site www..

关于作者

Gregor Hohpe在ThoughtWorks领导Enterprise Intergration部门,ThoughtWorks是应用开发与集成的 专业服务公司。Gregor被广泛地认为是异步消息架构中的思想领导者,他是广受好评的 “Enterprise Integration Patterns”一书的作者之一。Gregor经常在世界各地的技术大会上作 演讲,他也维护他的网站 www.。

参考资料

[1] Web Services are not Distributed Objects, Werner Vogels, IEEE Internet Computing, Nov-Dec 2003
[2] Patterns of Enterprise Application Architecture, Martin Fowler, 2002, Addison-Wesley
[3] Enterprise Integration Patterns, Gregor Hohpe, Bobby Woolf, 2003, Addison-Wesley
[4] Visualizing Dependencies, Gregor Hohpe, http://www./ramblings/11_dependencies.html
[5] Enterprise Service Bus, David Chappell, 2004, O'Reilly
[6] Starbucks Does Not Use 2-phase Commit, Gregor Hohpe, http://www./ramblings/18_starbucks.html
[7] Sagas, Hector Garcia-Molina, Kenneth Salem in Proc. ACM Sigmod, 1987
[8] Production Workflow, Frank Leymann, Dieter Roller, 2000, Prentice-Hall PTR

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多