摘 要 在Web 应用开发中,之所以能够利用对象的概念,方便地创建灵活,稳定的Web 服务,归功于J2EE 的多层体系结构。而在传统软件(非Web 应用),如何利用面向对象技术,是本文的主要目的。本文基于一个分布式的开发工具模型 – SoftEngine, 简要阐述了分布式系统的基本技术及特征,包括:任务驱动,分布式对象,流水线式设计模式,以及数据安全等关键技术 。 1. 前言 自从有了J2EE 体系结构,基于诸如:BEA WebLogic, IBM WebSphere 以及Sun ONE 等多层结构的应用服务器,Web 开发已变得如此的便捷、高效。Web 程序工程师也不在过多的关心如何解决系统稳定性、高负荷等问题,而精力更多地集中在如何实现应用逻辑。比如:现在,很少有人在采用了上述的中间件产品后,还问津系统是否稳定、如何扩容等问题。因为J2EE 体系,已经解决了这些繁琐的技术难点。 而其他的、非Web 方式的传统软件开发工程师就没有如此的幸运了。特别在一些基础分布式系统、实时系统及数据传输系统的开发项目,基于Web 方式的应用已无法解决。J2EE 已无法提供更多的帮助。此时,只能依靠程序员从系统最基本的功能开始开发。除功能外,系统性能好坏,完全取决于开发人员的素质及经验。在短时间内构造出如同Web 应用一样稳定、高效的系统,简直成为一种梦想。 为了实现这个梦想,软件的“总线结构”孕育而生。所谓总线结构,就是所有的功能部件以相同的方式连结在一个用来相互通信的结构性部件上。由此带来的好处是,部件的内部设计被独立出来,再也不必考虑部件之间的互连问题,设计人员可以专注于部件的功能设计 。 2. 分布式系统所应具有的体系结构 一个分布式计算系统(distributed computing system ),就好比现实生活中的一个公司。其中,某些职员负责日常的内部事务保证公司正常运作;某些职员负责对外联系工作,保持与其他公司的关系;在公司员工之间定义了一定的工作流程,通过固定格式的表单提交任务及交换信息。在公司运作中,只要员工的职责及工作流程保持不变,其他的可变因数,如:工作的物理位置或交换信息的不同,都不会造成本质的变化。 因此,在分布式系统中,需要定义出不同的职能对象。这些对象是可分布的。对象之间还需定义各种内容的信息/ 消息体。以SoftEngine 为例,定以了三种最基本的元素对象: 功能对象(Function Object) ,负责内部功能操作。 服务对象(Serve Object) ,具备通讯能力。 任务对象(Task Object) ,在功能及服务对象间传递信息。 对象间的协调、信息传递等复杂的操作,被封装在SoftEngine 的内核。通过简单的接口对象,演变出各种不同的应用系统。如下图,所示的体系结构 :
图表 1 The Architecture Of SoftEngine Components Area 组件局域 是一个开放式的区域。为应用组件提供了三种主要的接口: 功能接口(Function Interface)、服务接口(Serve Interface)、任务接口(Task Interface) 。继承了面向对象的特点,组件局域具备了许多“软总线”的特性,可以灵活地加载、卸载应用对象 。 SoftEngine Kernel 内核 是SoftEngine 的核心部分,封装了大部份的内部操作,如: 对象间的协作、任务传递和对象的分布管理等。其中最主要有两个部件: 任务通道(Task Channel)和通讯通道(Communication Channel) ,用于安全,有效,准确地传输数据 。 Task Channel 任务通道 直接连接每个对象,传输任务信息从一个对象到另一个对象 。 Communication Channel 通讯通道 连接每个分布的SoftEngine 内核。传输任务信息从一个SoftEngine 到另一个SoftEngine 系统 。 Singleton Model 独体运行模式. 如果应用系统不需要分布,也就是说:所有的应用组件发布在同一个SoftEngine 环境中,那么这个单独运行的模式叫独体运行模式 。 Group Model 群体运行模式. 基于独体运行模式,应用组件被分布在不同的SoftEngine 系统。彼此之间通过通讯通道连接在一起,形成一个分布的系统,称之为: 群体运行模式 。 SoftEngine 的体系结构,以多态的方 式 ( 独体Singleton / 群 体 Group ) 存在。独体充分表现了软总线的特性;而多个独体组成的群体表现出分布式的系统特性。 从独体上看:是软总线结构的应用软件,其中的每个模块(对象Object )以插件的方式,通过系统提供得接口及配置文件,发布在系统上。对象彼此之间,通过任务通道(Task Channel)协调工作在一起,而Object 之间的构件操作被封装在SoftEngine Kernel 内。 从群体上看:是一个分布式的结构,每个独立运行的SoftEngine 系统,通过Kernel 的通讯通道(Communication Channel)彼此连接,协调工作,成为一个完整的强大的分布式系统 。 3. 布式系统的关键技术 3.1 面向对象技术Object-Oriented 客户/ 服务器模式,是典型的分布式计算模型。在此模式下,客户端建立连接到服务器,通过相互约定的协议通讯,以达到交换信息的目的。SoftEngine 核心的通讯通道封装了这些复杂的底层细节。通过多层的封装技术,将底层对象与应用对象完全隔离。SoftEngine 只在组件区域为应用提供了一些抽象类作为“软总线”的接口。如下图所示:
图表 2 Abstract class is the interface of Soft-Bus. Application Objects : 应用对象 System Objects: 系统对象 Infrastructure Objects: 底层对象 Abstract Class: 抽象类 设计一个可重用的面向对象软件不是简单的事,一个分布式的重用系统更是困难的事。在SoftEngine 的基础之上却很容做到。因为其本身的体系结构是对象化设计的,也是可重用的 。 在组件区域内,开发人员只需关注应用的业务,继承接口抽象类,实现所定义的业务流程,通过发布实例化的应用对象,构造应用系统。这种面向对象的开发模式,对程序员来讲是非常简单,非常有效的,也可从繁重的体力劳动(编码)中解脱出来,更多的精力用于设计工作。 组件区域中的应用对象,是可被重用。按照一定的发布方式,可以在不同的SoftEngine 系统中再次利用。可被重用性不再取决于编码的质量,而在于如何设计出好的应用业务逻辑流程。关于这一点,我们将在后续的流水线式设计模式中讨论。 3.2 任务驱动Task-Driven 在SoftEngine 内部采用的是任务驱动。在这里所提到的任务驱动概念,不完全相同于一些专业任务驱动论文,更接近事件驱动(Event-Driven) 。也就是说,所有的对象在没有任务时,处于空闲状态;直到有任务(事件)到达,对象的运行模式才会转变为激活状态。 在不同的对象间,传递任务是合作(collaborate) 的唯一方式。合作模式有两种: 1、完全独立的模式(Individual) 2、松散耦合模式(Loosely Coupled) 下面图示了其中的不同:
图表 3 Individual and loosely coupled collaboration 对象A 和对象B 有次序地处理同一个任务,对象B 必须等待对象A 的处理果。对象A 和对象B 的合作关系称为:松散藕合模式 。 对象B 和对象C 分别处理不同的任务。这种关系称为:完全独立的模式。也就是说,对象C 需要的任务,不总是来自于对象B ,它们之间没有必要的关联。 在完全独立模式,对象C 由于功能相对独立,无须等待想关数据,所以效率比较高,具有很高的实时性。但存在功能相对集中,适应性比较差,被可重用的可能性也相对减少的不足。松散耦合模式,为完成任务,将处理步骤/ 功能细分为不同的对象。使对象A 和B 的功能相对专一,适应性增强,可重用性也增加了。同时更据不同的功能效率可以方便的作负载均衡(Load balancing)调整。但是任务的多次投递,以及相对的耦合关系,也降低了局部的处理性能。就如下图所显示的两者的性能比较:
图表 4 Performance of collaboration 在实际应用开发中,如果选择完全独立的模式 或 松散耦合模式,取决于在设计阶段如何定义应用的业务流程。这将在后续章节: 流水线式设计模式中再次阐述 。 每个被传送中的任务,都具有自己的生存周期(Live cycle),从产生到被丢弃。如下图生存周期图:
图表 5 Live-cycle of task Created : 被产生 Forward: 正向传送 Backward: 回送 Pending: 悬挂 Timeout: 超时 Operating:处理 Droped:被丢弃 一个任务由某个功能对象产生,并传送(forward)到下一个目的对象,在那里将被处理之后,这个任务会被丢弃,或被返回(backward)到源对象。这是一个常规的任务生存周期,但如果在任务传递途中,发生如目标对象不可到达等阻碍,该任务将会被悬挂(pending) 。直到继续传送或超时(timeout)被丢弃。任务在传递中的安全问题,将会在后续的数据安全主题中讨论。 3.3 分布式对象Distributed Object 在上文提到,SoftEngine 由不同种类的对象构造,在发布实施角度上看,可分为三种: 本地(Local)对象,远地(Remote) 对象,虚拟(Virtual) 对象 。
图表 6 Local, Remote and Virtual Object 在上图中: 本地对象Local Object : 指分布在同一个SoftEngine 独体系统中的对象,互称为本地对象。如:在系统I 中的对象A 和B 互为本地对象 。 远地对象Remote Object : 指分布在不同SoftEngine 系统中的对象(同一个群体系统)。 如: 对象A 和C ,以及B 和C 互为远地对象。对于系统II 中的C ,系统I 中的A 和B 属于远地对象的 。 虚拟对象Virtual Object : 不同于本地和远地对象,虚拟对象不属于真实的对象,而是一个虚设的类型。真正的操作不在虚拟对象本身,只是远地对象在本地的映射。如:虚拟对象A,是A 在系统II 中的映射。对象C 向A发送的任务,会转送到远地对象A ,而C 不知道,也没有必要知道。同理,如果对象B 需要发送任务到C ,在系统I 中可定义C 的虚拟对象 。 本地和远地对象是相互的关系。而虚拟对象只是一种映射,用于关联本地和远地对象,起到分布和负载均衡的作用。三者之间的关系在开发中无须关心,只有在系统部署中,通过配置发布环境来确定或改变。以下图为例,介绍负载均衡:
图表 7 A load balance system 在一个分布的系统中,有两个独立的运行系 统 I 和II 。其中有相同的对象A1, A2, A3, A4 被分别发布,他们所被加载的类是相同的,只是被定义的发布名称不同。如果再定义一个虚拟对象A 同时映射到A1 – A4 ,同时配置不同的策略,如负载均衡的循环法(Round-Robin) ,或冗余法(Hi-Available)。发送到A 的任务,会按照不同的策略分发到A1-A4 上。 在应用对象开发测试完成后,部署的策略,包括:系统配置、对象配置、对象发布等信息,被定义在每个独体系统的配置文件中。通过这些配置文件,将会动态地构建出一个分布的网状系统,可以完成特定的应用功能。所以如此构成的分布式系统具备以下的体点: 整个分布式系统由各种对象,以动态组件的方式构造。 对象可以存在于任何网络(LAN/WAN) ,任何主机。 通过配置,对象可以动态地加载/ 卸载。整个分布系统,始终处于动态调整状态。 虚拟对象让本地对象忽视真实的远地对象的物理位置。 开发与分布过程完全分离。在对象开发过程中,无需过多的考虑系统级的问题。
图表 8 A distributed system 3.4 数据安全Data-Safety 应用系统所需的数据信息,在以任务的方式送入SoftEngine 系统内部后,任务将会被高效地传输在每个独体系统内部,或群体系统的网络中。因此,如何保证任务的安全到达,是一个关键的问题。在SoftEngine 里,解决这个问题的技术称为:数据安全。其中包括四个部分 : 安全的任务池Safety Task Pool 任务悬挂Pending 降速传递Slowdown 流量控制Flow control 以下图为例描述:
图表 9 Data-Safety Forward Task: 发送任务 Backward Task: 回送任务 Undeliverable Task: 无法发送任务 Panding Task:悬挂任务 Task Channel:任务通道 Pending Pool: 悬挂缓冲池 .安全的任务池Safety Task Pool 在SoftEngine 的内核,大部份对象都有自己的任务池与任务通道连接。类似于文件系统的内存缓冲,任务池对于提高系统性能有非常重要的作用。同时为了保证数据的安全不丢失,任务池需要良好的设计,包括: 过载保护(overload protection) ,硬盘缓冲等安全机制,确保无论对象运行状态如何,任务都不会丢失。也就是说,当一个任务被成功地送入目的对象的任务池,这意味着:任务是安全的。所以任务池保证了静态任务的安全性。 .任务悬挂Pending 在一些特殊情况下,任务无法立即发送到目的对象(如上图对象A 发送任务到对象C )。无法发送的任务将会被悬挂在一个特殊的任务池中 – 悬挂缓冲池。通过悬挂缓冲池,SoftEngine 系统会记住有那些任务因为那些目的对象而被悬挂,等待目的对象恢复后重发,或则任务超过了规定的生存周期,被丢弃。 .降速传递Slowdown 在常规状态下,对象A 会以最快的速度向对象B 发送任务。但如果此时,对象B 处于无效状态,或任务量过载,那么第一个无法发送的任务将会被悬挂,同时发送源对象的发送速度也会被系统自动控制,减缓发送的频率。称之为: 降速传递。 降速传递机制可以在巨大的任务流中,有效地保护系统失去控制,尤其当某个任务流中的对象发生了意外。举个例子: 对象A,B,C 合作完成一项任务,对象A 会产生大量的任务,经过对象B ,发送到C 。如下图:
图表 10 One Sample 假设,对象C 由于某种原因停止了工作,在没有降速传递机制的情况下:对象B 也会由于大量的任务没有发送,而很快“爆掉”。对象A 也会跟着失控。此时堆积在A,B,C 中间的任务将无法处理。另外,系统的CPU 、Memory 开销可以会受到影响。采用了降速传递机制,对象B 的处理和发送任务的工作在一定程度上得到控制,对象A 也是如此。整个工作流的处理速度会自动减慢。直至对象C 恢复正常的工作状态。 所以当系统发生无法估计的问题时,降速传递可以有效地起到稳定系统的作用,避免大量数据因事故而丢失。 .流量控制Flow Control 在分布式系统中,任务将会通过任务通道、通讯通道在内存、网络上传输。保证传输线路上的数据安全也是非常重要了。流量控制机制是一个常见的技术用于解决传输双方的稳定及数据的安全问题。这里不做过多的介绍。 3.5 流水线式设计模式Pipelining Design Model “流水线式”经常在讨论CPU 技术时被谈及。在CPU 设计中,非流水线式设计模式的问题在于:每个单元指令必须在另一个指令开始前完成。这样会有很多的空闲时间片,没有效率。而流水线式的最基本思路在于充分利用CPU 内部空闲的组件,以至于可以同时处理多了单元指令,减少空闲时间片。也就是说,单元指令之间无须等待。非常明显,这个技术可以极大程度地提高硬件效率。 在SoftEngine 内借用了这种思路,将任务代替了指令,将功能对象代替了硬件组件。流水线式设计所运用的基本理论:将大的任务划分成一系列可交迭的、较小的任务,同时被一些功能对象分布处理。所以,在SoftEngine 基础上,应用的设计人员,需要分析应用的处理流程,在设计阶段划分大任务,提高应用系统的性能。SoftEngine 可以有效地支持这种先进的设计模式 。 举个例子,在普通的开发设计( 非流水线式设计) 中,假设一个任务需要4 个步骤完整, 第一步:保存数据到磁盘,耗时100ms 第二步:从磁盘中读取历史信息,耗时20ms 第三步:从内存中读取配置信息,耗时5ms 第四步:结果计算,并返回结果,耗时10ms 见下图:
图表 11 Non-pipelining mode 整个流程的处理时间需要135ms ,请求的返回时间也是135ms 。不难看出,第一步虽然耗时最多,但属于工作流的关键步骤,后续步骤也不等待其运行结果。而且在这个工作流中,每步操作,都需要等待上一步的操作(在同一时刻,只能有一个步骤在工作),所以135ms 不是最优化的结果。 通过流水线式设计模式,可以进步优化。首先,将这个任务,划分为三个小任务分别由四个对象处理,所以设计了5 个对象 : 对象A :划分大任务为三个小任务,同时分发。作为任务开始。 对象B :保存数据到磁盘,耗时100ms 对象C :从磁盘中读取历史信息,耗时20ms 对象D :从内存中读取配置信息,耗时5ms 对象E :等待任务2 和3 的结果,计算后返回,耗时10ms 见下图:
图表 12 Pipelining mode 虽然每个步骤单位处理的时间并没有减少。但对象B 的费时操作,被安排在主要流程之外,所以没有影响整个处理时间。对象C 和D 的处理被安排在同一时刻发生。对象E 等待C 和D 的结果。所以只要工作流程为: (A) -> (C, D) -> (E) 步考虑任务传递时间耦合时间,及A 的分发时间,总的响应时间为: max( C, D ) + E = max( 20, 5 ) + 10 = 30ms 。 所以,流水线式设计模式的优势,显而易见。同时也改进了以往开发流程。 4. 分布式系统对开发流程的改进 以上介绍了分布式开发系统的关键概念及技术。利用这些技术,不但可以提高系统的性能,也可以改进以往开发的环境和步骤。开发步骤包括以下5 步: 设计Design: 改变以往从头设计的做法,主要精力集中在应用的业务流程、工作流程、任务流程的设计。不断优化流程,是应用系统成功与否的关键。 定义Define: 流程确定之后,就可以从中定义出各种功能对象,服务对象,任务对象,并加以描述。此过程需要将定义的对象放入确定的流程中,模拟其使用性、有效性等 。 实现Realization:开始实现定义好的的各种对象。并进行单元白盒测试,保证每个对象自身的稳定。在实现过程中会发现与定义中相矛盾的地方,此时需要与设计人员探讨修改。 部署Deployment: 实现了每个对象之后,就可以对系统按照设计的流程进行部署。整个过程,都是通过配置文件进行系统调整。所以只要对象具有较强的独立性,并且保证每个对象已达到错误最小化,就可以保证在联合测试中,不会有过多的修改工作。但部署中,会根据实际境况对原先的流程进行调整,以达到最佳效果。 运 行 Running: 开发的最后一步,就是进入运行状态。
图表 13 Development process 与传统开发流程相比,每个阶段的划分都更明确,这样有利于不同角色的划分和分工。尤其在部署阶段,原始设计人员可以使用编码完成的组件对象,直接介入系统的构建中。另外一个优势在于:整个开发周期变短。由于无需从头设计,按照一定的模式定义对象,程序的统一结构化,这些都是传统软件开发所无法比拟的。尤其充分的白盒测试,使联合测试的成功率提高,时间节省了许多 。
|