第一章:应用程序服务器Application Server


(by huihoo.com tonyliu)

为什么要有应用程序服务器

近十年来,基于Internet的应用正以前所未有的高速度向前发展,其中一个重要的方向就是基于Web的应用系统的发展。在此期间,随着技术的不断更新和应用的不断深入,Web应用系统的发展也经历了几个阶段性的跨越。



在Web发展的初期,人们通常使用Web服务器作为信息发布工具,它接收用户的HTTP请求,然后根据请求提供所对应的HTML文档,除了显示手段更为丰富的特点之外,HTML文档与普通静态文本并没有本质差别。 随后产生了动态页面和更多交互能力的需要,新的应用需要根据客户请求,从数据库中动态获取数据并予以显示。由此所产生的技术手段有CGI和Web服务器内置API等。这些手段在一定程度上满足了应用需求,但也都存在着各自的局限性:CGI效率低、速度较慢,而内置API开发困难,移植性差。

随着Web应用范围的扩大,基于商业应用的Web开始出现,人们需要Web服务器端提供更为复杂的和更为灵活的应用开发支持。JavaScript、VBScript等技术的运用,Web中对象和组件的应用使基于Web的商业程序开发得以迅速发展。但这种应用通常只适用于构建规模不大的商业系统,当商业系统进一步扩大时,其扩展性受到了限制,因为在Web服务器最初的设计目的中并不包括对大规模、高性能和高可靠性商业应用的支持。

应用程序服务器(Application Server)的产生正是为了突破这一瓶颈,应用程序服务器完全不同于Web服务器,是专门为基于大负荷高端处理的Web应用而设计的全新的运行环境,该环境能提供很高的可靠性,健壮的程序逻辑处理能力,能轻松地为成千上万甚至上百万用户提供服务。

另一方面,应用程序服务器的产生与软件体系结构的演变发展也是密切相关的。让我们简单回顾一下软件体系结构的发展历史。 与最初的大型中央主机相适应,最初的软件结构体系也是Mainframe结构,该结构下客户、数据和程序被集中在主机上,通常只有少量的GUI界面,对远程数据库的访问比较困难。随着PC的广泛应用,该结构逐渐在应用中被淘汰。

在80年代中期出现了Client/Server分布式计算结构,应用程序的处理在客户(PC机)和服务器(Mainframe或Server)之间分担;请求通常被关系型数据库处理,PC机在接受到被处理的数据后实现显示和业务逻辑;系统支持模块化开发,通常有GUI界面。Client/Server结构因为其灵活性得到了极其广泛的应用。但对于大型软件系统而言,这种结构在系统的部署和扩展性方面还是存在着不足。

Internet的发展给传统应用软件的开发带来了深刻的影响。基于Internet和Web的软件和应用系统无疑需要更为开放和灵活的体系结构。随着越来越多的商业系统被搬上Internet,一种新的、更具生命力的体系结构被广泛采用,这就是为我们所知的“三层/多层计算”。

一个典型的三层模型如如下图所示:



。客户层(client tier) 用户接口和用户请求的发出地,典型应用是网络浏览器和胖客户(如Java程序)
。服务器层(server tier) 典型应用是Web服务器和运行业务代码的应用程序服务器
。数据层(data tier) 典型应用是关系型数据库和其他后端(back-end)数据资源, 如
Oracle和SAP、 R/3等

三层体系结构中,客户(请求信息)、程序(处理请求)和数据(被操作)被物理地隔离。三层结构是个更灵活的体系结构,它把显示逻辑从业务逻辑中分离出来,这就意味着业务代码是独立的,可以不关心怎样显示和在哪里显示。业务逻辑层现在处于中间层,不需要关心由哪种类型的客户来显示数据,也可以与后端系统保持相对独立性,有利于系统扩展。三层结构具有更好的移植性,可以跨不同类型的平台工作,允许用户请求在多个服务器间进行负载平衡。三层结构中安全性也更易于实现,因为应用程序已经同客户隔离。

应用程序服务器是什么

应用程序服务器是三层/多层体系结构的组成部分,应用程序服务器位于中间层。如上图所示,应用程序服务器运行于浏览器和数据资源之间,一个简单的实例是,顾客从浏览器中输入一个定单,web服务器将该请求发送给应用程序服务器,由应用程序服务器执行处理逻辑,并且获取或更新后端用户数据。

在企业级应用中,应用程序服务器是位于企业数据(以及其他企业遗留系统)和访问企业数据的客户之间的中间件,它提供了业务代码的存放和运行的环境。它从物理上把业务逻辑(Business logic)同客户端和数据资源分离开来。应用程序服务器可使一个商业系统得以快速简便的开发和部署,也可以适应商业系统的用户增加而无需重构系统,这一切都是因为它处于一个相对独立的结构层。 在实际应用中,一个企业系统可以由多个应用程序服务器、多个Web服务器和多个数据库服务器组成,应用程序代码可以分布在这多个应用程序服务器上。

应用程序服务器是企业级web应用的必经之路,新的企业级应用需要关键性应用能对不断增加的用户数量保持持续可用,此外企业应用还需要高的安全性和可靠性,无论访问系统的用户数量发生变化,还是系统数据资源发生变化,应用程序服务器都应始终保持运行。在应用程序服务器之前,Web应用程序通常运行在Web服务器中,但Web服务器最初只是被设计用来提供Web页面相关服务的,因此开发/运行Web应用程序是缓慢而复杂的。而应用程序服务器的强大功能则能够为企业级应用系统的开发和部署提供有力的保证。

一个对应用程序服务器的定义如下:采用具有分布式计算能力的集成结构、支持瘦客户机的软件服务器产品。应用程序服务器的基本用途包括:管理客户会话、管理业务逻辑、管理与后端计算资源(包括数据、事务和内容)的连接。

上述定义的涵盖范围是应用程序服务器本身,我们认为实际上应用程序服务器还应该包括其开发环境。每一个应用程序服务器产品都有自己的API。为了充分利用应用程序服务器所提供的功能,开发人员必须学会用这些API编程。服务器的API对实用系统的开发至关重要,掌握API方法和它们提供的功能需要花一定的学习时间。

越来越多地,应用程序服务器采用诸如COM、CORBA、Enterprise JavaBeans(EJB)和Java Servlets等标准化技术(其中CORBA和EJB更为常用),上述标准化技术的采用使开发变得更加容易,因为它们为通用服务器管理功能提供了标准化API,遵循标准保证了当更改应用程序服务器时原有组件可移植。

但另一方面,每个应用程序服务器产品提供商都会对标准加以扩展,增加自身特色以赢得竞争优势。这就导致了Web世界中的大量争论,对标准的兼容成为热点问题。

一个很重要的问题摆在Web应用系统开发者的面前:我需要应用程序服务器吗?

答案通常是需要,尤其当你要构建一个企业级系统时。即使你没有可扩展性的需求,你也能从应用程序服务器中得到很多其他收益。基于Web的开发已经迅速从数据发布、少维护量的应用转向完整、复杂的商业应用。应用程序服务器可以简化新的商业交互程序的开发;随着应用系统规模的不断扩大,逻辑重用和团队开发变得越来越重要。此外,由于应用程序需要获得组织内更多的数据资源,对数据访问的集中管理和集中安全也越来越重要。应用程序服务器通过对象和组件技术为此提供了很好的解决方案。

如果你需要构建同时支持HTML客户和胖客户的应用系统,你肯定需要应用程序服务器。作为三层客户/服务器结构你当然不能缺少中间层!你同样可以利用应用程序服务器的业务逻辑和数据访问组件。

应用程序服务器必须从某个地方获得它所需要的组件(业务逻辑、数据访问对象、HTML生成组件等),这些组件都要被加载到服务器中。组件的开发和加载的容易与否取决于开发环境,有些应用程序服务器提供商依靠第三方IDE和工具来为服务器提供组件,另一些供应商则把开发工具与服务器集成在一起。你可以根据具体需要来作出选择。

应用程序服务器可以分为以下几大种类:

1、纯运行Web应用服务器(Pure-Play Web Application Server)

这类服务器是与开发工具独立的,支持某些种类的标准组件模型如CORBA或EJB。这类服务器产品供应商把主要精力放在支持扩展性和与不同数据资源的连接上。代表性产品有Netscape NAS,IBM WebSphere,WebLogic Tengah和Novera jBussiness。这些服务器倾向于以Java为中心,帮助开发者使用EJB组件和Servlets来构建系统。你可以使用喜欢的Java IDE来创建组件并加载到服务器中。

2、开发及部署服务器(Develop-and-Deploy Server) 开发及部署服务器将开发环境与应用程序服务器紧密集成。它们允许开发者迅速开发应用系统并立刻部署,它们的侧重点主要放在复杂环境中高度交互开发的能力。代表性产品之一是SilverStream Application Server和Sun NetDynamics。

3、来自Client/Server供应商的应用服务器(Application Servers from Client/Server Vendors)

另外一类产品由目前比较流行的工具供应商提供,这类服务器对习惯于传统的Client/Server方式的开发人员比较有吸引力。比如Microsoft MTS和Sybase Enterprise Application Server。MTS采用COM组件模型(并没有太多应用程序服务器采用COM作为组件标准),而Enterprise Application Server 3.0 几乎能容纳所有组件(包括PowerBuilder组件)。这类服务器最主要的好处在于对原有代码的重用。

应用程序服务器到底能做什么

所有应用程序服务器有其共有的基本特性,包括连接管理,状态和会话管理,业务逻辑集中和数据库连接等。很显然,应用程序服务器必须支持允许客户连接的有关协议,必须有验证用户的安全机制。服务器进程的主要工作是管理客户连接,以及将到来的客户请求重定向到不同的后端资源。

安全服务(Security Services)

为了获得资源访问权,客户必须通过服务器的认证,因此服务器必须要有验证用户的安全设施。大多数应用程序服务器提供增加用户/用户组的机制和对组件的访问控制。此外,越来越多的应用程序服务器提供对安全服务的验证(如操作系统,LDAP等),更高级的安全通过用户主机上X.509数字认证的使用来实现。一旦用户身份被验证,服务器将允许或禁止他对组件的访问和所管理的数据连接。

状态和会话管理(State and Session Management)

服务器必须在同一用户会话过程中多次请求之间维护有关的状态数据。从扩展性方面考虑,运行在服务器上的组件应避免使用实例变量。在应用程序服务器环境中,服务器必须通过会话对象来保持用户数据。会话对象(Session Object)的唯一功能就是保存服务器端的数据,它可以简化复杂应用程序的开发,因为它可以使你不必在程序中编写有关使用cookies和隐藏字段来回传递状态的代码。同时它也使那些存在安全风险的数据远离浏览器端,增加系统安全性保证。一般情况下,在浏览器和服务器间来回传递的唯一信息只是会话ID。

对于胖客户机连接(Java,VB,PowerBuilder等),用户进程与应用程序服务器保持持久连接,客户与服务器端的持久性对象间随时可能发生数据传递。由于服务器上组件变得更加状态化,更加绑定于某个特定用户,服务器所能处理的用户数将减少。因此开发人员更愿意采用如下方法:从服务器端请求数据,断开与特定对象的连接,在客户端本地处理数据,然后把数据变化发送给服务器以求确认并前推给数据库。在这种情况下会话对象变得不太重要,但它仍然提供了一种手段,使得服务器端的对象之间可以不经客户而直接传递数据。

负载平衡和失败恢复(Load Banlancing and Fail-Over)

所有的应用程序服务器都提供某种类型的负载平衡和失败恢复机制。负载平衡意味着一组服务器可以被集中成为一个服务器集群(cluster/farm)。发往服务器的请求被一个分配器(dispatcher)处理以便将请求发给最空闲的服务器,此后客户将直接与那台服务器通信。负载平衡机制为系统提供了很好的扩展性,随着用户负载的增加,更多的服务器主机可以被加入到服务器集群中。

失败恢复机制提供容错性。如果集群内某台主机失效停机,新的请求将被重新路由给其他某台服务器。简单的失败恢复不能解决所有问题,如果用户在某项任务过程中服务器停机,负载平衡机制将会发现这个问题并将该用户重新转配给其他服务器,但是该用户的状态和会话数据将对新服务器不再可用。正因为这个缺陷,有的应用程序服务器提供会话级失败恢复,状态和会话数据将被复制给集群内其他服务器或是被存储在永久性媒介(如数据库)中,使用户数据对每个服务器总是保持可用。

业务和处理逻辑(Business and Processing Logic)

在应用程序服务器的核心部分集中容纳了业务逻辑(即访问,生成数据和执行处理的代码)。与应用程序相关的业务逻辑可以由可重用组件组成,而不需要每个开发者自己开发。一旦组件被创建,就可以被载入到应用程序服务器中,分配适当的安全设定,然后就可以运行。大多数应用程序服务器提供机制用于指定组件的持久性,事务处理设定和线程设定等。

胖客户访问服务器组件(Rich-Client Access to Server Components)

胖客户可以直接访问服务器端组件。例如Java applet可以请求服务器实例化一个组件,然后就可以调用其方法。通常的执行顺序是:连接服务器,调用方法获得数据结果集,断开连接,显示数据给用户并允许对数据操作,执行本地数据更新,连接服务器,发送数据更新给服务器,执行服务器上数据更新,最后将数据存入数据库。

HTML生成

纯的瘦客户程序不能直接与服务器端组件通信,没有办法用客户浏览器的HTML或ECMA脚本交换结果集。这意味着服务器端需要另一个组件来生成和处理HTML。应用程序服务器必须解码(decode)一个URL,来决定执行哪一个组件。该组件访问数据库或其他组件,获得结果集,将结果集包装为HTML(或XML),再将其返回给浏览器。应用程序服务器上的HTML生成组件变成了业务逻辑组件的客户。

一旦页面被返回给浏览器,我们期待用户更改数据并提交回应用程序服务器。返回给服务器的数据可以作为URL参数(GET),通过表单字段(POST),或者通过cookie。服务器从HTTP请求中获取数据,使其为服务器端被调用的组件使用。

数据访问

应用程序服务器提供机制,用于增加和管理与关系型数据库(如Oracle,DB2)的连接。一旦数据源被加入,组件可以对其作调用,发布SQL请求。现在越来越多的应用程序服务器也允许访问非关系型数据源(如CICS)。对关系型数据的访问已经相当标准化(JDBC、ODBC等),而对非关系型数据的访问接口还是由每个应用程序服务器自己实现。第三方供应商也为不同的应用程序服务器提供接口产品,使其易于访问其他数据源(SAP,PeopleSoft,Notes和CICS/MQSeries等)。通常的做法是创建某种类型的对象或组件来包裹数据源,并使其功能可以为其他组件所使用。通过将非关系型数据源封装在对象中,前端开发者就可以提供对流数据的访问而不需要知道访问具体是如何实现的。

事务管理(Transaction Management)

Web开发人员和应用程序开发人员过去习惯于自己完成数据库事务管理。无论采用什么编程语言,几乎每个人都使用SQL访问数据,然后是确认或者回滚。而在应用程序服务器环境中,事务是由服务器唯一管理的。开发人员编写发布SQL请求的代码和通知服务器该逻辑是否成功的代码。如果该调用序列中任何组件执行失败,服务器将执行回滚;否则服务器执行确认。服务器提供不同级别的事务控制,例如它可以使某个特定的组件有其独立的事务。

连接池(Connection Pooling)

当页面被提交,数据被更新时,服务器必须连接数据库以完成工作。如果用户每次提交页面时都要执行数据库连接和断开,系统的性能和扩展性就会变得很差,因为在一个事务周期中建立到数据库的连接是一个开销相当大的操作。一个解决方案是当用户登录时建立连接,而当用户离开或超时时断开连接。但这个方案显然缺乏可扩展性,如果5000个用户同时登录,那么就需要5000个数据库连接。

应用程序服务器不为每一个用户保持单独连接。实际上,它维护了一个连接池,供所有服务器端组件共享。如果用户需要更新数据,相应组件就会请求服务器从连接池中分配一个可用连接,当所有的更新被完成后,组件负责通知服务器是否所有操作都成功。当一个事务结束后,连接被立刻释放。

线程池和实例池(Thread Pooling and Instance Pooling)

高端的应用程序服务器还提供线程池和实例池。因为对于线程或者组件实例而言,开销最大的操作来自创建和实例化过程。线程池和实例池可以提供更好的性能,线程和组件实例可以立刻被服务器所使用。但如果组件是有状态的,或者线程不能被快速释放,线程池和实例池机制将不能发挥其优点。