|
作者: NetDemon [netdemon] ADMIN | 登录 |
作者:NetDemon email:netdemon@20cn.com http://www.20cn.net/ 本文版权属20CN网络安全小组所有,允许转载,但需保持文章的完整性并注明出处 现在的Web技术五花八门,不断有新的技术出现,如果您想制作一个能承受巨大访问量的互动网站 (比如论坛等),那种方式最适合你呢?本文试图以比较实际的测试手法,对比一下各种技术的真 实效果,测试包括了各种语言编写的CGI程序在不同的web环境下的效率,主要针对unix上的情况 对windows的IIS,因为实际条件的限制,没法在同一个硬件条件下测试,因此没法完成. 阅读文章之前,我希望您先搞清楚一个概念,不要把CGI和perl语言联系在一起。 CGI英文全称是 Common Gateway Interface,通常翻译为共同网关接口,是HTTP服务器与机器上 的其他程序进行通信的一个接口。这个“其他程序”可以使用任何计算机语言来编写, 它通过CGI这个接口从HTTP服务器取得输入,然后把运行的结果又通过CGI这个接口交给HTTP服务器, 而HTTP服务器把这个结果送给浏览器。 perl是一种解释形的计算机语言,具有强大且方便的文本处理能力,被誉为计算机的“瑞士军刀”, 它被除了Windows之外的几乎所有的其他服务器操作系统所默认安装。BSD UINX 上有些系统命令 就是用perl编写。如whereis which adduser catman等。因为HTTP动态页面主要就是文本的处理 所以perl也就非常的适合来编写这个“其他程序”。这也就造成了一个很好笑的事实,上google 搜索CGI,出来的国内网站基本都是在谈perl,在很多人眼里,CGI成了一种语言,成了perl的代名 词。 CGI的出现让WEB从静态变为为动态,随着Web的越来越普及,很多的网站的都需要有动态的页面, 以便与浏览者互交。CGI方式的缺点也越来越突出。因为HTTP要生成一个动态页面,系统就必须 启动一个新的进程以运行CGI程序,不断地fork是一项很消耗时间和资源的工作。如果能够让HTTP 服务器本身就支持一种语言,用这个语言来编写动态页面的话,这就至少不需要fork。因此就出现 一种叫做动态网页设计语言的东西,如php,asp,jsp。这些不是真正的语言,他们都只是专门用于 web的,脱离了web环境,他们就什么都干不了(php正在努力改变这种现状)。 而SSI(服务端包含)是一种早期的较为简单及粗糙的动态方式,能够实现的功能很少,基本上现 在很少使用,但如果只是简单的处理,这也未尝不是一个好的方法。他是Http服务器本身所支持 的"语言"。 modperl简单点说就是类似php的方式,让apache http服务器本身能够支持perl语言,除了能够不需 要每一个请求就启动perl解释器来解释perl之外,还能够让你可以用perl来写apache的模块,让你 自己的程序成为http服务器本身的功能,以及用perl语言来设置httpd.conf等,详细情况请见 http://perl.apache.org/ 至于fastcgi,那又是另外的一种方式,他让这个CGI程序,以类似系统的守护进程(NT服务)那样的 运行,而不是每次请求都是启动一个新的进程,从理论上讲,这是目前最为有效的提高效率的方式。 而且和CGI一样,不局限于用什么语言来写。详细资料:http://www.fastcgi.com/ 无论用哪种方式,都不会比静态html快,我这里之所以把html的测试也包括主要是作为对比。 我在写这篇文章之前,也有其他的爱好者对这些方面做了一些测试并写有文章,但我觉得有些测试 其实不大现实,比如,有些测试的静态文件是<html><body>hello World!</body></html> 而cgi是print "<html><body>hello World!</body></html>"; ,然后用并发1000个以上的连接来测试, 但是现实我们怎么可能是这样的呢?如果只是把一个常量print出来,我们何苦用cgi?现实中我们 的cgi一般总是需要对传入的HTTP变量作适当的处理,然后读取一个或多个文件或是读取数据库, 继续处理,然后再输出。而实际的并发连接也通常不可能达到500或1000的数量。 因此我的测试尽量模拟比较现实的web行为,作为静态html参考的是20CN主页的"关于我们"这一页, http://www.20cn.net/aboutus.html ,如果你简单的浏览一下20CN的各个页面,你可以发现,各个页 面都有类似的地方,上方的logo,banner,主栏目连接部分是相同的,而下方的版权说明部分也是相同的, 只是中间的主要页面主体部分不相同,因此我们可以认为这个站的各页面有3部分构成,如果用动态web 的话,通常我们就是把相同的部分保存一个文件,每一页都输出这个内容,这样如果修改了这文件, 整个网站都更改,不需要每页逐一修改,这也是动态web的最基本应用了(很遗憾,20CN因为早年的现实 原因,实际并不是这样的实现的)。所以我就把这个about.html拆解为header.html fooder.html textbody.html三个文件,对应上、下、中。 而测试程序是这样,首先假设有一个输入,把这个输入变量处理分析过滤一通,然后读入这三个文件, 把textbody这一部分也处理一通,然后再print出来。因为实际的处理应该还不止这么简单,因此测序 程序我尽量使用非常没效率的处理方法作拟补。然后利用apahce自带的程序工具进行测试,并发连接 数量方面我只使用50个,所谓并发(同时)连接50是指当apache把一个页面的数据在给浏览器1发送的 这段时间内,他还同时还在给浏览器2 - 50发送,这样的情况相当于大概1000以上的人同时在线的网 站,才有可能出现这样的情况。 用于测试的几种程序都是一样的工作流程: 打开3个文件-〉过滤输入-〉生成一个12位的随机字符-〉在文件2中查找这个字符-〉把文件2中所有的 2、0、C、N的字符去掉-〉输出结果。(本来还有更加复杂的处理,但是用C实现起来很麻烦,就只能 如此了),而shtml因为没办法做这样的处理,他只是简单的include这3个文件而已,aboutus.shtml的 内容如下: <!--#include virtual="/header.html" --> <!--#include virtual="/textbody.html" --> <!--#include virtual="/fooder.html" --> 好了,说了这么多,我看你也不耐烦了,下面就是你所关心的最重要的测试结果: 1.html静态页面KeepAlive # ab -c50 -n3000 -k http://www.20cn.net/aboutus.html Keep-Alive requests: 2985 Total transferred: 25224674 bytes HTML transferred: 24266107 bytes Requests per second: 1346.50 [#/sec] (mean) Time per request: 37.13 [ms] (mean) Time per request: 0.74 [ms] (mean, across all concurrent requests) Transfer rate: 11321.67 [Kbytes/sec] received 2.html静态页面没有KeepAlive # ab -c50 -n3000 http://www.20cn.net/aboutus.html Requests per second: 916.03 [#/sec] (mean) Time per request: 54.58 [ms] (mean) Time per request: 1.09 [ms] (mean, across all concurrent requests) Transfer rate: 7675.88 [Kbytes/sec] received 因为其他的方式都不能使用KeepAlive,而且ab的结果中最有意义的就是Requests per second (每秒完成多少次请求),我们取 Requests per second 916.03 作为下面测试的比较基准 3.SSI # ab -c50 -n3000 http://www.20cn.org/aboutus.shtml Requests per second: 507.10 [#/sec] (mean) 4.Perl写的普通CGI # ab -c50 -n3000 http://www.20cn.org/cgi-bin/bench.pl Requests per second: 8.00 [#/sec] (mean) 5.C写的普通CGI # ab -c50 -n3000 http://www.20cn.org/cgi-bin/bench.cgi Requests per second: 29.26 [#/sec] (mean) 6.Mod-Perl # ab -c50 -n3000 http://www.20cn.org/mod-perl/bench.pl Requests per second: 200.03 [#/sec] (mean) 7.PHP # ab -c50 -n3000 http://www.20cn.com/bench.php Requests per second: 180.14 [#/sec] (mean) 8.Perl写的FastCGI # ab -c50 -n3000 http://www.20cn.org/cgi-bin/bench.fpl Requests per second: 262.05 [#/sec] (mean) 9.C写的FastCGI # ab -c50 -n3000 http://www.20cn.org/cgi-bin/bench.fcgi Requests per second: 503.44 [#/sec] (mean) 结果非常的明显,用C的fastcgi达到了和shtml几乎一样的速度,考虑到shtml几乎不需要处理什么, 我们可以认为C的Fastcgi比服务端包含还快。而perl写的FastCGI比C的性能差了近一半。但依然比 其他任何方式都快。Mod-perl和php的原理差不多,只是用的语言不一样,所以他们的测试结果也是 差不多。结果中最为突出的就是perl的普通CGI方式,他一定最低,这是测试之前就明白的道理。 但低到这样的每秒只有8次请求的程度,就大跌眼镜了,只有C的普通CGI的4分之1。任何用perl写的 以普通CGI方式运行的论坛阿什么的,说什么高速、海量都是完全吹牛的。本质就决定了它的不可能。 完成3000次请求历时6分多钟,让我都没有耐心等待测试结果,本来准备进行的写入的测试也不想做 了,留着等下回有耐心的时候再做吧。主要是测试带有写入,以及操作数据库的各种方式的差别。 测试环境: 硬件:PIII733 CPU 512M内存 IDE硬盘 OS:FreeBSD4.9-stable 软件:apache1.3.17+mod_fastcgi-2.2.12+mod_perl-1.27+mod_php4-4.3.4 Perl测试程序: 代码: [此贴被 NetDemon(netdemon) 在 02月25日18时00分 编辑过] |
地主 发表时间: 04-02-25 01:32 |
回复: Aoming [aoming] 版主 | 登录 |
好,既然老大今天这么有兴致,从实测角度给出了几中web动态交互过程的性能比较。 那我就花点时间打点字,从书籍中转载一条从理论上阐述关于Perl CGI性能的片段顶一下。 ========================================================================= 主题:Perl CGI的性能 出自:《Perl Web开发技术》(翻译版),机械工业出版社,王爱过,周丽萍 等 (<Perl for the Web>,Chris Radcliff) Perl CGI可能在开发网站的初期是一种常用方式。但在实际使用后采用Perl CGI,性能问题就开始变得很明显。这种情况在原型应用程序应用在实际环境中经常碰到,表现为较慢的响应速度、不寻常的行为和系统失败等。在一个站点遭遇Perl CGI脚本产生的性能问题之后,不可能再次允许其出现在实际环境中。 Perl CGI在测试一个Web程序的初期工作很好,但随着站点使用得越来越多,应用程序负载也更大,CGI进程产生越来越多的开销,并最终超过服务器的能力。处理器过载,内存被占满,数据库没有可用连接,系统将比预期地更早终止。 Perl 的随机编译器在这里是一个最大缺陷,执行Perl CGI进程时CPU的许多负载都来源于编译和初始化程序。最明显的结论是Perl相对当前任务响应太慢了,当然不会有任何公司会鼓吹Perl CGI的速度,在实际使用中,经过优化的Perl程序的例子更少。 ============ 一次性任务 ============ Perl CGI进程在原型测试环境下工作得很好,这时通常每次只有一个用户访问Web应用程序,并且所有界面延迟时间只是由于用户请求的处理引起的。这与Perl程序在Web处理之外的其他情况下的使用方式很类似,所以,那些情况的假设在此都成立,也就是每次只有一个请求,在请求完成后,程序和数据就不再需要了。 这些假定在演示原型时也是正确的。这时演示者是访问系统的唯一用户,通常处理过程产生的延迟很小并且也被解释过程给掩盖了。结果,CGI进程响应的缓慢性不可能在演示或可用性测试阶段觉察出来。即使单一用户注意到运行缓慢,也可能注重其他考虑而被忽视。 把原型投入实际使用就会截然不同。关于一个用户、很少的请求和没有对资源竞争的假设不再成立,应用程序的负载会按指数规律增长,这是由于Web请求的性质,他们不像单一用户应用程序中那样在同意时刻输入,也不像大多数用户应用程序(如数据库或群件)那样固定在一定范围之内。Web请求可能同时输入,并且数量可能不断增加,而不采取定单处理过程中排队请求那样简单的方式。相反,要求Web服务器存在和请求一样多的进程,附加进程的负载似的现有进程更加缓慢,因此,在处理以前进程的时候,会有更多的进程输入进来。在有限负载下,这种情况很容易失控,从而导致所有的Perl CGI进程阻塞停止,在处理过程中的所有请求都失败了。 ==================== Perl进程的内存痕迹 ==================== 一个Perl CGI进程可能占用1MB到15MB的RAM空间。占用这些内存的进程如果单独处理很容易管理,甚至不能使最一般的Web服务器崩溃。然而,CGI进程处理过程中使用的Perl编译器可能是静态链接而不是动态链接,导致一个不在进程间共乡的Perl解释器。如果许多CGI进程在同时运行,Perl解释器就可能复制无数次,将占用更多的内存开销。 如果新进程在旧进程没结束之前就开始了,新进程会站用更多的系统内存。随着站点的经常光顾的访问者的增多,将会产生更多的覆盖和系统资源的更大消耗。例如,如果一个站点的一个CGI应用程序每秒接到20个请求,那么最少需要2秒钟来处理,可能要40个CGI进程不停地开始和结束,这样会占用500M的RAM空间,随着请求率的增加,对一个实际的Web应用程序来说,100个请求每秒仍是一个非常保守的数字,内存请求增加不是只简单相加,而是呈几何基数地增加。每个进程通过增加CPU负载从而减慢其他进程,但这样又会增加必须的进程数,并且会恶性循环下去。这样即使用最昂贵的Web服务器,可用RAM的限制也将很快突破。 当可用RAM被CGI进程需求超过后,大多数服务器开始使用磁盘上的交换空间来为进程产生虚拟内存。这时,进程开始明显变得缓慢,处理一个进程的时间成10倍地增加,同时,活动进程的数量也同样增加了。进程总负载一旦超过系统的极限,所有进程都会终止。同样,请求失败,连接的系统可能会处在不确定的状态下,并产色怀念感不寻常的行为和管理上的混乱。 ============ 编译的费用 ============ Perl CGI的大部分开销来自于编译成字节码和初始化程序数据。每个Perl程序在执行前都要经过编译,每个程序(不管采用什么语言)都需要出事内存结构和运行时系统库函数。编译过程对任何在系统环境中不许要解释器就运行的程序都是必须的。 编译过程在运行一个单用户程序时很少能看得到,如 C 程序,编译过程需要几分钟甚至几小时。编译步骤也是检查系统相关错误的时候,所以如果第一个编译步骤没成功,就需要更多的调试,也就需要更多的时间。因此,编译过程通常在程序提交给用户之前就已完成了。即使开放源代码程序通常也只在目标系统上第一次安装时编译一次。 而Perl程序要在执行前进行编译,这使Perl程序可以更迅速地对系统环境变化做出响应,也比编译过的执行程序更容易跨平台移植。Perl runtime在几秒(或几毫秒)内编译每个程序,包括所有库函数和核心程序。Perl程序开始时同时开始编译和执行步骤,这就给人Perl是解释不是编译的印象。而Perl runtime(有时叫Perl解释器)需要在编译和执行每个程序时激活,这种印象也就更深了。 对有些Perl程序,编译时间不止几秒钟。程序更大时更是如此,因此要添加大量的模块到程序中。初始变量、系统连接和更大的数据结构都可能增加系统初始阶段的时间开销。例如,把较大的XML文档分析Perl数据结构,可能会占用相当的时间,这些额外的编译和初始时间造成了PerlCGI使用范围的限制,因为更复杂的过程可能会包含不允许的性能损失。 除了大多数集中处理器操作的Perl程序,编译一个Perl程序会比简单执行编译过程的程序占用更多的CPU时间,因为编译程序需要于Perl语言本身同样的文本操作、系统集成和磁盘输入/输出,但更复杂一些。实际在大多数情况下,一个Perl CGI进程占用比相同的经过预编译的程序多20倍的CPU处理器时间。 ====== 小结 ====== 建立Web应用程序的原型是Perl的用武之地,许多Perl程序开始是作为Perl原型的。Perl的灵活性和强大的系统集成模块使原型设计和实施特别容易。Perl CGI的性能在短时间内受到挫折,并且Perl CGI进程占用更多内存和CPU时间,最后可能使系统过载,完全终止Web服务器进程。这主要是由于Perl进程的实时编译的开销引起的,虽然是一、两秒钟,但这比程序本身还需要更多的处理能力。 [此贴被 Aoming(aoming) 在 02月25日05时04分 编辑过] |
B1层 发表时间: 04-02-25 03:56 |
回复: Aoming [aoming] 版主 | 登录 |
引用: 我觉得持续性连接和非持续性连接,以及编译的开销可以这样打个比喻: 2包大小同样是10M的文件FTP上传到服务器,第1包是一个单独的文件,而第2包是一个文件夹下杂乱的文件,数量很多。那么通过FTP上传,将发现,上传第1包文件的时间将比第二包文件的时间用的少,而且将少很多时间。因为每传送一个文件,客户段和服务段都要产生发送请求与响应的命令。简化来说,如果客户段尽发送一个“喂,我开始传啦,你接”,服务段只发送一个“ok,我收到了,你可以继续,或者断开”,而发送这两个命令都只需要1秒钟(把时间放大,实际比这个小多了),那么假设在网络连通质量不变的情况下,同样是10M大小的文件量,1个独立的文件只需要“ 1(秒)+传送10M数量的时间+1(秒)”,而总共具有10M大小N个文件则需要“ N*(1(秒)+1(秒))+传送10M数量的时间”。 所以尽量保持连接的延续,减少反复的启动进程,调用Perl解释器,是提高Perl CGI执行效率的根源。 [此贴被 Aoming(aoming) 在 02月25日05时05分 编辑过] |
B2层 发表时间: 04-02-25 04:10 |
回复: idof [idof] 论坛用户 | 登录 |
俺看的头都大了 |
B3层 发表时间: 04-02-25 12:05 |
回复: xiean [xiean] 论坛用户 | 登录 |
Professional |
B4层 发表时间: 04-02-25 16:03 |
回复: TomyChen [quest] 版主 | 登录 |
支持一下,呵呵,很专业的说。。。 |
B5层 发表时间: 04-02-25 16:58 |
回复: abctm [abctm] 版主 | 登录 |
|
B6层 发表时间: 04-02-27 11:54 |
回复: amr [amr] 论坛用户 | 登录 |
老大就是老大哈 |
B7层 发表时间: 05-07-19 19:15 |
回复: TecZm [teczm] 版主 | 登录 |
不太明白,perl编程和shell编程有啥区别 |
B8层 发表时间: 05-07-20 17:47 |
回复: Aoming [aoming] 版主 | 登录 |
.....首先感谢这好帖子被顶了起来,然后 TecZm把 Perl 和 shell 划到一起, 就象把 计算器和计算机划到一起说没区别一样 |
B9层 发表时间: 05-07-20 23:07 |
回复: authen [authen] 论坛用户 | 登录 |
哪个是计算机?哪个是计算器? |
B10层 发表时间: 05-07-21 10:26 |
回复: NetDemon [netdemon] ADMIN | 登录 |
perl编程和shell编程的区别是perl可以用来写“20CN网络安全小组论坛”而Shell编程不可以 |
B11层 发表时间: 05-07-21 18:44 |
|
20CN网络安全小组版权所有
Copyright © 2000-2010 20CN Security Group. All Rights Reserved.
论坛程序编写:NetDemon
粤ICP备05087286号