Thursday, January 24, 2008

Host|中国网络分类广告两种业态四种模式八大网站

中国网络分类广告两种业态四种模式八大网站 - 草根实验室

中国网络分类广告两种业态四种模式八大网站

草根实验室  109Cn.Com  2007-08-28  选择您喜欢的字体:[ ]
Tags:八大 网站 模式 分类广告 网络 广告 B2C 收费 分类 基本上
首先谈一下我对分类广告网站的定义。分类广告这个名词,最早当然来自报纸的分类广告版,意思是很多种类的小广告的集合――区别于分布于其他各版的独立的大幅广告,分类广告的读者目的性更加明确。那么分类广告网站,就是专门经营目标明确投放精准的广告的网站。从趋势来说,随着技术的发展,未来的市场是分类广告的天下。

  业态

  分类广告网站有两种业态:一是B2C,就是广告发布者要收费,广告浏览者不收费。这样造成了广告基本上是企业发布,网民浏览。二是C2C,就是广告发布者和浏览者都不收费,网站通过少量的广告位或者少量的收费项目获得收入。这样大多数广告就会由网民个人发布,少量由企业发布。

  B2C形式的分类广告网站商业气氛浓郁,信息可信度高,但是网民浏览量不大。C2C形式的分类广告网站信息未经验证,可信度不高。因为没有收费门槛,垃圾信息很多,但是网民互动性强,浏览量大。

  盈利模式

  目前分类广告网站有四种盈利模式:线上 平媒,纯线上广告,收费服务,移动增值服务。

  线上 平媒基本上是靠网络实现人气,平媒实现营收。

  纯线上广告就不用多说了。

  收费服务是利用人气和品牌开展一些增值服务,如51job的人才测评人事外包。 字串1

  移动增值服务,是指利用手机平台提供信息服务。

  以上所述四种盈利模式,有些网站已经开始正常运营,有些正在摸索之中,即将投入运营。

  八大网站

  下面介绍一下具有代表性的八个网站:

  一、 百度――B2C,纯线上广告 收费服务

  百度虽然是搜索引擎,但其实是中国最大的分类广告商。其通过关键字定位,将相关广告显示给搜索引擎用户。百度盈利模式是线上广告 竞价排名。百度的竞价排名影响了其用户体验,但其也是不得以而为之,面对google即将进入中国的压力,百度必须做大业绩尽快上市融资――在网络界,公司只有上市了才能说基本上存活下来了。

  二、 Google――B2C,纯线上广告

  Google已经于今年四月在上海设立办事处并获得广告经营资格,本月正式签约上海火速作为其总代理。今后的中国分类广告市场,一段时间内基本上由百度和google瓜分了,其他公司只能喝点汤了。

  三、 窄告――B2C,纯线上广告

  窄告的模式其实并不新鲜,只不过在国内比较少见而已。窄告其实就是一个广告联盟,只不过他从事的是经过关键字定位的分类广告。窄告以后要如何在与google、sohu广告联盟的竞争中做大?我没有想到什么特别好的办法。赶紧扎扎实实地占领市场吧,趁这个市场还没有被瓜分完的时候。

字串4



  四、 网易分类――B2C,纯线上广告

  网易分类是典型的B2C,基本上全是企业发布的广告。可是网易分类要不是网易或者其他门户做的,还会有人看吗?没了门户的流量怎么办?

  五、51job――B2C,线上 平媒 收费服务

  51job专注于分类广告的一个子类――招聘求职。51job基本上已经把这个细分行业做到极致了。其盈利模式齐全,连很多增值服务如人才测评和人事外包都做起来了。基本上51job和中华英才网等几家公司已经瓜分了这个市场,后来者除非颠覆整个行业规则,不然很难再超越了。

  六、客齐集――C2C,线上 平媒

  客齐集是EBAY收购了CRAIGSLIST、RENT等几家区域分类广告网站后推出的全球性分类广告网站。可能是EBAY认为分类广告市场还不够成熟,希望能够低成本试水,所以花钱不象易趣那么猛。

  七、站台――C2C,纯线上广告

  站台是个神秘的网站,域名注册地在国外,干的却是地地道道的中国的事情。站长估计是不肯多花一分钱,但是想赌一下分类网站的未来。所以完全仿照CRAIGSLIST做个连颜色排版都一样的网站,因为做的早,加上站长似乎懂一点SEO,所以访问量还不低。

 八、分类哥哥――B2C,线上 平媒

  分类哥哥是一家专门从事报纸分类广告代理的公司设立的网站,该公司自称拥有几十份报纸的分类广告代理权。如其所述,其资源非常好,大量的报纸分类广告客户极有可能转化为网络分类广告的客户。

  其实分类广告网站非常多,本文为研究仅选取了八个具有代表性的网站作了点评,并不认为这些网站是做的最好、最大的前八名。

LinuxCmd|教你学用CURL --- 命令行浏览器

教你学用CURL --- 命令行浏览器 - 荒 ⌒soGoo⌒ 堂 - DonewsBlog


工具:    CURL      WGET


CURL? 嗯,说来话长了~~~~

这东西现在已经是苹果机上内置的命令行工具之一了,可见其魅力之一斑

1)
二话不说,先从这里开始吧!

curl http://www.yahoo.com

回车之后,www.yahoo.com 的html就稀里哗啦地显示在屏幕上了~~~~~

2)
嗯,要想把读过来页面存下来,是不是要这样呢?
curl http://www.yahoo.com > page.html

当然可以,但不用这么麻烦的!
用curl的内置option就好,存下http的结果,用这个option: -o
curl -o page.html http://www.yahoo.com

这样,你就可以看到屏幕上出现一个下载页面进度指示。等进展到100%,自然就OK咯

3)
什么什么?!访问不到?肯定是你的proxy没有设定了。
使用curl的时候,用这个option可以指定http访问所使用的proxy服务器及其端口: -x
curl -x 123.45.67.89:1080 -o page.html http://www.yahoo.com


4)
访问有些网站的时候比较讨厌,他使用cookie来记录session信息。
像IE/NN这样的浏览器,当然可以轻易处理cookie信息,但我们的curl呢?.....
我们来学习这个option: -D <-- 这个是把http的response里面的cookie信息存到一个特别的文件中去
curl -x 123.45.67.89:1080 -o page.html -D cookie0001.txt http://www.yahoo.com

这样,当页面被存到page.html的同时,cookie信息也被存到了cookie0001.txt里面了


5)
那么,下一次访问的时候,如何继续使用上次留下的cookie信息呢?要知道,很多网站都是靠监视你的cookie信息,
来判断你是不是不按规矩访问他们的网站的。
这次我们使用这个option来把上次的cookie信息追加到http request里面去: -b
curl -x 123.45.67.89:1080 -o page1.html -D cookie0002.txt -b cookie0001.txt http://www.yahoo.com

这样,我们就可以几乎模拟所有的IE操作,去访问网页了!


6)
稍微等等~~~~~我好像忘记什么了~~~~~
对了!是浏览器信息~~~~

有些讨厌的网站总要我们使用某些特定的浏览器去访问他们,有时候更过分的是,还要使用某些特定的版本~~~~
NND,哪里有时间为了它去找这些怪异的浏览器呢!?

好在curl给我们提供了一个有用的option,可以让我们随意指定自己这次访问所宣称的自己的浏览器信息: -A
curl -A "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" -x 123.45.67.89:1080 -o page.html -D cookie0001.txt http://www.yahoo.com

这样,服务器端接到访问的要求,会认为你是一个运行在Windows 2000上的IE6.0,嘿嘿嘿,其实也许你用的是苹果机呢!

而"Mozilla/4.73 [en] (X11; U; Linux 2.2; 15 i686"则可以告诉对方你是一台PC上跑着的Linux,用的是Netscape 4.73,呵呵呵


7)
另外一个服务器端常用的限制方法,就是检查http访问的referer。比如你先访问首页,再访问里面所指定的下载页,这第二次访问的referer地址就是第一次访问成功后的页面地址。这样,服务器端只要发现对下载页面某次访问的referer地址不 是首页的地址,就可以断定那是个盗连了~~~~~

讨厌讨厌~~~我就是要盗连~~~~~!!
幸好curl给我们提供了设定referer的option: -e
curl -A "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)" -x 123.45.67.89:1080 -e " mail.yahoo.com" -o page.html -D cookie0001.txt http://www.yahoo.com

这样,就可以骗对方的服务器,你是从mail.yahoo.com点击某个链接过来的了,呵呵呵


8)
写着写着发现漏掉什么重要的东西了!----- 利用curl 下载文件

刚才讲过了,下载页面到一个文件里,可以使用 -o ,下载文件也是一样。
比如, curl -o 1.jpg http://cgi2.tky.3web.ne.jp/~zzh/screen1.JPG
这里教大家一个新的option: -O
大写的O,这么用: curl -O http://cgi2.tky.3web.ne.jp/~zzh/screen1.JPG
这样,就可以按照服务器上的文件名,自动存在本地了!

再来一个更好用的。
如果screen1.JPG以外还有screen2.JPG、screen3.JPG、....、screen10.JPG需要下载,难不成还要让我们写一个script来完成这些操作?
不干!
在curl里面,这么写就可以了:
curl -O http://cgi2.tky.3web.ne.jp/~zzh/screen[1-10].JPG

呵呵呵,厉害吧?!~~~

9)
再来,我们继续讲解下载!
curl -O http://cgi2.tky.3web.ne.jp/~{zzh,nick}/[001-201].JPG

这样产生的下载,就是
~zzh/001.JPG
~zzh/002.JPG
...
~zzh/201.JPG
~nick/001.JPG
~nick/002.JPG
...
~nick/201.JPG

够方便的了吧?哈哈哈

咦?高兴得太早了。
由于zzh/nick下的文件名都是001,002...,201,下载下来的文件重名,后面的把前面的文件都给覆盖掉了~~~

没关系,我们还有更狠的!
curl -o #2_#1.jpg http://cgi2.tky.3web.ne.jp/~{zzh,nick}/[001-201].JPG

--这是.....自定义文件名的下载?
--对头,呵呵!

#1是变量,指的是{zzh,nick}这部分,第一次取值zzh,第二次取值nick
#2代表的变量,则是第二段可变部分---[001-201],取值从001逐一加到201
这样,自定义出来下载下来的文件名,就变成了这样:
原来: ~zzh/001.JPG ---> 下载后: 001-zzh.JPG
原来: ~nick/001.JPG ---> 下载后: 001-nick.JPG

这样一来,就不怕文件重名啦,呵呵


9)
继续讲下载
我们平时在windows平台上,flashget这样的工具可以帮我们分块并行下载,还可以断线续传。
curl在这些方面也不输给谁,嘿嘿

比如我们下载screen1.JPG中,突然掉线了,我们就可以这样开始续传
curl -c -O http://cgi2.tky.3wb.ne.jp/~zzh/screen1.JPG

当然,你不要拿个flashget下载了一半的文件来糊弄我~~~~别的下载软件的半截文件可不一定能用哦~~~

分块下载,我们使用这个option就可以了: -r
举例说明
比如我们有一个 http://cgi2.tky.3web.ne.jp/~zzh/zhao1.mp3 要下载(赵老师的电话朗诵 :D )
我们就可以用这样的命令:
curl -r 0-10240 -o "zhao.part1" http:/cgi2.tky.3web.ne.jp/~zzh/zhao1.mp3 &\
curl -r 10241-20480 -o "zhao.part1" http:/cgi2.tky.3web.ne.jp/~zzh/zhao1.mp3 &\
curl -r 20481-40960 -o "zhao.part1" http:/cgi2.tky.3web.ne.jp/~zzh/zhao1.mp3 &\
curl -r 40961- -o "zhao.part1" http:/cgi2.tky.3web.ne.jp/~zzh/zhao1.mp3

这样就可以分块下载啦。
不过你需要自己把这些破碎的文件合并起来
如果你用UNIX或苹果,用 cat zhao.part* > zhao.mp3就可以
如果用的是Windows,用copy /b 来解决吧,呵呵

上面讲的都是http协议的下载,其实ftp也一样可以用。
用法嘛,
curl -u name:passwd ftp://ip:port/path/file
或者大家熟悉的
curl ftp://name:passwd@ip:port/path/file



10)
说完了下载,接下来自然该讲上传咯
上传的option是 -T

比如我们向ftp传一个文件: curl -T localfile -u name:passwd ftp://upload_site:port/path/

当然,向http服务器上传文件也可以
比如 curl -T localfile http://cgi2.tky.3web.ne.jp/~zzh/abc.cgi
注意,这时候,使用的协议是HTTP的PUT method

刚才说到PUT,嘿嘿,自然让老服想起来了其他几种methos还没讲呢!
GET和POST都不能忘哦。

http提交一个表单,比较常用的是POST模式和GET模式

GET模式什么option都不用,只需要把变量写在url里面就可以了
比如:
curl http://www.yahoo.com/login.cgi?user=nickwolfe&password=12345

而POST模式的option则是 -d

比如,curl -d "user=nickwolfe&password=12345" http://www.yahoo.com/login.cgi
就相当于向这个站点发出一次登陆申请~~~~~

到底该用GET模式还是POST模式,要看对面服务器的程序设定。

一点需要注意的是,POST模式下的文件上的文件上传,比如
<form method="POST" enctype="multipar/form-data" action="http://cgi2.tky.3web.ne.jp/~zzh/up_file.cgi ">
<input type=file name=upload>
<input type=submit name=nick value="go">
</form>
这样一个HTTP表单,我们要用curl进行模拟,就该是这样的语法:
curl -F upload=@localfile -F nick=go http://cgi2.tky.3web.ne.jp/~zzh/up_file.cgi

罗罗嗦嗦讲了这么多,其实curl还有很多很多技巧和用法
比如 https的时候使用本地证书,就可以这样
curl -E localcert.pem https://remote_server

再比如,你还可以用curl通过dict协议去查字典~~~~~
curl dict://dict.org/d:computer

今天就先讲到这里吧,呵呵。疯狂的curl功能,需要你---一起来发掘。

copyright by nickwolfe@CCF
2004.08.24 21:24

Wednesday, January 23, 2008

Performance|关于rails大容量网站部署的性能讨论

关于rails大容量网站部署的性能讨论|新空互联

关于rails大容量网站部署的性能讨论

文章来源:  文章作者:  发布时间:2007-10-15  字体:[缩小字体  放大字体]

  • 这两天在安装服务器,顺便到处看了一下,搞清楚了一些对rails的误解。因此对服务器部署有了一些新的想法,和大家探讨一下。

    字串5

    以前我以为rails像PHP那样,以apache的server mod方式运行,今天仔细看了一下FastCGI/SCGI/mongrel的安装手册,这才搞明白,我弄错了。

    字串3

    当FastCGI/SCGI/mongrel方式部署的时候,ruby并不是直接运行在apache的进程中的,而是独立的运行在CGI或者 server上面的。在这种情况下lighthttpd/apache仅仅充当了一个前端HTTP请求分发的代理作用(和静态页面的处理),动态内容交给了后台的ruby CGI/Server去处理的。 字串4

    其实这种部署方式和我们J2EE常用的apache+(mod_jk)+n个tomcat实例的方式本质上是没有什么区别的。更进一步来说,IBM WebSphere的cluster实际上也是这种方式,在前端使用Apache作为请求分发代理,后面若干个WebSphere实例来处理请求。

    字串2

    在J2EE群集部署方案中,虽然目前的企业应用都强调了应用服务器提供的Session复制功能,EJB调用的负载均衡能力,但是考虑到目前 J2EE潮流发展的趋势,已经不再使用EJB的负载均衡,同时Session复制也被证明为影响cluster水平扩展的主要障碍之一。因此对于大容量的 J2EE水平扩展群集而言,保持每个节点的无状态性,不再使用Session来保持全局状态是必经之路。 字串1

    因为只有每个JVM进程不保持全局状态,才能够保证n个JVM节点的幂等性,那些所有涉及到全局状态的,必须放在JVM进程之外,例如用户ID可以使用cookie,session可以放入数据库,文件可以放在共享存储系统中。 字串3

    这样的方案做下来,前端的apache也仅仅是一个HTTP请求代理,后面的应用服务器实例几乎是可以无限水平扩展的,瓶颈永远不会出现在应用服务器层,只会出现在apache端,或者数据库端。当然apache不行,还可以用lighthttpd,甚至使用四层交换机硬件进行请求的分发工作,后端的单台数据库不行,还可以使用多个数据库同步复制,甚至使用Oracle Grid。

    字串4

    这个时候我们考察一下J2EE的群集方案,竟然会发现和Rails的群集方案没有多大的差别了。为了讨论的简单,我们拿J2EE的apache2.2 + tomcat5.5和apache2.2 + mongrel来对比一下:

    字串1

    先看J2EE群集方案:

    字串4

    Apache2.2 proxy分发请求给后面n个tomat5.5实例(这里甚至都不需要使用mod_proxy_ajp,apache直接转发请求给tomcat的http端口);
    每个tomcat实例没有内部全局状态,完全是无状态的服务,用户标示从cookie取得,session可以放在数据库中,假设不使用分布式Cache。每个tomcat实例收到一个请求以后,自行处理,然后返回给apache。 字串1

    再看rails群集方案: 字串5

    Apache2.2 proxy分发请求给后面n个mongrel实例;
    每个mongrel实例也是无状态的,用户标示使用cookie,从apache收到一个请求以后,自行处理,然后返回给apache。 字串2

    从两者的对比来看,J2EE的每个节点是一个JVM进程,里面若干Java线程都是无状态的,而mongrel据说也是多线程的,rails的每个节点是一个Ruby进程,里面若干ruby线程都是无状态。竟然完全一致的模型! 字串8

    在这种情况下,究竟是进程多一点,线程少一点;还是进程少一点,线程多一点,都不是最重要的讨论话题,焦点是J2EE和Rails的群集方案是一致的,在应用逻辑处理层的群集水平扩展能力都是近乎无限的,因此都不是系统的瓶颈所在。这意味着什么呢? 字串3

    这意味着,只要硬件管够,J2EE系统和Rails系统的网站负载能力不会有多大的差别。 字串9

    也许,ruby的解析执行速度比Java慢,由于没有数据库连接池导致每个请求需要多消耗一些建立物理连接的时间。但是这些因素只能导致单个用户请求的响应时间比Java慢,不会导致整个网站的负载能力差。

    字串5

    也就是说,如果Java的系统,用户打开一个页面需要两秒钟,而Rails的系统,用户打开一个页面可能需要四秒钟,但是他们都能够每天负载100 万的PV,这100万个请求,rails大概也同样是四秒钟,这方面没有什么差别(同样的网站负载,mongrel的实例数量也许远多于tomcat实例数量,但是总体处理能力没有区别)。 字串9

    当然,J2EE群集方案和Rails群集方案还是有两点不同:

    字串2

    1、J2EE有内置分布式Cache,例如JBoss Cache,而rails的方式是独立的Cache进程在运行,这两种方式来说,对于群集水平扩展,甚至rails的Cache方案还要更好。

    字串7

    2、数据库连接池,对于mongrel,我觉得增加数据库连接池是完全做得到的事情。不过即使不增加连接池,这里也不会成为系统瓶颈,最多会导致数据库多花一些时间在处理连接的建立和断开上面。 字串9

    最后这种群集部署方案下面,web server退化成为一个请求分发代理,找不到任何理由使用apache了,对,是lighthttpd出场kick off apache的时候。如果网站容量大到连lighthttpd都无法及时分发请求的时候,你还可以用四层交换机来分发请求,那lighthttpd也该被 kick off了。 字串3

    用J2EE看起来确实能够更加节省硬件,但是我们不能再用Rails无法负载大容量网站的理由来攻击rails了,只有硬件管够,不管是J2EE,还是Rails,都有近乎无限的水平扩展能力。

Web|轻量级Web服务器

轻量级Web服务器 浏览文章 技术文栏 建站佳源

轻量级Web服务器

【来源: 建站资讯整理 | 更新日期: 2007-9-7 13:23:17 | 浏览(316)人次 | 评论(0)条 | 投稿
点击详细了解:阿里妈妈,帮你实现网络赚钱梦,流量变成现金!
"轻量级" Web 服务器,例如 lighthttpd、 litespeed 和 mongrel,可以为项目带来很多的好处。本文调查这种可能性,并展示这些 Web 服务器的适用性。
一个 Web 服务器需要哪些东西?

< 第一个重要的方面是清楚地理解所调查的领域(请参阅 参考资料,以了解更详细的信息)。终端用户在 Internet 上的基本动作就是 "进入一个 Web 页面"。从大处讲,这牵涉到两个应用程序之间的协作:
一个 Web 浏览器,例如 Firefox 或 Internet Explorer,用于请求一个特定的页面,并且以人类可读的方式显示从另一个应用程序那里收到的内容。
一个 Web 服务器,通常是在远程机器上,负责对页面请求作出响应,返回 HTML 编码的或类似的数据流。
所有 Web 用户直接与浏览器交互,因此他们的选择和分析相应地有些狂热。而服务器只对站点的技术人员可见。根据 Netcraft 最近的调查,虽然存在很多不同的 Web 服务器,但是其中两种 Web 服务器就占据了 90% 的份额,这两种 Web 服务器是 Apache 和 Internet Information Server (IIS)。它们都是经过高度锤炼的产品,并且声称不仅具有广泛的内在技术特性,而且有很多配套的书籍、增件、顾问、提供商等。那么,它们是否还有值得改造的地方呢?

答案是肯定的。评价一个 Web 服务器的重要指标有:

性能:对请求作出响应的速度有多快?
可伸缩性:当很多用户同时访问它时,服务器还能继续可靠地运行吗?
安全性:服务器是否只执行它应该执行的操作。它在认证用户和加密传输方面提供了怎样的支持?它的使用是否使附近的应用程序或主机变得更易受攻击?
可靠性:服务器的失效模式和故障发生率如何?
标准遵从性:服务器遵从相关的 RFC 吗?
灵活性:是否可以对服务器进行调优,以支持较重的请求负载、需要计算的动态页面或者代价不菲的认证等等?
平台需求:该服务器可用于哪些平台?它是否有特定的硬件需求?
易管理性:服务器是否易于设置和维护?它是否与日志记录、审计、成本计算等组织标准兼容?
Apache 和 IIS 不能同时在那么多的标准方面做到最好。理论上讲,显然那些定向的产品至少能在以上的一至两个方面超越市场领头产品。

关于轻量级 Web 服务器的一件有趣的、值得调查的事情是,它们之间的竞争远远不止是理论上的:仔细研究表明,它们有很多 东西可以提供,并且即使在很多常见的情况下,它们相对于 Apache 和 IIS 也坚持了自己的风格。虽然可以合理地认为市场领头产品已经经过了小心的优化,从而能够有效地在性能(举个例子)方面避免被击败,但是很多小型的竞争对手因为只提供简单的静态 Web 页面服务,速度反而更快。当使用这些 Web 服务器运行测试时,您会感觉好像是在赛道上驾驶一辆 go-kart 小车,不知不觉竟然超过了 Porsche 和 Viper 车。这还不是全部:有时候,轻量级 Web 服务器可作为那些大哥级服务器的有效补充,而不只是与它们竞争。即使您知道自己将使用 Apache,有时候通过将它与一个轻量级伙伴搭档,反而可以最大限度地利用它。最好的解决方案有时候需要两个或更多 Web 服务器的协作。

Web 服务的轻巧性
本调查中重点关注的 "轻巧性" 实际上是一种主观质量,就像 "艺术" 或 "风格"。它通常意味着简单、易于安装、流线化、要求低和健壮 ―― 比 Apache 和 IIS 更小、更简单,当然,在试图满足大量市场的过程中,它们已经变得异常复杂。出于这个目的,虽然 Java Web Server、AOLserver 和 Zeus 拥有迷人的可移植性和性能优势,但是它们的复杂性和大小使其不得不被拒之门外。

轻量级 Web 服务器可以适用于市场领头产品和其他 "重量级" 服务器无法胜任的情况。例如,整个服务器可以打包在一个文件中。这意味着开发人员可以方便地携带生产环境所需的所有工具。即使在生产服务器上运行的是 Apache,也仍然可以在宾馆的房间里,借助只需数秒钟就可以安装完毕的轻量级 Web 服务器以尝试新想法。而且,由于轻量级 Web 服务器要求很低,因此可以在那些无法负担 IIS 的主机上顺畅地运行。
单文件打包

单文件打包
Apache 需要小心地安装散布在多个目录中的很多文件。与之截然不同的是,下面的 Web 服务器却打包在一个可执行文件中。我的一个雇主 Phaseit 的专长是部署和打包,我们能使 Apache 的安装看上去比平常更简单一些。但是即使我们做得最好,Apache 或 IIS 与轻量级 Web 服务器在 "空间占用" 方面也仍然有很大的差异:前者要占用大量的空间。  

小的、轻量级的 Web 服务器还可以在小功率的主机上良好地运行。在我们的公司(Phaseit ―― 见右侧注释)中,我们在远程的、条件欠佳或配置较低的环境中的工业计算机上运行专用的 硬件。在这些情况下,能够通过一个对处理能力或磁盘空间要求很低的应用程序来提供 Web 页面是一个很大的优势。这意味着我们的机器可以避免 Apache 的开发和处理能力所带来的开销,构建基于 Web 的管理控制台。

从某种程度上讲,几乎所有轻量级 Web 服务器都是开放源码的。如果我们需要某一款 Web 服务器所特有的行为,那么下面概述的一些 Web 服务器都非常小巧,易于理解,也易于增强,只有两个例外。这些 Web 服务器为嵌入 Web 服务的项目提供极好的原始材料,不管这些 Web 服务是在特殊的硬件中,还是在为在通用计算机上运行而设计的特定应用程序中。它们还广泛用于具有传统外观的 Web 站点:

YouTube 依靠 lighttpd 快速交付归档的内容,例如视频;
cdServe 运行 "German Woodworking Machinery and Tools" CD;
LiteSpeed 宣扬它在 twitter、www.funnyoride.comwww.airliners.com、WordPress.com、 fanfiction.com、SlashGear、 www.forumactif.com 和其他著名 Web 站点上担任的角色;
OpenSUSE、RubyOnRails、MarkaBoo 和其他一些著名站点依赖于 Mongrel;
demon.netbluelight.commtv.com、The Drudge Report、 garfield.com 等站点则使用 thttpd;
等等。
下面的例子说明了开发人员使用轻量级服务器的轻巧性:在我们公司,我们采用专门的硬件提供办公室电话解决方案。它基于定制的、以传统的 Linux® 应用程序的形式运行的软件。只需一个附加文件和一点
init.d
配置,很容易添加一个强大的 "Web 控制台",该 Web 控制台能提供硬件和软件的管理界面。 终端用户可以从任何浏览器中监视和配置他们的计算机,而不必安排专门的硬件连接或解决使用 "垂直" 硬件时常见的其他复杂性。

面向服务架构(SOA)被认为难以使用。在我们的经验中,SOA 至少有一部分这方面的缺点阻碍了 Web 服务的使用。我们利用轻量级 Web 服务来设置快速的 SOA,以进行演示。

轻量级服务器甚至可以用于生产数据中心,包括前面列出的 high-profile 站点。性能非常高的站点会将操作分开,从而最大限度地利用缓存、代理等技术。例如,一个基于 Apache 的站点可能采用一种这样的架构:通过小型的 Web 服务器从专用的文件系统提供缓慢变化的图片。终端用户看到的结果实际上是 Apache 和一个或多个辅助 Web 服务器通过协作得到的输出,它们各自担任自己擅长的角色。这样的安排可以以非常低的计算成本提供非常 快的结果。

手段和目的

虽然轻量级 Web 服务器有很多共同之处,但是各有各的不同。大多数轻量级 Web 服务器是用 C 编写的,但是实践证明,有些其他实现语言也可以成功地用于实现服务器,对此我已经做了实验,这些语言包括 Erlang、Java、Lisp、Lua、Perl、Python 和 Tcl。如果其中有您喜欢的语言,那么也许可以找到适合您的 Web 服务器。

由于很多特定的原因,您可能会将目光投向某种 "不常见" 的语言:

教学:使用轻量级 Web 服务器来制定一个重要、但是并不太大的目标。这是获得使用某种语言的经验的好方法。
虽然用 C 编写的轻量级 Web 服务器大小为 10-50 KB,更高级的语言有 100 KB 到数 MB 的运行时,但整个 Web 服务器的源文件可能只占几千个字节。这种 Web 服务器占用的空间很小,因此比 Apache 更易于与技术伙伴共享。
更高级的语言可以使实验更吸引人 ―― 例如,添加一个新的 HTTP/1.1 特性可能只需几行源代码。这些轻量级服务器是非常方便的实验材料。
将 HTTP 服务器添加到已有的、用高级语言编写的应用程序中只需增加几行源代码。
Athana 可以作为这些主题的例子。它是用 Python 编写的 Web 服务器。它支持 HTTP 多部分(上传)、会话、 cookie 等。从 0.2.1 版开始,Athana 一直被编写在一个单独的、精心组织的源文件中。

如前所述,不同的轻量级 Web 服务器有着不同的优点,它们或多或少独立于编程语言。所有轻量级 Web 服务器都比 Apache 更小、更易于配置。与 Apache 相比,有些轻量级 Web 服务器更快,有些则快得多。有些则强调安全性、重负载下的从容性、可扩展性或者内存占有量。在任何情况下,都可以以一种不适用于 Apache 的方式彻底地理解这些服务器。

哪些特定的产品使这些可能性成为现实?即使只留意 "轻量级" 服务器,面对的也是一个很大的难于管理的产品集合。不过可以将它们按子类来划分:超轻型、关注安全型、支持特定语言型等等。

我特别喜欢其中的超轻型 Web 服务器,它们比 Apache 小得多。如此小的应用程序可以直接记住,系统地、严密地加以考虑,以证明它们的安全性或可伸缩性。小型 Web 服务器包括:

Cheetah Server,用不到一千行的 C 代码编写而成。
DustMote,一个非常 小的 Web 服务器,用一个大约 3000 字节的 Tcl 源文件实现。
fnord,大小取决于平台和配置,不超过 20K。虽然很小,但是它支持虚拟主机、CGI 和 keep-alive。
ihttpd,使用不到 800 行的 C 代码,包括 CGI,并通过
inetd
提供页面。
im-httpd,非常小的服务器 ―― 只有大约 7 KB,链接到
glibc
。而且它也非常快。
mattows,支持 CGI,只有 600 行 C 代码。
Scrinchy,虽然很小,不到 30KB,但是支持多种脚本编制语言,包括一种特殊用途的、基于栈的 Sy 脚本语言。
ZWS 演示了一个即使是使用 500 多行带足够注释的 zsh (!) 编写的应用程序 ―― 在这里是一个 HTTP 0.9+ 服务器 ―― 也可以有多强大。
体积小并不妨碍这些服务器被正式使用。例如,fnord 可以处理数千个同时进行的连接。也许轻量级作为一个类别最令人印象深刻的成就是高性能服务器:

cghttpd 是一个小型 Web 服务器,它被理解为使用 2.6 系列内核中可用的异步功能的一个试验品。
darkhttpd 是一个快速的、单线程的 HTTP/1.1 服务器。
Gatling 是为高性能设计的。它的特性包括 FTP、IPv6、虚拟主机、CGI 等。
Kernux 是一个 Linux 内核模块,它实现了一个 HTTP 守护进程。
lighttpd 是使用率排名第五的 Web 服务器(排名还在上升)。它为很多同时进行的连接进行了优化:"典型的场景是使用 lighttpd 作为一个下载(off-load)服务器,以提供静态内容……"
LiteSpeed Web Server 是一款轻量级商业 Web 服务器,强调性能和安全性。LiteSpeed Technologies 公司宣传为静态内容提速了 6 倍,在解释页面方面也有一定的提高。
Miniature JWS,也称 tjws,它是基于 Java 的 Web 服务器,可以处理servlet、JSP 和数千个并发连接,而大小只有 77 KB。它的作者声称它 "比 Apache 2.x 快 10%"。
Yaws 是用 Erlang 编写的一款高性能 HTTP/1.1 服务器。

有些 Web 服务器被实现为类或库,以便嵌入到较大的应用程序中。 在这些 Web 服务器当中,我发现特别有趣的有:

EHS ―― "嵌入式 HTTP 服务器",被设计为一个 C++ 类,用于嵌入到较大的 C++ 应用程序;还有
Embedded TCL Web Server,它是一个很普通的 Web 服务器,支持 SSL 和 Basic Authentication,速度非常快 ―― 其作者使它至少与 lighthttpd 和 AOLserver 一样快。它是用不到 100 行 Tcl 编写的。

Python 是几种适合不寻常环境的 Web 服务器的实现语言,这些 Web 服务器包括:

cdServer 是一个小型的、用 Python 编写的 HTTP 服务器,它"被设计用来提供来自 CD-ROM 的(静态)内容" 。它在提供动态内容方面能力有限。我们有几个涉及不受影响的"live CDs" 的项目,在这些项目中像 cdServer 之类的工具很关键。
edna,一款智能的用 Python 编写的 MP3 服务器,它是用 HTTP 实现的。

还有其他一些用 Perl 和其他不出名的语言编写的轻量级 Web 服务器:

Camlserv,用 ocaml 编写的一个完整的 Web 服务器,目标是 "高度交互式的 Web 页面"。它由几千行 ocaml 编写而成,其中大部分代码都与 MySQL 和 HTML 的特殊处理有关。
dhttpd 用和 Apache 相同的格式记录访问。它支持 CGI,并具有内建的 Perl 解释器、虚拟主机、IPv6、带宽管理和安全性等方面的特性。
DNHTTPD 是用 Perl 编写的,用于 UNIX®。它支持虚拟主机、SSL 连接、CGI 等。
Jellybean 是用 Perl 编写的基于 HTTP 的 Perl Object Server。
lns.http 是一个 Common LISP HTTP/1.1 Web 框架。
Mongrel 是用 Ruby 编写的、用于 HTTP 的一个库和服务器。
Nanoweb 是用 PHP 编写的一款快速、健壮的 Web 服务器。它宣称具有丰富的特性,包括完全遵从 HTTP/1.1、访问控制、身份验证、虚拟主机、SSL 兼容性等。
Naridesh 是用 Perl 编写的 Web 服务器。
OpenAngel 是用 Perl 编写的。它强调的重点是安全性。
Xavante 是用 Lua 编写的 HTTP/1.1 Web 服务器。
XSP 是用 C# 编写的,用于运行 ASP.NET

有时候您可能需要其他一些用 C 编写的、具有不常见的次要优势的轻量级 Web 服务器:

ABYSS 可以在 UNIX 和 Win32 之间移植,其 "目的是成为完全遵从 HTTP/1.1 的 Web 服务器"。它占用的内存很少。
Anti-Web HTTPD(也称 "Anti-Web"、"awhttpd" 和 "AW")是一款单进程、无线程、支持 CGI 的服务器,它强调安全性和简单性。
MHTTPD 支持从外部文件或 LDAP 服务器进行的 MHTTPD Basic Authentication。
mini-httpd 可以在一个系统线程中处理多个并发请求,但是在主机上占用的内存或 CPU 很少。
Naken Web 类似于很多其他的轻量级服务器 ―― 它支持 Basic Authentication、静态内容等 ―― 但是它的作者将它设计为用于 Webcam 操作,并且在 Gumstix、WRT54GL、OpenWrt 和其他新的平台上运行。
Null httpd 是一款多线程的、简单的、可移植的 Web 服务器。
Seminole 是一款商业 Web 服务器,内存需求较小,功能较多。
thttpd throttle,支持
chroot、
Basic Authentication 等。
结束语

Web 服务器远远不止是 Apache 和 IIS 的天下。您可以发现很多其他的 Web 服务器,它们很小,易于理解,但是又足够快,可以被正式使用。这样的 Web 服务器可以很好地加快您的下一个项目。

关于作者


  Cameron Laird 长期为 developerWorks 撰稿,并且曾担任专栏作家。他经常撰写关于能加快其雇主的应用程序开发的开放源代码项目的文章,着重关注可靠性和安全性

Health|如何战胜不良体态?

如何战胜不良体态?_39健康健身

 种种的不良体态,不仅摧毁了人的自信,而且给身体健康带来威胁。目前,临床上对这些不良体态没有特效的治疗方法,运动则是有效可行的矫正方法。

  现在,就让我们对各种不良体态展开一场"运动战",用运动和毅力赢回属于自己的那份 挺拔和美丽

  本栏目将陆续刊登各种不良体态的矫形文章,敬请读者关注

  含胸驼背矫形篇

  人的脊椎有正常的生理弯曲度,成年人在颈部、腰部各有3~5厘米的凹陷,胸椎呈正常的均匀弧形。含胸驼背的体态主要是由于背部肌肉过度松弛引起的。长期伏案工作者、学生以及经常上网者,由于背部经常处于过度伸展状态,致使背部肌肉松弛、薄弱容易使胸椎椎间盘突出,长此以往就会引起脊柱变形,形成驼背。另外,有些人在站立、行走中不注意保持正确的身体姿势,觉得含胸最放松也更舒服,平时习惯于弓着腰做事情,时间长了也会引起胸椎异常乃至脊柱变形。青少年驼背既影响到体形健美,又会压迫与脑、脊髓相关的脑神经、脊神经,内脏神经造成神经障碍,导致青少年记忆力下降、反应迟钝、智商偏低。可以说,驼背同 近视一样,是危害青少年健康成长的一大公害。要矫正不良的体姿,除了有意识地训练自己正确的坐、立、行姿势外,还要进行有针对性的健身塑形练习。

  临床上没有治疗驼背的药物只有通过运动疗法才能达到矫正的目的。而运动疗法是通过锻炼脊背,加强薄弱部位的肌肉力量使脊背挺直来实现的。下面介绍一些主要锻炼背部肌肉,帮助脊背挺直的徒手和器械训练方法。矫正驼背首先要放松肩背部肌肉,缓解局部肌肉僵硬。矫正的原则主要是增强薄弱部位――背肌的力量,改善背肌松弛伸展的状态,延伸躯干的长度,扩展胸廓。从今天开始,注意坐、立、行的身体姿态,再配合下面的矫正练习,相信你很快就塑造出一个挺拔的身姿

   女孩篇・徒手矫形操

  专为女孩设计的这套徒手矫形操无需任何器材,只利用自己的体重练习,简单方便,可操作性强,坚持一段时间,效果绝对不会令你失望

  1、靠墙站立(矫正驼背最简单的方法

  后脑勺、背、臀部、两肩、脚跟贴墙站立。

  背部收紧、胸部前挺,两肩在一直线上并稍向后展,两臂自然下垂轻贴身体两侧,脖颈挺直紧贴衣领,下巴微收,头向上顶,背部挺直。练习时两脚跟、小腿肚、臀部、两肩、后背及后脑勺必须紧贴墙壁。每日可贴墙站1~2次,每次不少于30分钟。多体会正确站姿的要领,久之即可改善驼背姿态。

  2、含展胸

  上体直立的站姿或坐姿。两肩后展,胸椎前挺、背肌收紧,胸大肌充分伸展。稍停,背肌放松、胸肌略内收。重复练习10-20次×3组(即做3组,每组10~20次,下同)。

  3、夹臂挺胸

  上体挺直的跪坐或站、坐姿。双手体后相握,两臂伸直夹紧、挺胸,两肩后移。稍停、松肘,上体放松。重复练习10~20次×3组。

  4、俯卧背弓

  俯卧在垫上,屈膝,小腿贴大腿,两手分别握住同侧的踝关节。吸气,头胸部和腿部同时向上抬起,两臂伸直,背部收紧,使身体呈弓形,稍停3~4秒钟。呼气,还原放松。重复10~20次,共练习3~5组。

  5、挺胸夹臂上抬 上体立直的跪坐,站立或坐姿。双手体后相握,两臂伸直夹紧,挺胸两肩后移。上体前屈两臂上抬,背部收紧。稍停,松肘上体放松。重复练习10~20次×3组。

  6、跪坐胸腹前挺

  两腿分开跪立垫上。直臂、双手分握脚跟头部后仰或保持不动,以手为支撑点,保持自然呼吸的同时,向前上方做挺胸展腹,立腰顶髋的动作。保持5~30秒钟,重复练习10~20次×3组。

  7、俯卧两头起

  俯卧在垫上,两手放于头后。吸气,头、胸部和腿部同时向上抬起,使身体呈弓形,稍停3~4秒钟。呼气、还原放松。重复10~20次×3组。

  8、站姿胸扩展

  两臂垂于体侧,两腿自然并拢站立。吸气,两臂上抬,肩部向后扩展。保持三四秒钟,呼气还原回预备姿势。重复10~20次×3组。

  9、俯卧夹臂挺起

  两腿并拢,俯卧于垫上,两手背后相握。吸气,抬头挺胸,肩背部用力收紧,使胸部从垫子抬离,至离地约20~40厘米,两臂上抬至手、肩、臂呈一直线,肩胛骨下沉,胸腹部充分伸展,双腿并拢伸直夹紧。呼气,慢慢还原回预备姿势。动作过程中保持颈椎和脊椎的向前伸展。

  10、跪坐后展胸

  跪地(双脚并拢,脚背伸直贴垫面),两手置大腿上,脊背挺直,目光平视。挺胸,背部用力收紧。

  吸气,两臂摆至体后侧,稍停2~3秒钟。呼气,缓慢还原。重复15-30次×4组。

  11、坐姿展胸前屈体

  两小腿交叉,坐于垫上,挺胸收腹紧腰屈臂两手扶膝,脊背挺直,目光平视。

  吸气,挺胸抬头,背部收紧,上体前屈至自己的最大限度。稍停2-3秒钟呼气,缓慢还原回预备姿势。重复15~20次×3组。

  12、坐姿直臂扩胸

  两小腿交叉,坐于垫上,挺胸收腹紧腰两臂伸直前平举,掌心向下。

  吸气,两手平稳而均匀地向两侧打开,两臂与两肩成一直线,稍停2-3秒钟。呼气,缓慢还原。重复10~20次×3组。

  13、俯卧单臂上举

  两臂前伸,俯卧干垫上。吸气,左臂不动,紧腰,肩、背肌肉用力抬头挺胸,右臂上抬至最高点。稍停、呼气,慢慢还原。换左臂上抬至最高点。

  男孩篇・器械矫形操

  正如前面所述,矫正驼背的方法是加强背部肌肉的力量。男孩子对于冷冰冰的器械抱有天生的热情。这里特为他们设计了器械矫正驼背的练习方案。当然,不喜欢器械练习或没条件的人,做前面的女孩徒手练习也完全没问题。

  这套器械矫正训练计划,可有目的地发展斜方肌,背阔肌及菱形肌等背部的肌群。因为这些肌肉位于脊柱上所有颈椎和胸椎的后面,当这些肌肉发达有力了,就能使增大了的胸后凸减少。久而久之,背部肌肉力量也就能把多余的增大部分变成正常范围,背也就不驼了。另外,平时要保持挺胸,收腹、立腰、颈部挺直的姿势也非常重要

  背部主要肌肉群由上背肌群:斜方肌、大圆肌、小圆肌、冈下肌和菱形肌,中背肌群:背阔肌;下背肌群:竖脊肌等组成。器械矫正含胸驼背以中上背部肌肉练习为主。

  一、发展上背肌群的练习

  1、坐姿俯身

  挺胸立腰,腰背挺直。肩负较轻杠铃,坐在训练凳上。

  吸气,上体慢慢向前倾,至腹部触及大腿,呼气,慢慢用力抬起上体,还原回预备姿势。

  2、俯身耸肩

  上体前俯约与地面平行,两肘伸直提杠铃,肘部保持伸直状态。

  吸气,肩部用力向上耸至最高点稍停后,呼气,慢慢还原,重复练习。选择较轻重量开始练习,从每组8次做起,到能完成12次时,增加2。5公斤,再从8次做起,到能完成12次时,再增加2 5公斤。

  提示:练习时背肌用力,不能借用外力。

  3、俯身杠铃(哑铃)前平举

  两脚开立,上体向前俯身约与地面平行双臂体前向下伸直两手正握杠铃,使杠铃垂于小腿处。两臂保持伸直姿势,将杠铃缓慢向前上方抬起至最高点,慢慢还原,重复练习。

  提示:练习时,肘关节可稍弯曲,不要低头。

  4,坐姿颈后下拉

  两手握住拉力器把手,坐在凳上,两腿固定,臀部不离凳面(防止利用体重降低难度)。

  吸气,上背部收缩用力,屈肘,用力下拉拉力器,肘关节尽量靠近身体两侧,使拉杆垂直拉下至颈后肩上,把手约触及第七颈椎处。稍停。呼气,上背部用力控制身体,慢慢地伸直两臂还原。两臂伸直时两肩胛向上放松上背部充分向上伸展。

  提示:上体应保持挺直,使把手处于第七颈椎垂直上方,下拉应慢用力(勿爆发式用力),还原时应控制速度。

  二、主要发展背阔肌的练习。背阔肌是人体的第二大肌肉,仅次股四头肌

  1、坐姿划船

  坐在划船器上,上身前俯握住划船器(或拉力器)把手。

  吸气、屏住呼吸,两臂向后方拉动,同时上体微后仰(不超过10度),挺胸,两侧肩胛骨向中间挤压从而达到顶峰收缩。当手触及胸腹部后,呼气还原,重复练习。

  提示:为使后拉距离加长,加大对背阔肌上中部的刺激,应低头,弓腰屈体向前伸臂,后拉时应少做伸膝动作,用背阔肌发力完成动作。

  2、俯身划船

  站在器械旁,两手宽握支架手柄。

  吸气,抬头挺胸、屈膝、腰收紧,背阔肌,大圆肌和三角肌后束发力两臂将手柄向上拉起。呼气,慢慢还原。该动作采用窄握练习,可发展上背部肌肉块与线条。

  3、器械划船架划船

  两腿分开坐在练习架的凳子上,腹部紧贴软垫,两手抓握支架手柄。

  吸气,抬头挺胸,腰收紧,背阔肌,大圆肌和三角肌后束发力,两臂将手柄向后拉起。呼气,慢慢还原。

  4、高位划船机划船

  面向高位划船机坐在器械凳上,两膝置软垫下固定,两臂上举,双手握牢把手。

  吸气,两臂下拉至肩部,稍停,呼气,慢慢还原。

  三、发达下背部练习

  1、山羊挺身

  大腿紧贴山羊架支撑垫两脚踩在踏板上,小腿抵住护垫两手五指交叉抱头,上体从架端探出,向下屈身。

  竖脊肌用力,挺胸,上体抬起高于凳面,然后还原。

  2、颈后杠铃体前屈

  两脚开立与肩宽或稍比肩宽,两手握杠铃杆置颈后肩上,挺胸、收腹、紧腰,上体直立。

  吸气,上体慢慢前屈,至背部与地面平行,臀部向后移,身体重心在脚跟后上方。稍停,呼气,腰背肌群用力,挺身起立,还原。

  提示:在练习过程中,腰背部始终挺直,紧腰、含胸。动作尽量缓慢有控制,切勿快速屈体,防止腰肌及脊椎受伤。

  该矫形训练计划第一个月应保持每周3次(隔天1次),每次选择6~8个动作,重量选择能完成8~12次的即可,采用循环训练法,即依次按计划练习,直至全部完成。第二个月完成两个循环的练习,第三个月完成三个循环

  提示:

  ①在做器械矫形练习前一定别忘记热身哦,要让身体充分活动开,避免不必要的伤害事故发生。

  ②在完成矫形练习的同时别忘了身体的全面均衡发展,让你的形体更健美。

Java|应该如何正确使用Quartz

应该如何正确使用Quartz - robbin的自言自语 - JavaEye技术网站

应该如何正确使用Quartz

关键字: quartz job
对于Web容器来说,最忌讳应用程序私自启动线程,自行进行线程调度,像Quartz这种在web容器内部默认就自己启动了10个线程进行异步job调度的框架本身就是很危险的事情,很容易造成servlet线程资源回收不掉,所以我一向排斥使用quartz。

quartz还有一个问题就是不支持cluster。导致使用quartz的应用都没有办法做群集。

那么应该如何正确的使用quartz的同时,又不影响web容器自身的线程调度呢?

办法就是自己单独启动一个Job Server,来quartz跑job,不要部署在web容器中。

其他web节点当需要启动异步任务的时候,可以通过种种方式(DB, JMS, Web Service, etc)通知Job Server,而Job Server收到这个通知之后,把异步任务加载到自己的任务队列中去。

其实想改造当前已经集成quartz的web应用也不算困难:

例如可以使用数据库的表来记录和维护任务队列和状态,把quartz部分完全从web应用中剥离出去,自己写一个Java Main程序把配置quartz的spring容器跑起来,这样Job Server就启动了(注意这个Job Server完全脱离tomcat)。此外这个Main程序应该再启动一个子线程,定期扫描数据库的任务队列表:
有新的任务就加入quartz的任务调度;
把当前任务的执行状态写入任务表;
看到删除任务的表字段状态以后,删除相应的任务。

然后web应用去掉quartz部分配置,把原来的调用quartz任务的代码改写为读写数据库的任务表,这样就把job部分完全从web容器剥离掉了,甚至web容器做cluster也没有问题了,并且多个web节点在同时读写任务表的时候,还有数据库的事务来确保操作的一致性,实在是很棒。

另外还可以单独做一个job管理界面,可以通过web界面手工添加任务,查看任务状态,删除任务等等。

Performance|为什么ORM性能比iBATIS好?

为什么ORM性能比iBATIS好? - robbin的自言自语 - JavaEye技术网站

缓存是有很多层次的,有web server前端缓存,有动态页面静态化,有页面片断缓存,有查询缓存,也有对象缓存。不同层面的缓存适用于不同的应用场景,作用也各自不同,如果可以,你全部一起用上,他们不矛盾,但这个话题比较大,现在不展开谈。

针对OLTP类型的web应用,只要代码写的质量没有问题,最终的性能瓶颈毫无疑问还是数据库查询。应用服务器层面可以水平扩展,但是数据库是单点的,很难水平扩展,所以如何有效降低数据库查询频率,减轻数据库压力,是web应用性能问题的根源。

以上所有的缓存方式都可以直接或者间接的降低数据库访问,但缓存是有应用场景的,虽然新闻网站非常适合使用动态页面静态化技术,但是例如电子商务网站就不适合动态页面静态化,而页面缓存和查询缓存可以使用的场景也不多。但是对象缓存是所有缓存技术当中适用场景最广泛的,任何OLTP应用,即使实时性要求很高,你也可以使用对象缓存,而且好的ORM实现,对象缓存是完全透明的,不需要你的程序代码进行硬编码。

用不用对象缓存,怎么用对象缓存,不是一个调优的技巧问题,而是整个应用的架构问题。在你开发一个应用之前,你就要想清楚,这个应用最终的场景是什么?会有多大的用户量和数据量。你将采用什么方式来架构这个应用:

OK,也许你偏爱SQL,那么你选择iBATIS,数据库设计当中大表有很多冗余字段,会尽量消除大表之间的关联关系,最终用户量和访问量很高以后,你会选择使用Oracle,雇佣资深的DBA,进行数据库调优和SQL调优,这是大多数公司走的路。

但是我告诉你,你还有另外一条路可以走。你可以选择ORM(不见得一定是Hibernate),数据库设计当中避免出现大表,比较多的表关联关系,通过ORM以对象化方式操作。当用户量和访问量很高以后,除了数据库端本身的优化,你还有对象缓存这条途径。对象缓存是怎样提高性能的呢?随便举个例子:

论坛的列表页面,需要显示topic的分页列表,topic作者的名字,topic最后回复帖子的作者,如果是iBATIS,你准备怎么做?

select ... from topic left join user left join post .....


你需要通过join user表来取得topic作者的名字,然后你还需要join post表取得最后回复的帖子,post再join user表取得最后回贴作者名字。

也许你说,我可以设计表冗余,在topic里面增加username,在post里面增加username,所以通过大表冗余字段,消除了复杂的表关联:

select ... from topic left join post...


OK,且不说冗余字段的维护问题,现在仍然是两张大表的关联查询。然后让我们看看ORM怎么做?

select * from topic where ... --分页条件


就这么一条SQL搞定,比上面的关联查询对数据库的压力小多了。

也许你说,不对阿,作者信息呢?回贴作者信息呢?这些难道不会发送SQL吗?如果发送SQL,这不就是臭名昭著的n+1条问题吗?

你说的对,最坏情况下,会有很多条SQL:
select * from user where id = topic_id...; .... select * from user where id = topic_id...;  select * from post where id = last_topic_id...; .... select * from post where id = last_topic_id...;  select * from user where id = post_id...; .... select * from user where id = post_id...;


事实上何止n+1,根本就是3n+1条SQL了。那你怎么还说ORM性能高呢?

因为对象缓存在起作用,你可以观察到后面的3n条SQL语句全部都是基于主键的单表查询,这3n条语句在理想状况下(比较繁忙的web网站),全部都可以命中缓存。所以事实上只有一条SQL,就是:

select * from topic where ...--分页条件


这条单表的条件查询和iBATIS通过字段冗余简化过后的大表关联查询相比,当数据量大到一定程度以后(十几万条),查询的速度会差至少一个数量级,而且对数据库的压力很小,这就是对象缓存的真正威力!

更进一步分析,使用ORM,我们不考虑缓存的情况,那么就是3n+1条SQL。但是这3n+1条SQL的执行速度一定比iBATIS的大表关联查询慢吗?不一定!因为使用ORM的情况下,第一条SQL是单表的条件查询,在有索引的情况下,速度很快,后面的3n条SQL都是单表的主键查询,在繁忙的数据库系统当中,3n条SQL几乎可以全部命中数据库的data buffer。但是使用iBATIS的大表关联查询,很可能会造成全表扫描,这样性能是非常差的。

所以结论就是:即使不使用对象缓存,ORM的n+1条SQL性能仍然很有可能超过iBATIS的大表关联查询,而且对数据库造成的压力要小很多。这个结论貌似令人难以置信,但经过我的实践证明,就是事实。前提是数据量和访问量都要比较大,否则看不出来这种效果

还是拿上面这个例子的应用场景来说,由于JavaEye网站用RoR的ActiveRecord,所以这个场景事实上就会发送3n+1条SQL语句。我从log里面看到这密密麻麻的SQL,着实非常担忧性能,所以尝试使用了find的:include选项去eager fetch,迫使ActiveRecord发送单条复杂的关联查询。但非常不幸的是,在网站服务器的production.log里面经过前后对比,发现使用:include以后,单条复杂关联查询耗时更多,数据库压力更大。

在使用memcached之后,比3n+1条的性能进一步明显提升。所以性能对比就是这样的:

ORM + Cache > ORM n+1 > iBATIS 关联查询

那为什么应用Cache可以进一步提高性能,是因为访问Cache的开销比访问数据库小的得多造成的。

应用程序根据主键key去Cache Server取value,是非常简单的算法,开销极小。
而发送一条主键查询的SQL到数据库,要经过非常复杂的过程,有SQL的解析,执行计划的优化,占位符参数的代入,只读事务的保护和隔离等等,最终虽然也命中了数据库的data buffer,但是开销确实很大。

BerkeleyDB就是一个极好的证明,它号称其查询速度是Oracle的1000倍,不是因为它做的比Oracle牛,而是因为它本质上就是一个大Cache,查询没有额外的开销。

Performance|漫谈应用缓存的命中率问题

漫谈应用缓存的命中率问题 - robbin的自言自语 - JavaEye技术网站

这篇文章源自于:

http://www.javaeye.com/topic/77195

其中很多人谈到了缓存命中率的问题,应用缓存的命中率取决于很多的因素:

1、应用场景
是OLTP还是OLAP应用,即使是OLTP,也要看访问的频度,一个极少被访问到的缓存等于没有什么效果。一般来说,互联网网站是非常适合缓存应用的场景。

2、缓存的粒度
毫无疑问,缓存的粒度越小,命中率就越高,对象缓存是目前缓存粒度最小的,因此被命中的几率更高。举个例子来说吧:你访问当前这个页面,浏览帖子,那么对于ORM来说,需要发送n条SQL,取各自帖子user的对象。很显然,如果这个user在其他帖子里面也跟贴了,那么在访问那个帖子的时候,就可以直接从缓存里面取这个user对象了。

3、架构的设计
架构的设计对于缓存命中率也有至关重要的影响。例如你应该如何去尽量避免缓存失效的问题,如何尽量提供频繁访问数据的缓存问题,这些都是考验架构师水平的地方。再举个例子来说,对于论坛,需要记录每个topic的浏览次数,所以每次有人访问这个topic,那么topic表就要update一次,这意味着什么呢?对于topic的对象缓存是无效的,每次访问都要更新缓存。那么可以想一些办法,例如增加一个中间变量记录点击次数,每累计一定的点击,才更新一次数据库,从而减低缓存失效的频率。

4、缓存的容量和缓存的有效期
缓存太小,造成频繁的LRU,也会降低命中率,缓存的有效期太短也会造成缓存命中率下降。

所以缓存命中率问题不能一概而论,一定说命中率很低或者命中率很高。但是如果你对于缓存的掌握很精通,有意识的去调整应用的架构,去分解缓存的粒度,总是会带来很高的命中率的。

这里我可以举一个实际的案例,JavaEye2.0网站在使用对象缓存之前,通过MySQL的监控工具进行观察,在连续24小时的平均每秒发送SQL条数超过了200条,在使用对象缓存之后,连续24小时的平均每秒发送SQL条数下降到了120条左右,几乎下降了一半。

考虑到很多SQL都是分页语句,关联查询,条件查询,集合操作,都是不能被缓存的SQL,而真正能够被缓存的SQL只有根据主键查询对象和对象关联对象的查询。所以真正能够被缓存的SQL估计最多占所有SQL的60%。所以换算下来,应用缓存的命中率之高,已经相当惊人了。

不过这里要提醒的一点,有将近一半的SQL都被缓存,不意味着性能可以提升一倍。这是因为能够被缓存的都是按照主键查询单条记录的SQL,这些SQL本身即使发送到数据库,对数据库造成的压力也没有想像的那么大。真正对数据库造成庞大压力的正是那些没有索引的大表查询,和造成了全表扫描的关联查询,这些一旦涉及到全表扫描的查询,才是性能的真正杀手。当然了,不管怎么说,通过使用对象缓存,是毫无疑问可以大幅度降低数据库的负载压力的,有效提升web应用的性能的。

关于这一点,我再给出一组数据来加深大家的印象,通过使用操作系统网络工具进行统计:

JavaEye网站web server的端口每秒数据流量是2MB;
JavaEye网站的MySQL数据库端口的每秒数据流量是1.2MB;
而网站的memcached的端口每秒的数据流量高达5MB。

SearchEngine|JavaEye3.0开发手记之三 Sphinx

JavaEye3.0开发手记之三 - 狮身人面 - robbin的自言自语 - JavaEye技术网站

Sphinx的含义是"狮身人面像",是古埃及的金字塔建筑,传说Sphinx是有翼的狮身女怪,她常叫过路行人猜谜, 猜不出者即遭杀害。

但是我这里说的Sphinx是一个高性能的搜索引擎:

http://www.sphinxsearch.com/

Sphinx是一个俄国人开发的搜索引擎,它的主要特点是:

一、性能非常出色
150万条记录一两分钟就索引完毕,2-4GB以内的文本检索速度不到0.1秒钟。ferret也望尘莫及,更不要说lucene了。

二、和数据库集成性很好
Sphinx通过配置文件可以自行读取数据库信息做索引,不依赖任何外部的应用程序,并且可以作为一个daemon进程启动,支持分布式检索,并发响应性能很好。因此很多过去使用ferret的人因为并发检索的问题都改用Sphinx了。

三、可以做MySQL的全文检索
MySQL的数据库引擎是可插拔的结构,Sphinx开发了一个SphinxSE数据库引擎,可以在编译MySQL的时候直接编译到MySQL里面去,这样的话,可以在数据库级别支持高性能的全文检索,那么你可以以如下SQL方式去全文检索了:

select * from xxxx where query='test;sort=attr_asc:group_id' AND ....;

很棒吧。


四、RoR支持也很棒
有一个acts_as_sphinx插件,类似acts_as_ferret,集成到RoR里面很简单。

Sphinx目前可能存在的问题估计还是中文分词问题:

Sphinx支持UTF-8编码的分词,但是他自己的文档上面说仅仅支持英文和俄文的分词,因此我估计对中文分词可能还不能很好的支持。

总之,Sphinx是我们另外一个很棒的选择。

BestPractice|JavaEye网站的RoR性能优化经验谈 RoR的部署方案选择

RoR的部署方案选择 - robbin的自言自语 - JavaEye技术网站

RoR的部署方式从架构上来说分为前端和后端:

一、前端
前端的作用就是处理静态资源,将动态请求分发到后端,有时候也带有一些额外的功能,例如对特定URL进行rewrite和redirect,对HTTP输出进行gzip压缩等等。

前端目前已知的可以选择apache, lighttpd, litespeed, nginx, haproxy

1、apache2.2
apache是全球市场占有率最高的web server,超过全球互联网网站50%的网站都用apache。apache2.2 + mod_proxy_balancer是一个非常流行,非常稳定的方案。

使用apache2.2唯一的问题就是apache的性能和后面那些轻量级web server相比,差太远了。一方面在处理静态请求方面apache要比lighttpd慢3-5倍,内存消耗和CPU消耗也高出一个数量级,另一方面mod_proxy_balancer的分发性能也不高,比haproxy差很远。

2、lighttpd
lighttpd是一个轻量级高性能web server,一个在MySQL Inc工作的德国人写的。性能很好,内存和CPU资源消耗很低,支持绝大多数apache的功能,是apache的绝好替代者。目前lighttpd已经上升到全球互联网第四大web server,市场占有率仅此于apache,IIS和Sun。

lighttpd唯一的问题是proxy功能不完善,因此不适合搭配mongrel来使用。lighttpd下一个版本1.5.0的proxy模块重写过了,将会解决这个问题。

3、litespeed
和lighttpd差不多,商业产品,收费的。比lighttpd来说,多一个web管理界面,不用写配置文件了。litespeed专门为单机运行的RoR开发了一个lsapi协议,号称性能最好,比httpd和fcgi都要好。他的proxy功能比lighttpd完善。

litespeed的缺点我却认为恰恰是这个lsapi。因为lsapi不是web server启动的时候启动固定数目的ruby进程,而是根据请求繁忙程度,动态创建和销毁ruby进程,貌似节省资源,实则和apache2.2进程模型一样,留下很大的黑客攻击漏洞。只要黑客瞬时发起大量动态请求,就会让服务器忙于创建ruby进程而导致CPU资源耗尽,失去响应。

当然,litespeed也支持httpd和fcgi,这个和lighttpd用法一样的,到没有这种问题。

4、nginx
一个俄国人开发的轻量级高性能web server,特点是做proxy性能很好,因此被推荐取代apache2.2的mod_proxy_balancer,来和mongrel cluster搭配。其他方面和lighttpd到差不多。

要说缺点,可能就是发展的时间比较短,至今没有正式版本,还是beta版。没有经过足够网站的验证。

5、haproxy
就是一个纯粹的高性能proxy,不处理静态资源的,所有请求统统分发到后端。

二、后端
后端就是跑ruby进程,处理RoR动态请求了。运行后端ruby进程有两种方式:

1、fcgi方式
准确的说,不能叫做fcgi方式,其实就是启动一个ruby进程,让这个ruby进程监听一个tcp/unix socket,以fcgi协议和前端通讯。所以fcgi不是指ruby进程的运行方式,而是ruby进程使用的通讯协议。这就好比你tomcat可以用http也可以使用ajp通讯一样,tomcat自己的运行方式都一样的,只是通讯方式不一样。

fcgi方式启动ruby进程,可以使用lighttpd带的一个spawn-fcgi工具来启动(JavaEye目前采用这种方式)。

值得一提的是,apache2.2的mod_fastcgi的方式和上面还不太一样,由apache动态创建fcgi进程和管理fcgi进程,这种方式和litespeed的lsapi面临的问题是一样的,此外apache的mod_fastcgi自己也有很多严重的bug,是一种很糟糕的部署方式。这种糟糕的部署方式也败坏了fcgi的名声。

fastcgi只是一种协议,虽然古老,但并不是不好用,http协议也很古老。没有必要因为apache的mod_fastcgi的运行方式的问题而连带把fastcgi都一同否定了。fastcgi只是一个协议(程序之间的语言),是apache的mod_fastcgi这个模块有问题。打个比方,有个人英语水平很差,和你用英语对话,总是结结巴巴的,那你说是英语(fastcgi)这种语言有问题呢?还是和你对话的这个人(mod_fastcgi)有问题呢?

2、http方式
也就是用mongrel去跑ruby进程,由于mongrel实际上已经是一个简单的http server,所以也可以单独作为web server使用。mongrel现在越来越受欢迎了。

用fcgi方式还是http方式,我个人觉得区别不大,关键还是看应用的场合,一般而言,推荐的搭配是:

lighttpd + fcgi 或者 nginx +mongrel,而apache因为性能差距,而不被推荐。

JavaEye为什么用lighttpd + fcgi呢?原因如下:

1) lighttpd发展了好几年了,市场占有率也相当高,是一个经过实践检验的server,它的文档也很全;而nginx还没有经过足够的市场检验,文档也很缺乏
2) JavaEye的ruby进程和web server在一台机器上面跑,通过unix socket使用fcgi协议通讯可以避免tcp的网络开销,其通讯速度比使用tcp socket使用http协议通讯要快一些。

什么场合使用haproxy?

大规模部署,例如你的RoR应用到十几台服务器上面去,你用haproxy会更好,可以方便的添加删除应用服务器节点,proxy性能更好。


RoR部署方案深度剖析

关键字: deployment
RoR的部署方案可谓五花八门,有Apache/Fastcgi方式的,有Nginx/Mongrel方式的,还有lighttpd/Fastcgi方 式,也有人使用HAProxy/Mongrel,各种部署方式都是众说纷纭,让人搞不清楚哪种方式更好一些。我的这篇文章就是希望结合我们运营 JavaEye网站一年多以来的经验(通过统计Rails的production.log,JavaEye网站目前每天处理超过70万200 OK状态的Ruby动态请求,应该是国内目前负载量最大的RoR应用了),为大家剖析RoR部署方案的优劣,帮助大家选择适合自己生产环境的RoR部署方 式。

在讨论部署方案之前,先让我们看一下RoR网站部署的简单架构:



浏览器的HTTP访问请求首先达到Web服务器,充当Web服务器的一般是Lighttpd/Apache/Nginx,如果访问请求包含静态资 源,那么Web服务器就会直接从本地硬盘读取静态资源文件,例如图片,JavaScript,CSS等等,返回给客户端浏览器;如果访问请求是动态请求, 那么Web服务器把URL请求转发到后端的FastCGI/Mongrel来处理,等到FastCGI/Mongrel处理完请求,将生成的页面数据返回 给Web服务器,最后Web服务器把页面数据发送到客户端的浏览器。

从RoR的部署方式来看,主要由前端的Web服务器和后端的应用服务器构成:前端的Web服务器可以使用Apache,Lighttpd,Nginx和Litespeed,后端的应用服务器可以使用FastCGI和Mongrel,下面我们分门别类的介绍和剖析:

一、介绍Web服务器
Web服务器的主要作用有两点:一是处理静态资源,二是将动态请求分发到后端应用服务器,然后接收后端应用服务器生成的页面数据,将其返回浏览器,充当了一个信息沟通的桥梁作用,在本文当中我们重点分析后者的作用。

1、Apache 2.2

Apache是全球互联网使用最广泛的Web服务器,但在处理静态资源文件上却不是性能最优秀的Web服务器,不过一般情况下,静态资源的访问并不是RoR网站的瓶颈,因此也不必过于在意这一点。

Apache 2.2既支持HTTP Proxy方式连接后端的Mongrel应用服务器,也可以通过mod_fastcgi/mod_fcgid来连接FastCGI应用服务器:当以 HTTP Proxy方式连接Mongrel的时候,Apache接收Mongrel返回的页面数据的buffer size最大只能开到8KB(默认是4KB或者8KB),因此当页面数据超过8KB的时候,可能需要Apache和Mongrel之间发生多次交互;当以 mod_fastcgi方式连接FastCGI应用服务器的时候,接收返回数据的Buffer size仍然只有8KB而已,如果使用mod_fcgid,那么buffer size为64KB,有了很大的改善。

2、Nginx

Nginx是俄国人出品的轻量级Web服务器,在处理静态资源方面,据说性能还略微超过Lighttpd,但是Nginx在性能消耗方面略微比Lighttpd要高一些。

Nginx内置了良好的HTTP Proxy和FastCGI支持,因此即可以连接Mongrel,也可以连接FastCGI服务器,在这两种情况下,Nginx默认的接收应用服务器返回 数据的Buffer Size也只有区区的8KB,但是你可以自行设置更大Buffer Size。

3、Lighttpd

Lighttpd是全球互联网排名第五的Web服务器,也是近两年来上升最快的Web服务器,特别是很受一些著名Web 2.0大网站的欢迎,例如wikipedia的某些服务器,youtube的视频服务器,在国内,豆瓣网站和JavaEye网站都是Lighttpd的绝 对拥护者。在处理静态资源方面,Lighttpd性能远远超过Apache。

Lighttpd既支持HTTP Proxy连接Mongrel,也支持FastCGI方式,但是Lighttpd的FastCGI支持在所有流行的Web服务器当中可能是最优秀的,所以 用FastCGI的网站都很喜欢Lighttpd。Lighttpd在接收后端应用服务器返回数据的方式上和Apache/Nginx有非常大的区别:

Apache/Nginx是针对每个应用服务器连接分配固定Size的Buffer,而且默认只开8KB,这个Size对于现在网页动辄50- 100KB的情况来说,显得过于保守,如果应用服务器的返回数据无法一次填满Web服务器的Buffer,那么就会导致应用服务器和Web服务器之间多次 数据传输,这对于RoR网站的性能会造成一些相关的影响,我们在后面会详细的分析。

Lighttpd并不针对应用服务器的每个连接分配固定的Buffer,而是尽可能的把应用服务器返回的数据一次性接收下来,因此无论应用服务器返回多大的数据量,Lighttpd都是照单全收,胃口非常惊人。

4、Litespeed

Litespeed是一个商业收费的Web服务器,静态资源处理能力据它自己的评测数据比Lighttpd略高。Litespeed也同时支持 HTTP Proxy连接Mongrel和FastCGI连接应用服务器。此外Litespeed专门为单机运行的RoR开发了一个lsapi协议,号称性能最好的 RoR通讯协议,比HTTP Proxy和FastCGI都要好。但是lsapi的运行方式有很大缺陷:因为lsapi不是web server启动的时候启动固定数目的ruby进程,而是根据请求繁忙程度,动态创建和销毁ruby进程,貌似节省资源,实则留下很大的黑客攻击漏洞。只 要黑客瞬时发起大量动态请求,就会让服务器忙于创建ruby进程而导致CPU资源耗尽,失去响应。

由于Litespeed在运行RoR方面并没有表现出比Lighttpd优越之处,而且还是收费软件,企业版本售价在双核CPU上面每年收费 499美元,并且也不开源,因此我们就不再把关注点放在Litespeed上面。当然Litespeed收费也不是白收的,它提供了非常好用的基于Web 的服务器管理界面,以及非常多的安全性方面的设置参数。

5、HAProxy

HAProxy并不是一个Web服务器,他既不能处理静态资源,也不能充当浏览器和应用服务器之间的缓冲桥梁,他只是充当了一个请求分发的软件网 关作用。ThoughtWorks公司的RubyWorks选择使用HAProxy + Mongrel Cluster的方式来部署RoR应用,不能不说是一个愚蠢的方案。这种方案其实相当于把n个Mongrel应用服务器捆绑起来,直接充当Web服务器, 而Mongrel毕竟是一个Ruby写的服务器,无论是网络IO能力,还是静态资源的处理速度,无法和真正的Web服务器相提并论,让Mongrel直接 处理静态资源和调度网络IO,会造成服务器资源毫无必要的极大开销,因此HAProxy也不在我们的考虑之列。


二、分析应用服务器的处理方式

无论是Mongrel还是FastCGI,都能够良好的运行Rails服务器,但是他们在和Web服务器之间的数据传输方式上存在一些差别,而正是这些差别,对部署方式有重大的影响:

1、Mongrel

Mongrel本身可以直接充当Web服务器,但在这种情况下性能并不会好。因为Mongrel只有HTTP协议的解析部分是用C语言编写的,其 余所有代码都是纯Ruby的。在处理静态资源下载上面,Mongrel的实现方式非常低效率,他只是简单的以16KB为单位,依次读入文件内容,再写出到 网络Socket端口,其性能远远比不上传统的Web服务器调用操作系统的read()和write()库实现的静态文件下载速度,如果和现代Web服务器实现的sendfile方式的"零拷贝"下载相比,简直就是望尘莫及。

Mongrel使用了Ruby的用户线程机制来实现多线程并发,并且使用了一个fastthread补丁,改善了Ruby用户线程的同步互斥锁问 题。但是Ruby并不是本地线程,我们也不要对Mongrel的网络IO负载能力抱有什么不切实际的幻想。同时Rails本身也不是线程安全的,因此 Mongrel在执行Rails代码的过程中,完全是加锁的状态,那和单进程其实也没有太大差别。

因此,当我们使用Mongrel的时候,一般会在前端放置Web服务器,通过HTTP Proxy方式把请求转发给后端的Mongrel应用服务器。在这种情况下,Mongrel只处理动态请求,在运行Rails框架生成页面数据之后,把数 据返回给Web服务器就可以了。但是在这种部署方案下,有一个很重要的细节被我们忽视了,Mongrel运行Rails生成的页面数据是怎么返回给Web 服务器的呢?通过仔细钻研源代码我们可以搞清楚Mongrel处理Rails请求的细节:

1) Mongrel接收到请求以后,启动一个ruby线程解析请求信息
2) 加锁,调用Rails Dispatcher启动Rails框架
3) Rails处理完毕,创建一个StringIO对象,把Rails生成的页面数据写入到StringIO中
4) 解锁,把StringIO的数据flush到Web服务器

这个StringIO对象其实很重要!它充当了一个输出缓冲区的作用,我们设想一下,当Mongrel作为独立的Web服务器的时候,如果 Rails生成的页面比较大,而客户端浏览器下载页面的速度又比较慢,假设没有这个StringIO对象,会发生什么问题? Rails线程在执行render方法的时候就会被挂住!同步互斥锁没有解锁,Mongrel再也无法处理下一个动态请求了。

当Mongrel仅仅作为应用服务器的时候,这个StringIO仍然很重要,为什么?我们前面提到过了,Apache/Nginx的接收缓冲区 都只开了8KB,如果页面比较大,Mongrel就没有办法一次性把数据全部推给Web服务器,必须等到Web服务器把接收缓冲区的8K数据推到客户浏览 器端以后,清空缓冲区,才能接收下一个8KB的数据。这种情况下,Mongrel必须和Web服务器之间进行多次数据传输,才能完成整个Web响应的过 程,显然没有一次性把页面数据全部推给Web服务器快。如果Web服务器使用Lighttpd的话,情况会不一样。当Mongrel把StringIO的 数据flush出去的时候,Lighttpd是一次性全部接收下来了,不需要多次交互,因此Lighttpd+Mongrel的RoR网站的实际速度要快 于Apache/Nginx+Mongel。

Mongrel使用StringIO对象缓存输出结果,在某些特殊的情况下会带来很大的安全隐忧。我们假设使用服务器端程序控制带权限的文件下 载,某用户下载的是一个100MB的文件,该用户使用了多线程下载工具,他开了10个线程并发下载,那么每个线程Mongrel在响应之后,都会把整个文 件读入到内存的StringIO对象当中,所以总共会创建出来10个StringIO对象保存10份文件内容,所以Mongrel的内存会一下暴涨到 1GB以上。而且最可怕的是,即使当用户下载结束以后,Mongrel的内存都不会迅速回落,而是一直保持如此高的内存占用,这是因为Ruby的GC机制 不好,不能够及时进行垃圾回收。

也许你会觉得不太可能下载100MB那么大的附件,但是以JavaEye网站为例,圈子的共享文件最大允许10MB,只要用户在多台机器上面,每 台机器开100个线程下载圈子共享文件,每个Mongrel的内存占用都会立刻超过1GB,用不了几分钟,服务器的物理内存就会被耗尽,网站失去响应。这 个缺陷非常容易被别有用心的黑客利用,攻击网站。这也是JavaEye网站为什么始终不用mongrel的原因之一。

通过上面的剖析,我们知道Mongrel在使用Lighttpd的时候,可以达到最快的RoR执行速度,但是Lighttpd当前的1.4.18 版本的HTTP Proxy的负载均衡和故障切换功能有一些bug,因此一般很少有人会使用这种方式。大多数人都会采用Mongrel搭配Apache2.2或者 Nginx,但是正如我们上面做分析的那样,Apache/Nginx的Buffer Size实在是一个很讨厌的限制,特别是Apache只能最大开8KB的Buffer,因此我建议使用Nginx搭配Mongrel,并且把Nginx的 Proxy Buffer Size设置的大一些,比如说设置为64KB,以保证大多数页面输出结果可以一次性flush到Web服务器去。

2、FastCGI

很多人对FastCGI谈虎色变,仿佛FastCGI就是内存泄漏,性能故障的罪魁祸首,又或者嫌弃FastCGI太古老了,已经被淘汰掉的技术 了,其实这是一个很大的误解。FastCGI本质上只是一种进程间通讯的协议,虽然是一个比较古老的协议,但是还是比HTTP协议年轻多了,HTTP协议 不是照样现在很流行吗?

在PHP/ASP/JSP流行之前,FastCGI曾经非常普及,只不过那个时代的FastCGI程序是用C语言编写的,写起来太费劲,而 PHP/ASP/JSP相比之下,写起来就太简单了,所以FastCGI就渐渐被丢到了历史的故纸堆里面。但是最近两年来,由于Ruby和Python的 快速Web开发框架的强势崛起,FastCGI仿佛又咸鱼翻身了。

当我们以FastCGI方式运行Rails应用服务器的时候,每个FastCGI进程都是单线程运行的,考虑到Rails本身不是线程安全的,所 以和Mongrel运行Rails的实际效果是一样的,都是每个进程只能跑一个Rails实例。但是FastCGI在Rails生成页面数据返回给Web 服务器的方式和Mongrel截然不同:

前面我们说到Mongrel自己开了输出缓冲区,而FastCGI则完全不开任何缓冲区,当Rails执行render方法的时候, FastCGI实际执行的是FCGI::Stream.write方法调用,直接把数据写给Web服务器了。此时如果Web服务器是 Apache/Nginx,会发生什么?

如果我们使用mod_fastcgi模块,那么Apache的接收缓冲区就是8KB;
如果我们使用mod_fcgid模块,那么Apache的接收缓冲区就是64KB;(mod_fcgid是中国人开发的取代mod_fastcgi的开源项目,在Apache社区很受欢迎,谁敢说中国人只是开源"消费"国?)
如果我们使用Nginx服务器,那么默认的接收缓冲区就是8KB,但是可以改得更大;

如果页面数据比较大,超过8KB,会怎么样? FastCGI进程被挂在render方法上!必须等到Web服务器的缓冲区清空,把页面数据全部接收下来以后,FastCGI进程才能结束本次 Rails调用,处理下一个请求!所以千万别用Apache/Nginx搭配FastCGI应用服务器,否则你的RoR应用会死的很难看。根据我个人的测 试数据表明,同样的测试负载,Apache搭配70个FastCGI进程挂掉,但是Lighttpd搭配30个FastCGI进程轻松跑完!

当FastCGI搭配Lighttpd的时候,我们知道Lighttpd会一次性照单全收FastCGI送过来的页面数据,所以FastCGI进 程并不会被挂住。如果我们对比一下Lighttpd搭配Mongrel和FastCGI会发现,Lighttpd搭配FastCGI性能最好,为什么呢?

Mongrel首先自己会用StringIO缓冲页面数据,然后推送给Lighttpd以后,Lighttpd也在内存当中缓冲了一份页面数据, 造成了毫无必要的double buffer的开销。这自然不如FastCGI不做任何缓冲,直接推给Lighttpd性能来得高,内存消耗少了。

我们的方案分析到这里,大家应该自己心里有结论了,Lighttpd+FastCGI是性能最佳,服务器资源消耗最少的RoR部署方案,事实上目 前RoR网站部署使用最多最流行的也是Lighttpd+FastCGI方式,而JavaEye网站,自然也是这种方式的部署。因此我们可以对各种方案进 行一个性能优劣的排队:

引用
Lighttpd+FastCGI > Lighttpd+Mongrel > Nginx+Mongrel > Apache+Mongrel > Ngignx+FastCGI > Apache+FastCGI


其中Lighttpd+FastCGI是性能最佳方案,而Apache+FastCGI是性能最差方案。


有些细心的同学可能会产生一个新的疑问?你说到底,之所以Lighttpd跑RoR性能最好,还是在于Lighttpd接收数据不限定缓冲区的大 小,而Apache/Nginx限定了缓冲区大小所至。那为什么Nginx要限制呢?Lighttpd如果不限制的话,会不会导致Lighttpd内存爆 掉?

Nginx限制Proxy Buffer Size其实也有道理,因为Nginx并不是为RoR量身打造的Web服务器,Nginx最广泛的用途还是高负载大访问量的代理服务器,在Nginx主要 的应用场合,如果不做这样的限制,那Nginx端的资源消耗就相当高了,有可能会拖累所代理的服务速度。

Lighttpd主要用途之一就是提供高性能的FastCGI支持的Web服务器,所以必须为FastCGI量身打造。Lighttpd端承担的 负载越高,就越能有效的加快FastCGI执行速度。其实我们稍微心算一下,假设Lighttpd后面挂1000个FastCGI进程,每个 FastCGI进程同时送过来50KB的页面数据,Lighttpd就是全部吃下来,也不过只消耗50MB的内存而已,而事实上1000个FastCGI 进程足以支撑每日上千万的大网站了。

只有当我们使用服务器端程序控制大文件下载的时候,有可能造成Lighttpd内存暴涨,例如某个用户使用100个线程并发下载JavaEye圈 子的共享文件,在没有特殊处理的情况下,Lighttpd将全部吃下100个FastCGI进程送过来的10MB数据,就会立刻暴涨1GB的内存。这种情 况怎么办呢?其实我们也有办法让Lighttpd一点内存都不吃, 请看我写的另外一篇文章:RoR网站如何利用lighttpd的X-sendfile功能提升文件下载性能

可能很多人看了我的文章,对结论觉得很诧异,既然Lighttpd+FastCGI这样好,为什么那么多人都推崇Mongrel,否定FastCGI呢?我想,不外乎几个原因:

一、Lighttpd+FastCGI配置起来比较专业,而Mongrel配置简单

尽管我当初第一次搭建Lighttpd+FastCGI环境没费什么周折,但是我观察到非常多的Ruby程序员很难成功搭建一个Lighttpd +FastCGI的环境出来,很多人连Lighttpd都无法独立的运行起来。这也许是因为很多程序员习惯了Windows开发环境,对于Unix上面通 过源代码编译安装的方式过于陌生造成的。而我从97年开始使用Unix,至今已有10年历史,因此搭建这样简单的系统,对我来说不造成什么障碍。

而Mongrel就简单了,gem install mongrel安装完毕,mongrel_rails start启动,哪个人不会?毕竟绝大多数开发人员和部署人员不是高手,他们熟悉哪种方式,自然就会推崇哪种方式。


二、Mongrel可以独立作为Web服务器运行,开发环境和部署环境统一

一般来说,程序员肯定是尽量保持开发环境和部署环境的一致性,避免部署到生产环境出现不测的后果。既然在开发环境熟悉了Mongrel,当然更加愿意在生产环境使用Mongrel,而不愿意碰没有接触过的Lighttpd。


三、Mongrel支持HTTP协议,因此不论监控还是集成其他服务都比较简单,容易玩出更多的花活。

HTTP协议要比FastCGI协议普及的多,因此通过HTTP方式的监控工具,群集管理工具,集成其他服务的工具都是一抓一大把。而支持 FastCGI的第三方工具就少得可怜了。你要玩很多花活出来,用FastCGI的话,就难免得自己开发相应的工具,那当然不如使用Mongrel方便 啦。

最后,如果你看了这篇文章,想摩拳擦掌的安装一把Lighttpd+FastCGI的话,那么我的这篇文章就是最好的安装指南:

在Linux平台上安装和配置Ruby on Rails详解



JavaEye网站的RoR性能优化经验谈

JavaEye网站从2006年9月11日上线基于RoR的2.0版本开始,到现在已经运行了将近一年半了。在这一年半的时间里,JavaEye网站的每 日PV从最开始的5万,缓慢增长到了现在的60万。随着网站负载的不断增加,我们也在不断尝试和调整网站的性能,积累了不少第一手RoR应用性能优化的实 战经验。虽然我们并不是RoR性能优化的权威专家,我们所积累的经验也许并不是最优实践,但是作为国内最早涉足RoR商业运营的互联网网站之一,我们非常 乐意分享和交流我们的实战经验,以帮助后来者节省必要的摸索时间。

RoR惊人的开发速度恐怕是每个互联网创业者都梦寐以求的,但是随着网站流量的不断增大,可能大多数采用RoR的网站或迟或早会遇到RoR的性能 瓶颈,我的一个朋友capitian说过一句很有意思的话:"RoR应用做到后来,总有自己修改底层的冲动"。就我所了解和掌握的情况来看,很多RoR网 站都过早的遇到了性能瓶颈,一个很普遍的现象就是:RoR应用的CPU负载要远远高于数据库的负载。这是一个有点违背常理的现象,因为我们知道,硬盘IO 速度要比内存慢得多,所以一般Web应用的性能瓶颈往往会出现在数据库IO上,因此优化数据库访问,进行对象缓存是非常有效的性能优化手段。但是一旦应用 服务器负载比数据库还高的话,单纯的对象缓存就无用武之地了。下面我们从几个方面分别谈一谈如何进行RoR的性能优化:


应用的部署

RoR应用的部署包括操作系统,Web服务器,应用服务器和数据库四个方面:

一、操作系统

1、发行版本

RoR适合于部署在Unix类操作系统上面,通常比较多的人使用RHEL/CentOS/Ubuntu,我们比较偏爱SuSE Linux,对于我们服务器使用的AMD Opteron x86_64的CPU来说,SLES要比RHEL有更多的优化。另外应该尽量使用64位版本操作系统,以充分发挥x86_64 CPU的性能,并且x86_64的Linux很多Kernel参数也大很多,代价就是需要更多的物理内存。

2、文件系统

Linux最常用的文件系统是ext3,但我们使用的是Reiserfs文件系统。Reiserfs在读写大量小文件的目录性能非常高,即使处理 目录下面直接存放10万个文件,性能仍然不会下降。我们知道默认情况Rails会对每个浏览器会话在硬盘生成session文件,一个繁忙的网站,临时文 件目录下面有上万乃至几万个session文件是很常见的现象。对于这种目录下面几万个小文件的存取,reiserfs要比ext3性能高一个数量级。如 果希望对session文件有更好的存取性能,可以把临时目录链接到Linux的内存文件系统/dev/shm目录下面,这样实际上session文件的 存取都是直接内存操作了,这种方式唯一的问题在于不能支持群集部署。如果你已经升级到了Rails2.0,可以采取把session保存到Cookie里 面的方式,既可以避免服务器处理session的开销,而且还支持群集部署,是大规模网站部署的首选方式。

3、内核的网络参数调整

对于流量很大的网站来说,默认的Linux内核网络参数偏小,因此如果你的网站流量非常大,或者上传下载大文件比较多,可以针对性的调整内核网络参数,扩大内核的TCP接收数据和发送数据的Buffer缓冲区大小,比方说:
引用
net.core.rmem_default=262144
net.core.wmem_default=262144
net.core.rmem_max=262144
net.core.wmem_max=262144
net.ipv4.tcp_rmem=4096 65536 524288
net.ipv4.tcp_wmem=4096 65536 524288

参数具体调整,可以Google相关的Linux内核参数的文档,这里不展开详谈。


二、Web服务器

Web服务器首选Lighttpd,因为Lighttpd在和后端的应用服务器通讯方式上做了足够的优化:当POST大数据量的时候, Lighttpd在完整的接收客户端浏览器的数据之后,才会一次性发送给应用服务器;同样的,Lighttpd也是一次性把应用服务器处理的页面数据全部 接收,不设置Buffer Size的限制。因此Lighttpd能够尽最大可能的减轻应用服务器的负担,减少应用服务器用于处理数据传输的延迟,更加有效的利用应用服务器资源。这 方面的详细的论述请看:RoR部署方案深度剖析

关于Lighttpd的安装可以参考在Linux平台上安装和配置Ruby on Rails详解,这里仅谈Lighttpd的性能优化的几个要点:

1、网络IO调度方式
Linux Kernel 2.6支持sysepoll方式调度网络IO,能够处理极高的并发连接请求,Lighttpd可以通过配置文件打开sysepoll支持:
引用
server.event-handler = "linux-sysepoll"


2、网络IO传输方式
Linux Kernel 2.6支持sendfile方式传输数据,Lighttpd可以通过配置文件打开sendfile支持:
引用
server.network-backend = "linux-sendfile"

此外Lighttpd还支持应用服务器参与的文件下载控制X-sendfile,详细的论述请看:RoR网站如何利用lighttpd的X-sendfile功能提升文件下载性能

3、文件状态缓存
Lighttpd通过stat()调用获得文件被修改的信息,来决定当请求同一个静态文件资源的时候,是否需要再次读取硬盘文件。但是每次 stat()调用也有一定的开销,Lighttpd支持通过Fam Server来减少stat调用。即每次当文件被修改之后,Kernel会发送一个消息通知Fam Server,而Lighttpd会通过进程间通讯连接Fam Server,可以知道文件是否被修改的信息,不必再每次调用stat()。
引用
server.stat-cache-engine = "fam"


4、限定POST Size
为了避免黑客恶意的攻击服务器,伪造超大Post数据包轰炸Web服务器和应用服务器,可以限制Request请求的大小,例如限制为10MB:
引用
server.max-request-size = 10240


5、日志文件
Lighttpd是单进程单线程的服务器,调度网络IO性能是极高的,但是在某些极端情况下,单进程服务器也有风险,即一旦被某操作系统调用挂 住,整个服务器就没有办法响应请求了。比方说服务器其他进程导致的IO WAIT很高,操作系统的buffer又不够的时候,Lighttpd在大量的写access log就有被挂住的可能性。因此如果Lighttpd日志对你的参考价值不大,可以考虑关闭掉。像JavaEye网站每天Lighttpd产生430万条 log,对硬盘IO也是一个不小的负担,既然已经开着Rais的production.log,那么Lighttpd的access log没什么参考价值了,那就关掉它。

Lighttpd的性能优化请看其作者写的文章:
http://trac.lighttpd.net/trac/wiki/Docs%3APerformance


三、应用服务器

Ruby的应用服务器可以使用FastCGI,或者Mongrel,如果我们使用Lighttpd的话,FastCGI是最好的搭配。

1、FastCGI和Lighttpd的通讯方式

如果FastCGI和Lighttpd是在同一台服务器,那么建议采用Unix Socket通讯,这种通讯方式比TCP要快一些,FastCGI可以通过Lighttpd自带的spawn-fcgi命令行工具启动,创建socket 文件,而Lighttpd监听socket文件。如果两者不在同一台服务器,需要群集部署,那就必须采用TCP Socket通讯,方式是一样的。

2、FastCGI进程应该开多少个合适?

Rails是单进程方式运行的,理论上来说,开几个FastCGI进程,就只能并发响应几个请求。对于繁忙的网站来说,峰值期间每秒有几十个动态 请求是很正常的事情,但实际上FastCGI进程并不需要开那么多。这是因为前端的Web服务器在处理用户浏览器连接,发送Request请求需要相当长 的时间,在FastCGI处理完请求释放该连接以后,Web服务器还需要相当长的时间才能把页面数据完整的发送到客户端浏览器。用户在点击一个链接以后, 等待1-2秒,页面内容就显示出来,这对用户的感觉来说已经是非常快的了,而FastCGI用于处理该请求可能只需要0.1秒,那么一个FastCGI进 程虽然并不能够真正的并发运行,但实际上的效果是他可以在1秒之内处理10个请求,让10个用户在同时访问网站的过程当中感觉不到明显的延迟。

因此FastCGI需要开多少个,取决于你的网站峰值期间每秒有多少个用户请求过来,而你的FastCGI又能够以多快的速度处理请求。比方说你 的网站峰值期间每秒有50个动态请求,FastCGI在峰值期间处理每个请求需要0.2秒,那么实际上你只需要开10个FastCGI进程就足够了,为了 应付突发的峰值请求,你可以在这个计算量上面增加一些余量,比方说15-20个进程,肯定是绰绰有余了。

关于FastCGI的性能优化,可以参考Lighttpd作者的文章,虽然他是针对PHP跑FastCGI写的,但对RoR也有参考价值:
http://trac.lighttpd.net/trac/wiki/Docs%3APerformanceFastCGI



四、数据库

JavaEye网站使用MySQL5.0.XX版本,数据库引擎是InnoDB。关于MySQL数据库的调优,推荐大家看MySQL Performance Blog, 作者是一个MySQL性能调优方面的专家,并且提供MySQL咨询服务。他的博客上面有很丰富的关于MySQL调优的文章和演讲文稿,特别是关于 InnoDB方面,非常深入。JavaEye的数据库调优就是根据他的InnoDB演讲文稿来调整的,一般说来,有几个需要调整的参数:

innodb_buffer_pool_size
这个参数很重要,越大越好,对于专用的数据库服务器一般建议开服务器内存的50%以上。

query_cache_size
查询缓存,对于查询的性能提高有很大帮助,但不宜开得过大,查询缓存的过期可能很频繁,过大查询缓存反而降低性能,增加服务器开销

innodb_flush_method = O_DIRECT
针对InnoDB的数据文件,关闭操作系统的文件缓冲,由于InnoDB自己有巨大的Buffer Pool,操作系统对文件的读写缓冲功能反而会降低MySQL的InnoDB的IO性能。

最后针对数据库的SQL优化来说有两点原则:

1、对数据库表要适当的创建索引
特别是出现在where查询条件当中字段,和关联查询当中的外键,要高度注意。

2、尽量避免大表的全表扫描和数据库的硬盘IO
查询比较慢的SQL要explain一下,看看是否发生了全表扫描,采取各种措施减少或者避免大表的全表扫描问题,例如拆分表等等。

最后针对MySQL数据库运行情况,我们可以用show status; 和 show innodb status\G 来监测。


Rails应用程序的优化

Rails应用程序优化包括ruby解析器的优化,缓存的使用,以及应用代码级别的优化。Stefans Kaes曾经在Railsconf 2006有一个Rails应用程序优化的演讲,他的演讲PPT是极好的Rails性能优化指南,可以在这里下载:http://www.javaeye.com/topic/24508 。他还编写了一个用于Rails性能测试的软件包RailsBench,大家可以参考。由于Stefans Kaes的代码优化文档已经写的非常详细了,因此我就不在一一复述,只提出几点对性能影响比较大的方面:


一、ruby解析器的优化

ruby的解析器性能是很糟糕的,ruby早期的主要用途是取代perl写批量处理的脚本的,并不是为服务器应用编写的,因此在内存分配策略上非 常不适合服务器应用。Stefans Kaes编写了一个ruby GC的补丁文件,在railsbench下载包里面提供了。虽然当前Railsbench提供的GC补丁只有针对ruby 1.8.4和1.8.5版本的,但是在ruby 18,6上面使用1.8.5的GC补丁也完全没有问题。GC补丁的作用主要是针对Rails应用开大了ruby的内存堆,可以有效提高内存堆的利用率,降 低GC的频率。根据Stefans Kaes提供的测试数据,打补丁并且调整参数以后,GC的频率下降到只有原来的1/10还不到。降低GC频率尽管并不能够提高单个请求的执行速度,但是可 以增加整体应用的负载能力。

我们在JavaEye的服务器上也使用了GC补丁,并且根据推荐参数进行了调整。在使用GC补丁之后,Web服务器的CPU负载下降了大概15% 左右,效果非常显著。当然开大内存堆的代价就是ruby进程会多消耗内存,在我们的服务器上,ruby打补丁之后多消耗了50%左右的物理内存。


二、缓存的使用

1、对象缓存
JavaEye上面关于对象缓存的讨论很多,我们也提供了JavaEye这方面很多数据,因此不展开了。RoR可以使用两个对象缓存,一个是 CachedModel,类似Hibernate,比较简单,对Model的CRUD操作自动进行缓存;另外一个是cache_fu,需要自己编码来添加 对象缓存,但提供了更多高级机制,目前我们使用的是cache_fu。在使用对象缓存的情况下,应该把查询方法的:include去掉,避免关联查询无法 利用缓存的现象。

2、查询缓存
对于统计类耗时查询,如果不要求实时性,那么可以使用memcache-client将查询结果缓存到memcached里面,例如博客排行榜之类。

3、页面局部缓存
对象缓存和查询缓存都是降低数据库访问负载的,但如果RoR的负载很高,那么只能依靠页面局部缓存了。传统的互联网web1.0网站很流行采用动 态页面静态化技术来提高网站的负载,但是对于web2.0网站来说,每个页面都带有登陆用户的个人信息,页面的很多部分需要实时更新,例如投票,点击统 计,digg,显示用户在线状态等等,动态页面静态化非常困难。当然如果你非要采用动态页面静态化,技术上也不是实现不了,可以通过AJAX请求来处理静 态页面的动态部分,但是这种解决方案的开发成本过高,而且性能未必会有明显的改善,大家看看新浪和搜狐博客就知道这种技术被应用的有多糟糕了。

web2.0网站比较常用使用页面局部缓存,一种情况是页面不需要实时更新的,那么只需要设置一个合理的过期时间就行了,这种情况我们目前使用的 比较多;另外一种情况是虽然不需要实时更新,但是会在用户执行某些操作后需要缓存过期,比方说博客个人主页的很多页面,这种情况下缓存过期策略会比较复 杂,考虑到合理的开发成本,我们尚未对这样的页面使用局部缓存。

此外,Rails的页面局部缓存有一个缺点,就是和页面查询结果对应的Action当中的查询语句要放在View里面,否则每次action里面的查询还是会被执行,但是这样做会破坏程序代码良好的MVC结构。这种情况下,也可以采用另外一个Cache插件: better rails caching,在缓存页面的同时可以缓存Action当中的查询语句。


三、应用代码的优化

Stefans Kaes的文档里面对应用代码的优化进行了非常详细的介绍,因此我这里只提两个比较重要的注意事项:

1、link_to
Rails的link_to是非常慢的,它的代码实现过于复杂,特别是Rails1.2引入了REST以后,大量的命名路由被使用,这些命名路由 还需要通过一次method_missing,那就更加缓慢了。因此对于被频繁使用的内部URL地址,一定要自己用字符串拼接方式改写,可以很明显提高 View的render性能。此外类似的helper还有很多,例如button_tag,image_tag啥啥的,如非必要,尽量不用他的 helper

2、正则表达式
ruby的正则表达式也是极慢,例如auto_fix这个helper的正则表达式就比较复杂,造成的结果就是一但大量使用auto_fix, View的render就明显变慢,类似依赖正则表达式进行字符串过滤的helper有很多,如果需要频繁大量使用,请先自行做benchmark。



Rails应用程序的内存泄漏问题和解决

内存泄漏是服务器端程序经常遇到的,有时候内存泄漏问题会让人很头疼,总体来说,Rails的内存泄漏问题比Java要少得多,这是因为Java内存泄漏最常见的三种情况在Rails当中不存在:

1、HttpSession导致的内存泄漏
Java程序员喜欢往session里面丢很多东西,最糟糕的是竟然有很多框架软件也肆无忌惮往session里面丢状态数据,但Rails的session是不放在内存里面的,所以无此烦恼。

2、数据库连接释放不彻底
Java的数据库连接池释放不彻底,以及查询游标释放不彻底,都必然导致内存泄漏。Rails没有数据库连接池,而是每个进程持有一个长连接,因此不存在这个问题,而且由于持有长连接,也不存在Java里面的OpenSessionInView的烦恼。

3、用静态变量持有全局共享数据
Java程序员很喜欢通过静态全局变量来持有共享数据,但共享数据忘记清理的话,也很容易导致内存泄漏,Ruby是SNA架构,多进程服务器模式,进程间无法共享数据,反而避免了全局共享数据带来的麻烦。

但是Rails应用有一种情况:在Ruby代码中调用C写的第三方ruby类库的时候,很容易导致内存泄漏,但这种内存泄漏反而在Java中极其 罕见。Ruby本身有GC来管理内存堆,但是代码一旦调用C写的第三方ruby类库,内存堆的分配权就掌握在第三方C库的实现上面了,如果这个C库的代码 质量不够好,内存泄漏就不可避免。由于ruby本身性能很差,因此计算量大的功能往往依赖底层的C库来实现,这下内存泄漏的潘多拉魔盒就打开了!而 Java性能比较好,功能都是纯Java编写,基本上看不到需要依赖第三方C库的情况,因此比较安全。

JavaEye也面临着内存泄漏的困扰,这方面困扰主要来自于Rmagic。Rmagick调用ImageMagick的C库来完成图片的操作, 从我们的监测来看,RMagick大多数情况下会缓慢的泄漏内存,在某些特定的图片操作上会急剧的泄漏内存。解决办法就是用mini_magick替代 Rmagick,mini_magick是直接调用ImageMagick的mogrify命令,另起一个进程来操作图片,操作完进程就结束了,绝无后 患,由于Linux的fork进程开销不大,因此也不必担心性能问题。

此外,调用第三方C库的ruby代码编写都需要高度小心,比方说JavaEye使用ferret实现全文检索,根据应用的需要调用ferret的 API来编写自己的analyzer,其中在实现token_stream方法上面使用了XXXAnalyzer.new和XXXToken.new, XXXFilter.new,结果内存急剧泄漏,经过检查发现是Analyzer对象不能被反复创建,改成创建后缓存该对象就好了,但是Filter和 Token对象却必须每次创建,此外ferret的PerAnalyzerFilter也有内存泄漏问题。由于类库是用C编写的,单纯看API文档或者看 源代码片断一般无法判断出里面的内存泄漏陷阱的。


当遇到了难以解决和定位的内存泄漏问题,Ruby也有类似Java的内存Profiler工具:

1、Memory Profiler
一个纯ruby编写的内存探测器,原理很简单,就是用ruby的对象引用计数器ObjectSpace.each_object去遍历内存堆中的 每个ruby对象,进行统计和分析。用起来很简单,非常适合于开发环境下侦测内存泄漏问题,但不能用在生产环境下,极度影响Rails性能。

2、Bleak_house
Bleak_house给Ruby解析器打了补丁,插入相关的指令,可以从底层探测整个ruby内存堆中对象的情况,然后你可以定期dump出来 完整的内存堆里面的所有对象,再用bleak工具去分析dump文件,他比上面的工具分析的信息要全面,可以在测试环境和预发布环境下使用,但在生产环境 下,也会对应用的性能产生很大的影响,要慎用。

JavaEye网站在RoR性能方面的经验就全部分享给大家了,也希望做RoR的朋友都拿出来自己的经验和大家分享,共同学习和促进RoR的应用和普及。