第二章:NAS性能、扩展性和可靠性


(by huihoo.com tonyliu)

我们无意于强调NAS比Netscape Enterprise Server(Netscape的Web服务器)更好,它们是为不同的目标而设计的。在Web发展的早期,人们主要用它来发布信息,因此开发者只对如何使用HTTP协议提供HTML文档感兴趣, Web服务器(如Netscape Enterprise Server)就是为此目的而设计,它为相对较轻的应用代码提供通用运行环境,主要通过返回HTML页面来响应用户HTTP请求;在上述前提下,它工作得很好,但随着应用不断深入,人们需要开发更为复杂的Web应用程序,为此,不同的在Web服务器上开发Web应用的方法和访问数据库的方法被加入,但这种增强仅仅是功能的增加而不是体系结构的优化,所采用的技术手段如Server端插件、语言翻译(Perl等)也不能说真正实现了相对于Web环境的本地化处理。 不同于任何Web服务器,甚至是其他应用程序服务器,NAS 是专门为基于高端处理的Web应用而设计的。1995年, Keng Lim 提出了一个全新的概念:有朝一日,Web应用程序将需要一个新的运行环境,该环境能提供很高的可靠性,健壮的程序逻辑处理,能轻松地为成千上万甚至上百万用户提供服务。他的概念变成了Kiva Enterprise Server产品,该产品于1997年被Netscape收购并集成进NAS,因此可以说,NAS从体系结构上就专注于提供大负载、高可靠性的服务,可以满足接近于无限的用户数增长需求。 这就是NAS和Web服务器在定位上的区别,下面我们再就更细的技术层面来论述这个问题。

WEB 服务器和CGI: 访问数据库

Web服务器工程师很早就意识到通过Web来完成数据处理和显示的重要意义。最早期的处理方法是通过通用网关接口(CGI),它们可以用不同的编程语言实现,包括C/C++ 语言和Unix 脚本语言(如Perl和tcl) 图1显示了CGI脚本是如何工作的,一个请求被从Web浏览器发出,(例如:http://server.domain.com/cgi-bin/myCGI.pl),该请求实际指明的是一个服务器端程序而不是一个标准Web页面,Web服务器执行被请求的应用程序(myCGI.pl) ,随后将输出结果返回给Web浏览器。因此,当你发出一个CGI请求时,你不是在请求HTML文档,你实际上是在请求服务器端程序的输出。

图1 通过CGI脚本获取数据



这种方法的最大局限是当每次CGI脚本被激活时服务器必须启动一个新的进程,处理过程结束后进程随之结束。换句话说,CGI过程不能在本次激活和下一次激活间保持,这就造成了高的进程开销,限制了给定时间内可以运行的CGI脚本数目,降低了Web服务器的处理速度(有一种被成为“快速CGI”的解决方案将CGI脚本置于内存中,用以提高CGI应用程序的性能)

CGI还有其他一些影响性能的限制,服务器必须为每个Perl CGI脚本运行Perl解释器, C/C++ CGI脚本比Perl脚本运行速度快但是更复杂,需要更多的编程技巧。在对数据库操作时,开发者需要使用与数据库相关的内嵌SQL或函数库,一旦更换数据库,需要大量代码改写。此外, CGI脚本在每次执行数据库操作时都要执行数据库连接和断开这两个费时操作。CGI的另一个缺点生成Web页面的HTML代码与实际程序代码(C/C++, Perl等)紧密耦合,改变页面外观可能涉及对CGI程序的修改。

尽如此,CGI脚本在工作组级(少量用户)的情况下还在被广泛地使用,因为它相对简单,易于开发,能够在不同的Web服务器间移动(虽然很少有这么做的必要)。对于那些对性能和可维护性要求不高的网站而言,对于简单、一次性的系统任务而言,CGI还是一个理想的方案;而对于数据库驱动的Web应用程序和必须长时间维护的Web站点而言,CGI脚本是一个糟糕的解决方案。.

WEB服务器和服务器端JavaScript: 改善应用程序性能

在基于Web服务器的Web应用方面还是有明显优于CGI的技术存在,服务器端JavaScript(SSJS)就是其中之一,它被Netscape于1996年提出,随后很快被Microsoft仿制。它被认为是优良的工作组级和部门级解决方案。

SSJ运行环境引擎(NSAPI插件)在Web服务器内运行(参见图2),因此它是持续可用的;因为Web服务器无需在每次使用SSJS时实例化一个新进程(如同CGI所做的一样),它能更快、更有效地处理请求。

图2 通过服务器端JavaScript获取数据

CGI脚本把HTML代码包含在程序逻辑内,而SSJS程序用JavaScript语言编写,并被存放在HTML页面(类似于客户端JavaScript的实现),因此构成应用程序的Web页面可以被HTML开发者而不是应用程序开发者修改。

基于CGI的Web程序通常由两种语言构成(前端是客户端JavaScript,后端是Perl或C/C++),基于SSJS的Web程序仅采用JavaScript语言,在程序开发上更具一致性。此外, 因为SSJS是客户端JavaScript核心语言的超集,开发者可以在客户端程序和服务器端程序间共享代码, 同时JavaScript是相当流行而且简单的编程语言,寻找JavaScript程序员比Perl或C/C++程序员更为容易。 在数据库操作方面,SSJS程序比CGI脚本更友好,执行速度更快。SSJS程序不需要在处理每次请求时都连接和断开数据库,这是因为SSJS对象具有与Web服务器同样长的生命周期,DBPool对象可以永久保持与数据库的连接,也能同时建立多个与不同数据库系统的连接(SSJS支持访问Oracle, Informix, Sybase, DB2以及所有的ODBC数据库),在处理并发访问方面效率更高。

SSJS允许你最大限度发挥三层体系结构(three-tier architecture)的优点。在传统的两层客户/服务器结构(two-tier client-server architecture)下,客户直接连接到数据库,中间不需要通过Web服务器或者其他服务器。这在数据库处理和许可方面都是比较昂贵的,每个客户都需要连接到数据库,都需要访问数据库的许可。而在三层结构中,多个客户可以访问一个Web服务器,而由后者维护与数据库的共享连接,因此只要更少的数据库许可和数据库处理开销。 图3比较了两层结构和三层结构,可以看到在两层结构中,每个客户都有一个到数据库的连接,这意味着当用户在读取或输入数据时,数据库连接实际处于空闲状态。而三层结构在数据库连接的使用上效率更高,由于数据库连接被共享,因此可以用更少的数据库连接支持更多的用户。数据库连接被更高效地利用,通过共享连接的数据流相对更连续。另外,数据库的连接可以一直保持直到应用程序结束,因为用户是登录到位于服务器的应用程序上而不是数据库上。

图3 两层结构与三层结构的比较



SSJS还提供状态管理和会话管理功能,上述功能已经成为SSJS运行环境的一部分。

会话管理用于:
确定用户是否已经登录
监视用户活动
提供用户活动记录
存储同一个应用程序内不同页面上需要使用的用户信息
跟踪多个HTTP请求之间需要保存的信息 等

状态管理用于监视应用程序使用情况,检测应用程序正在做什么、多少用户正在使用它等,以利于信息和资源在用户间共享。 SSJS为状态管理和会话管理定义了四个对象:

Server对象:用于在应用程序间共享信息
Project对象:主要用于状态管理
Client对象: 用于会话管理
Request对象:用于提供基于页面的信息,便于访问被传递给页面的数据

这些对象始终保持运行和对应用程序可用,开发者只是需要决定会话信息是存储在客户端(如果服务器停机,重启后不会丢失任何用户会话信息),还是存储在服务器端(能支持并未激活客户端cookies的浏览器) SSJS比CGI(它是无状态的)有所改善,但还不是最理想的,因为状态和会话信息还不能在服务器之间共享。这是难于将SSJS扩展至工作组和部门范围以外的最大原因。如果你在SSJS程序中没有使用状态/会话信息,你可以很好地将系统扩展至工作组以外;但如果使用了,你就有可能碰到麻烦:如果你的系统是一个多台服务器的环境,如果其中一台服务器在处理过程中停机,用户可能丢失定单或数据库信息,他们不得不回到最初的地方重新开始,即使在这过程中他们被自动重定向到另一台服务器上。

正如我们所知道的,SSJS不是一个针对企业级关键应用的真正理想的方案。在企业级关键应用中,许多用户和应用程序(包括相关的状态和会话信息)必须全天候保持正常运行,而Web服务器毕竟不是专门设计用来处理数据库访问、分布式状态和会话管理、关键性任务处理的,Web服务器主要还是处理Web页面访问,通过插件它只能提供有限的应用程序方面的功能。

NAS: 可扩展的应用程序服务

NAS的出现极大改善了这个局面,它可以在同一服务器簇(cluster)内多台服务器间共享状态和会话管理。一个服务器簇由两台或多台主机组成,一旦一台主机停机,其余主机可立刻顶替,因为状态和会话信息是共享的,不会出现事务丢失。

NAS的设计出发点是提供应用程序服务,对于给定应用程序,不用考虑用户数量和并发请求数量的影响。如果用户群数量增加,只需要加入另外的应用程序服务器来处理负载,而应用程序无需作任何修改。在一个服务器簇内可以增加任何数量的服务器主机来支持给定应用程序。例如,香港电信的customer care and billing application支持超过200,000用户;2个月内开发完成的E*Trade's Mutual Fund application支持275,000 用户; 4个月开发完成的ISN's First Auction site 及其"flash" auctions支持8,000以上的并发用户。

NAS程序由HTML页面和应用程序逻辑(AppLogic)模块组成,如图4、图5所示,请求被Web服务器处理,如果关系到应用程序,请求就被转发到应用程序服务器处理。在应用程序服务器内部,请求被KXS进程(Kiva executive)接收,再转发给KCS进程(内部运行C/C++ AppLogic模块),或者KJS进程(内部运行Java AppLogic模块),每一个KCS和KJS进程维护特定数量的线程(由系统管理员配置),由这些线程负责执行AppLogic模块。这种方式提供了比CGI脚本和SSJS程序更快的响应速度,不会减慢Web服务器的速度。当一个AppLogic模块结束运行,结果被返回给Web服务器,最终返回给客户浏览器。

图4 NAS工作示意



因为你可以增加任何数目的服务器,指定任何数目的KCS和KJS进程以及在每个进程内维护任何数目的线程,你可以很好地调优你的NAS环境,给你的应用程序带来最好的性能。这种细致调优无疑会导致更低的硬件需求和对存在的服务器的更高效利用,而这一点是CGI、SSJS或一般Web解决方案所无法提供的。

NAS是如何保证24*7(每天24小时,每周7天)的可用性的?图5从直观上显示了NAS内部是如何工作的。如果一个KCS或KJS进程意外终止,KXS进程将会重启进程。 如果KXS意外终止,则会有一个附加的管理进程(KAS)负责监视并重启KXS。NAS还会有其他的检查手段保证KAS始终运行。而当所有的进程都意外终止,那么同一服务器簇内的其他的NAS服务器就会接管服务,同时NAS会立即发送Email或传真通知系统管理员。

而对于CGI或SSJS,如果服务器意外停机,这个情况可能根本无法发觉,除非使用客户或雇员打电话来抱怨。NAS则使你早于用户发现意外情况,使你能及时修复存在的问题,免于整个应用系统崩溃。

图5 NAS内部工作原理



NAS的存在可以使HTML与程序代码完全分离(CGI脚本的HTML嵌在程序代码中,SSJS稍好一些,程序代码嵌在HTML中),这个优点有利于应用程序的团队式开发,页面设计者只需专注于显示逻辑(HTML页面,Java GUI等),编程人员只需专注于业务逻辑(AppLogic模块和扩展),数据库管理人员只需专注于数据逻辑(SQL请求,视图,存储过程,函数等,以上内容被存放在请求文件中,与AppLogic模块分离)。工作组的每个成员都可以不受他人的干扰,集中、高效地完成本职工作。

若干建议

NAS适用于每个人吗?当然不是。有些站点规模很小,不需要支持大量的应用程序。而有的公司仅仅能支付得起一台主机和一个Web服务器的费用,当然不能期望他们使用NAS。我们只是希望他们了解NAS,知道当他们有进一步复杂应用的需要时,NAS是一个非常好的选择。

对于那些期望在未来(而不是现在)采用NAS以满足其站点流量增长和应用程序增强的用户,他们可能会思考现在能做什么?下面是我们的建议:

如果你使用CGI脚本,请考虑使用C/C++,以便将来在向NAS移植是不用全部重写。
如果你的Web开发人员不熟悉C/C++,Perl是一个不错的替代选择,但应注意,移植Perl应用程序到NAS时,需要将其改写为C/C++或Java。所以Perl脚本最好用实现支持少量用户数的单一性任务。
可以使用SSJS创建小的数据库应用程序,这是创建基于数据库的工作组级应用系统的一个简单方案。Netscape在现在和将来都会全力支持它,其他的Web服务器也正致力于支持SSJS。
用Java创建大型、任务关键性应用程序(使用Java Servlets、Java Beans等),以便在向NAS移植时最大限度地重用代码。
应当记住没有一种方法可以适合所有情况。每一个站点有不同的需求和侧重点。

CGI脚本适用在:你可能需要频繁改变Web服务器,你有经验丰富的Perl或C/C++开发队伍,你不是非常关心站点的可维护性,你只希望提供一个日常的、用于一般目的的解决方案等情况。

SSJS是数据库驱动的工作组级应用系统的理想解决方案。如果你的开发人员对Java语言已经很熟悉,那么使用Java

Servlets对于开发有一定规模的Web程序(包括数据库程序)是非常有用的。

NAS则是满足高流量站点(可能需要扩展至支持成百上千甚至上百万用户)和大型、任务关键性应用的完美解决方案。

方案可以不同,但是我们的最终目的是相同的,那就是使用最低的成本(包括初期成本、开发成本和长期维护成本),为客户提供最好的服务。基于Web服务器的方案目前还有大量应用,但当我们放眼未来,随着应用系统的日益复杂和用户群的日益增长,基于应用程序服务器的解决方案(如NAS)无疑能更好地满足用户的进一步需求。