基于模式的体系结构设计与开发,第 2 部分: 使用体系结构模式
2010-01-22 00:00:00 来源:WEB开发网开始之前
这个系列分为两部分,可以供所有对改善软件应用程序设计感兴趣的程序员、架构师、开发人员和技术爱好者阅读。完成本系列的学习之后,您将能够使用其中介绍的最佳实践,并选择正确的设计模式来解决具体的问题。
关于本系列
本系列教程通过铁路订票案例研究说明如何将设计模式应用于解决体系结构设计问题。
第 1 部分介绍了一个铁路订票系统,带您了解了设计中的几项注意事项,它们可以帮助您决定在何处使用设计模式以便改善设计并进而提高系统的整体性能。
本教程讨论了应用程序的非功能需求,并阐述了软件架构师为什么必须处理这些会对应用程序的性能、可用性、可伸缩性和增强性造成影响的需求。另外还将概略地讨论灾难恢复和故障恢复功能的注意事项。本教程的最后将讨论在您的设计中使用框架的问题。
关于本教程
体系结构模式能帮助您为软件系统定义一个基础结构组织或模式。它还能提供一组预定义的子系统,为系统中使用的每个组件指定相应的责任,而且它还包含一些在组织子系统之间的关系时使用的规则和指导原则。设计模式提供了一个模式,旨在细化那些用来定义整个软件应用程序的子系统或组件。它还提供了一套词汇表,可以定义子系统之间的关系。设计模式用于描述某个特定环境下出现的一般设计问题的解决方案。
本教程对第 1 部分中讨论的铁路订票系统进行了扩充,在其中应用了几种体系结构模式。体系结构模式可分为不同的类型。部署体系结构模式是最为重要的,因为应用程序的部署方式对于所有非功能需求(如性能和可用性)都具有关键的意义。
在本教程中,您将从架构师的视角来了解铁路订票系统的非功能需求。您会学习如何使用不同的体系结构模式来改进系统的基本设计。本教程还将讨论使用 MVC、Struts 和 Spring 等框架处理其他设计问题,包括利用一种框架(或多种框架的组合)进行应用程序开发时的代码重用和总体时间问题。
本教程中讨论的一些体系结构注意事项有:
高性能
高可伸缩性
故障转移和故障反馈功能
灾难恢复
先决条件
本教程假定您熟悉设计模式,并了解基本的面向对象的概念。如果您对统一建模语言 (Unified Modeling Language, UML) 有一定的了解,这将有所帮助,但并不是必需的。示例代码采用 Java™ 编写,但其内容非常简单,可以方便地转换为您选择的语言。
如果需要,可以下载 Java 5.0。
一位架构师对系统需求的看法
对于一个新项目,首先要考虑的事项中包括目标应用程序的业务需求。不过,业务规则并非存在于真空之中。在考虑某个设计时,性能、可伸缩性和可用性等其他需求的优先级也很高。架构师在分析系统的“非功能”需求中扮演着至关重要的角色。
这一部分将研究某些非功能需求,并把它们放在第 1 部分所述的铁路订票系统中予以讨论。
铁路订票案例研究
在设计应用程序时,架构师绝不能忽视其中某些特定的需求。从客观的角度确定需求,就和人们用平直的语言解释问题一样简单。清单 1 是客户与架构师就订票系统进行讨论的一个示例。
清单 1. 了解非功能需求 架构师:您希望最终开发出来的应用程序提供什么样的性能?
客户:我们希望系统的响应时间不要超出普通 Web 用户可以接受的容忍限度。
架构师:您描述得很好,但如果您提供一些关于预期性能的具体数据会更有帮助。
客户:好吧,用户在应用程序中创建的任何事务都应当在 10 秒钟内得到响应。
架构师:一般情况下,响应时间对连接类型有很高的依赖性。
客户:没错。宽带连接的响应时间不能超过 10 秒,拨号连接则不能超过 15 秒。
客户:我还希望系统在大部分时间都处于可用状态。我每年最多只能接受两天的停机时间。
我知道,您会因为各种各样的原因而需要偶尔重启服务器,但我希望您构建一套在重启时不会影响终端用户的系统。
如果我们必须提供一台备用服务器以便在主服务器重启时运行,我们可以为这次部署提供一台。
架构师:我明白了!您预计有多少用户使用这套系统?还有,他们中有多少会同时使用它?
客户:我预计开始时有 500 位基本用户。但是我要求系统在不做出重大修改的前提下支持最多 2,000 位用户。
我希望任何时候都能有 5% 的用户同时使用系统。
架构师:还有最后一个问题:您打算把服务器放在哪里?
客户:我们在两座城市设立了数据中心,我们只能把服务器放在这两个地方。这让我想到了一个重要的问题。
如果服务器停机了,那么它所承载的应用程序会发生什么情况呢?
架构师:我们可以使用具有故障转移功能的服务器,所以,如果一台服务器停机,另一台就会接管它的职能。
既然你有两个数据中心,我们还可以使用灾难恢复功能,由另一个数据中心提供环境的副本。
即使某个数据中心完全崩溃,我们还可以让另一个数据中心来接替它。
客户:我很喜欢这个主意!你能给我一份关于所有这些内容的提案吗?
作为一名架构师,您现在获得了非常有用的数据,它们将帮助您做出决策,以构建一个可靠的故障自动防护系统。下一部分将关注技术性更强的上下文中的需求。
对于相同的非功能需求的主观描述
您可以根据清单 1 生成系统的非功能需求,如表 1 所示。
表 1. 非功能需求
需求 | 对于拨号连接和宽带连接,分别在 15 秒和 10 秒内做出响应。 |
可用性 | 系统每年必须至少有 363 天处于可用状态;可接受的停机时间仅为 48 小时。 |
可伸缩性 | 系统必须能够支持 2 千名用户,其中 100 人为并发访问。 |
故障转移和灾难恢复 | 如果主应用程序服务器崩溃,必须能够通过故障转移将系统移到另一台服务器。此外,该系统还必须能从灾难中恢复(例如,数据中心停机或变得无法访问)。 |
现在需求已经确定,我们来看看对铁路订票系统的设计,以处理所有需求。
体系结构模式
这一部分在铁路订票系统的上下文中对前一部分中定义的四种非功能需求进行讨论。基本的体系结构模式将作为一个起始点,告诉您如何循序渐进地利用每一种模式改进和处理各项需求。
单服务器部署
单服务器部署是最基本、最原始的体系结构模式,在生产级的部署中,这种模式并不可取。但如果是为了进行开发和单元测试,使用这种体系结构也是可行的。在这一设计中,所有服务器组件是由同一台服务器承载的。如果这台服务器停机,结果是显而易见的。图 1 显示了部署体系结构。
图 1. 部署体系结构
因为整个应用程序都承载在同一台 Web 服务器上,这种部署方式的缺点多于优点。
优点
部署方便。您可以很方便地将所有组件部署在一台服务器上。
维护方便。因为所有东西都被放在同一台服务器上,维护应用程序变得极为简单。
缺点
系统的性能与并发用户的数量成反比。当并发用户增加时,系统性能开始下降,因为所有请求都是由一台服务器提供服务的。
服务器的可用性对应用程序的可用性有直接影响。如果服务器停机,应用程序也会停止,变得不可用。
这种类型的体系结构未采用分布的方式,因而难以进行扩展。所有应用程序层(表示、业务逻辑和数据)都是由同一台服务器承载的。
您通常是在进行开发和单元测试时使用这种部署方式的。对于关键性的应用程序部署而言,这显得过于原始。在某些方法中会通过应用其他模式来改变这种部署方式;下面这部分将就其中的一种设计策略进行讨论。
使用负载平衡器进行部署
如果要克服单服务器部署的缺点、提高可用性,最简单的方法就是添加多台服务器,以便为请求提供服务。但这引入了一个新问题,即最终用户必须知道所有服务器的地址。如果某台服务器出现故障,用户就只能手动连接其他站点。这不会为用户带来最佳的体验。
有种叫做负载平衡的部署技术可以修复这个问题。客户机将请求发送给某个负载平衡器,而后者则将请求定向到实际的服务器。这个过程对于客户机或用户是完全透明的。负载平衡器将请求定向到服务器时可供采用的算法很多。循环 (Round Robin) 算法是其中常用的一种,它会让负载平衡器将后续请求发送给队列中的不同服务器。负载平衡器的优秀特性之一是,利用它们可以通过编程手段实现粘性。当负载平衡器将客户机的请求定向到某个服务器时,“粘性”会使来自该客户机的所有后续请求都发送到同一台服务器。
铁路订票系统的布局
图 2 显示的是铁路订票系统的布局,该布局中应用了负载平衡器。
图 2. 采用负载平衡的服务器
在此场景中,负载被平均分布到两台服务器。
优点
最直接的好处是能提高可用性。即使一台服务器停机,另一台服务器也能为请求提供服务。
由于总负载被分布到不同的服务器,因此性能和可伸缩性也略有改善。
缺点
应用程序体系结构仍然不是分布式的。所有应用程序层组件都由同一台服务器承载。
这一模式处理了部分(而不是全部)非功能需求。下一种模式将介绍如何利用分布式组件做出更多的改进。
分布式服务器部署
正如负载平衡器的那个场景中介绍的那样,服务器负载实现了分布,但应用程序采用的仍是非分布的方式。表示、业务逻辑和数据都承载在同一台服务器上。虽然对许多应用程序来说,这样做已经足够了,但如果您希望将 Web 层与其他层分离,以将它放置在非专用区域中,那么您将需要一个单独的 Web 服务器和多台应用程序服务器,如图 3 所示。
图 3. 分布式服务器
在这个场景中,将按请求和服务器执行的任务类型对负载进行均匀分布。这个体系结构无疑能更好地适应较高的性能和可伸缩性。一旦您实现了一个分布式的部署体系结构模式,您所要做的就只是添加更多的服务器并收集度量,直到您拥有的服务器数能够满足非功能需求为止。
优点
更高的可用性。由于有多台服务器可用来为请求提供服务,即使其中一台服务器停机也可以轻易地对请求进行处理,从而提高了应用程序的可用性。
使性能得到改善。采用单独的 Web 服务器,能更有效率地处理请求增多的情况,从而改善应用程序的整体性能。
更好的可伸缩性。通过使用该模式,您可以在不影响应用程序可用性的情况下动态添加新的服务器,使应用程序具有高度的可伸缩性以应付更大的流量。
缺点
应用程序服务器仍然充当了单点故障的角色。
您可能已经注意到,随着体系结构模式的一一引入,我们离完成系统非功能需求的目标越来越近。到目前为止,我们已经看到在可用性、性能和可伸缩性方面做出的改进。在下面几个部分中,我们将引入更多的模式,它们将提供更多的改进。
分层部署
在分布式服务器部署模式中,应用程序服务器仍是一个单点故障。但是如果应用程序服务器停机了,那又该怎么办呢?Web 服务器仍然可以为静态请求提供服务,但动态请求将会失败。对于关键的应用程序,如我们针对铁路订票系统设计的解决方案,您需要的是一个具有故障转移功能的设计。
将服务器部署在集群中,可以将故障转移功能引入服务器。集群是一组执行相同任务的服务器,在外界来看,它们就像是一台服务器。图 4 对此概念进行了说明
图 4. 提供故障转移功能的应用程序服务器集群
在这个场景中,当服务器 1 出现故障时,应用程序服务器集群将利用故障转移将工作由服务器 1 转至服务器 2 来处理。您可以通过配置,在双忙或忙-闲服务器对中进行操作。如果是双忙服务器对,那么两台服务器都在持续处理请求。如果是忙-闲服务器对,则空闲服务器只在集群通过故障转移将忙碌服务器的工作转交过来时才会执行请求。当忙碌服务器恢复后,集群将自动把工作由空闲服务器转回忙碌服务器。如果集群中的备份服务器比主服务器的配置低,这种配置方式将会有所帮助。
优点
高可用性。
更好的性能。
更好的可伸缩性。
没有单点故障——这是使用服务器集群的一项附加的好处。应用程序可以通过故障转移转至另一台可以处理所有请求的服务器,这一切对于客户机和用户都是透明的。
缺点
如果数据中心停机,则应用程序也会停止。因为整个应用程序都承载在一个单独的数据中心中,所以如果数据中心在一次灾难中停机了,您的应用程序或服务会变得不可用。
在这种情况下,数据中心代表的就是您的应用程序服务器所处的物理位置。
迄今为止的各个体系结构模式几乎涵盖了所有非功能需求。您要做的最后一件事,就是使应用程序具有从灾难中快速恢复的能力。最后一种模式将告诉您如何实现这一点。
故障自动防护部署
如果所有服务器都是由同一个数据中心承载的,当整个数据中心停机时,应用程序也会停止。这样的事件是一次灾难。应用程序如何才能从灾难中迅速恢复呢?如果发生了灾难,应采取怎样的业务连续性方案?虽然很多应用程序可能无需进行复杂的灾难恢复,但高端的重要应用程序确实需要具有发生灾难时的可恢复性。您将已经完整部署的环境复制到另一个不同的数据中心中,可以在发生灾难时保证业务连续性,如图 5 所示。
图 5. 灾难恢复体系结构
由主数据中心到备用数据中心的切换可以手动或自动完成。如果是手动切换,必须将记录 Web 站点的 DNS 条目改为指向备用数据中心中的负载平衡器的实际 IP 地址。这一任务可以通过全球站点选择开关 (Global Site Selector Switches) 等设备自动完成。这类体系结构的设置颇为复杂,需要大量的每日备份工作,而且必须合理地执行恢复过程。图 5 中所示的一个重要项目是,来自数据中心 1 的数据库中的所有数据必须备份到数据中心 2 的数据库中去。
优点
使您的应用程序或服务对客户机和用户具备高度的可用性。
高度的可靠性。
最佳的用户体验。因为您的应用程序在全年中的任何一个时刻(无论白天黑夜,工作日还是假日)都处于可用状态,这将带来非常愉快的用户体验。
缺点
实现的成本过高。您需要硬件来支持双服务器部署,这可能要花去您一大笔钱。
维护成本增加,因为您必须将每条信息都复制到两台独立的服务器中。
需要大量工作才能实现完全的可恢复性。
到目前为止,所使用各种体系结构模式已经涵盖了所有需要解决的问题。有些模式很容易实现,有些则需要更多的工作。还有一个模式是您不应在此次讨论中遗漏的,那就是 MVC 模式。您可能已经听说过该模式,或许还在您的设计中使用过它。
模型-视图-控制器 (MVC) 模式
MVC 是软件系统中最常用的体系结构模式之一。它的简单性不但使它易于理解,还使您可以在设计中轻松地实现它。MVC 模式是一种将应用程序(或应用程序的子系统)分解为下列部分的方法:
模型:代表所传递的数据
视图:指客户机或用户使用的实际用户界面 (UI)
控制器:控制数据流,定义和控制 UI 中的事件流
这一模式允许 UI 考虑因素(视图)与数据(模型)分离,以使对 UI 做出的更改不致于影响数据处理,并可以在不改变 UI 的情况下对数据进行辨识。图 6 显示的是一个 MVC 系统的基本设计。
图 6. 使用 MVC 模式设计铁路订票前端
MVC 模式用一个中间组件(即控制器)使数据访问和业务逻辑从数据表示和 UI 中分离出来,从而解决了数据依赖性问题。
优点
设计清晰。将系统分解成各自具有特定功能的组件,能使系统设计变得更为清晰。
可以使用多视图。将视图从其他两个组件中分离出来,可以在不影响下总体设计的情况下改变视图。该模式还允许同一系统使用多个视图。在铁路订票系统中,这项功能可能会变得非常有用,因为有时我们的系统可能需要为现有分支机构使用的老式绿屏计算机提供支持。
易于更改。有了 MVC,可以很轻松地对系统中的不同部分做出更改,而不致于对其他组件造成较大的影响。
缺点
它引入了新的复杂层次。将组件分离开来,并引入新的间接层次,这种做法的缺点是使解决方案的复杂度略为提高。
虽然 MVC 有少许缺点,但它仍是软件设计中最常用的体系结构模式之一。有许多框架使用 MVC 作为其设计的基本模型,如 Apache Struts、Spring、Stripes 和 Tapestry。
接下来的部分将讨论在您的设计中使用框架。
在您的设计中使用框架
如果要使您的应用程序具有更高的可伸缩性,一个非常有用的高效方法是使用现有的软件框架。框架是一个可供软件系统重用的设计。应用程序框架往往是一组预先设计好的库或类,它们可以用来实现应用程序的标准结构,以解决某个特定的设计构件。
在铁路订票系统中使用 Struts 和 Spring 等框架有一个巨大的优势,那就是它们能降低整体开发时间。这两种框架中都包含了大量预先设计好的面向对象的应用程序编程接口 (API),几乎可以立即用在您的应用程序中。虽然开发人员必须学习如何使用 API,但这还是比从头开始来得快些。框架还提倡面向对象的概念,使您的代码在其他项目中具有更高的可重用性。接下来的几个部分将阐述 Apache 和 Spring 框架是如何改进铁路订票系统的设计的。
Apache Struts
在构建基于 Servlet 和 JavaServer Pages(JSP 组件)的 Web 应用程序方面,Struts 是较为成熟的 MVC 框架之一。由于铁路订票系统的前端是 Web 页面,因此 Struts 将是我们的设计的理想之选。在一个典型的企业 Web 应用程序中的事件流是这样的:
用户使用 Web 表格将信息提交给服务器。
该信息被提供给某个服务组件,后者会对信息进行处理,通常会在与数据库进行交互后生成一个 HTML 格式的响应。
或者,如果服务使用了 JSP 这样的服务器技术,则 JSP 和 Java 代码会负责生成同样的 HTML 响应。
用户在视图中看到结果页面。
对于大型项目而言,仅有这两项生成响应的技术是不够的,因为大型项目中的表示逻辑和业务逻辑会混杂在一起,使维护变得十分困难。Struts 的目标是,按照 MVC 模式将模型与视图和控制器清晰地分离开来。Struts 有一个定义良好的框架使这一任务成为可能。
优点
作为构建应用程序的基础,它是一个十分强大的框架。您无需自己设计 MVC,可以立即将 Struts 用在您的 Web 应用程序中。
能加快开发周期(使用框架的最大好处之一)。
提高可伸缩性。设计 Struts 的架构师们付出了真诚的努力,使您的应用程序拥有尽可能高的可伸缩性。
缺点
可能会发生未知的问题。如果 Struts API 中有错误,您只能等着 Struts 团队来修复它。
Struts 的优点使它成为开发铁路订票系统的有力候选者。Struts 本身的设计还有助于实现组件的高度分离。我们的设计的目标之一就是代码重用,只有与这样的努力相结合,Struts 才能有所帮助。
除了此处介绍的几点之外,关于 Struts 的内容还有很多,不过,如果要一一叙述,就会超出本教程的内容范围了。
Spring 框架
Spring 框架比 Struts 强大得多,它是另一个基于 MVC 的轻量级框架。它解决了应用程序架构们和开发人员所要面对的一个重大问题:实现组件的高度分离。Spring 使用一种名叫 Inversion of Control 的特殊技术,以帮助消除组件间的依赖性。由于组件间不是彼此紧密耦合的,每个组件的测试过程也得以简化。
在铁路订票系统设计中,Spring 可以使用多种不同的方法:
Spring MVC 是与 Struts 类似的 Web 应用程序组件,可以用来设计应用程序的前端 Web 界面。
Spring 有一个灵活的 Java Database Connectivity (JDBC) 和 Object Relational Mapping API,它们可以增强系统的数据库连接设计。
Spring 还支持面向方面的编程,这有助于我们在设计中实现更高的可伸缩性。
Spring 框架提供了许多 API,它们在实现铁路订票系统所需的可伸缩方面可能会很有用。您可以在“Apache Geronimo and the Spring Framework”这篇教程中了解到更多内容,该教程共分为六个部分。
总结
在本教程中,您已经了解了可以用来改进应用程序的性能、可用性和可伸缩性的多种方法。满足系统的非功能需求中的每项要求,这是有可能做到的,但如果要设计一系列适当的、能够解决所有问题的部署解决方案,还需要进行一番认真思考,并应具有专业技能。与设计模式类似,体系结构模式为身为架构师的您提供了一条学习和改进设计的方法。
本系列共分两部分,希望它能为您在设计和体系结构模式方面提供一些认识,帮助您解决自己的应用程序难题。如果某个应用程序性能较差,这一定是有原因的。您通常可以使用本文中介绍的某个模式,找到这个原因并予以修复。
更多精彩
赞助商链接