项目大了,出现的沟通中的无限效率底下。前一段在开发了个群组的后台管理系统,就是管理群组,管理里面发布的内容。但是任务分工的时候是按层划分的,之前没有这样开发过,笔者自己做的,就是像对方去描述我需要的接口,就很纠结,涉及到数组呀,键值的就很头疼,最开始开发的时候又没有数据,本来要是自己去封装m层就好了。大不了我的一些业务逻辑可以放到m层去做。d层就只放sql了。但是当合作的时候,合作的先把m写好了。对方感觉你要这样的数据就提过这样的数据就好了。但是痛苦的我要去符合接口的数据格式,我去痛苦的组合数组,以完成那接口需要的参数,代码开放完肯定存在不少的问题,问题出现的时候你没有可以肯定的代码。就只有一点点的跟踪,改别人写的代码。有时候参数先后的顺序可能错了,因为不一样的习惯。或者有代码的或者的逻辑的错误。调试起来就更加纠结。这个情况后来自己仔细想了想,把远程接口和本地接口分开是一个好方法,个人就自己写自己的本地接口,反正比较快,远程的放到一个文件,可以单独一个人开发,也方便有伪代码测试。在都不是特别的大牛,还是分模块开发,不要分层开发
还有一个不得不提的问题,破开发机环境不会报错。怎么调试环境都不成,然后很多警告错误看不到。要是致命错误,页面白板 。然后写代码的时候只有靠经验去判断某个地方可能出警告。但是数据正常的时候是没有的,也不是很多开发者都会注意到的问题,然后移到测试机就是一堆的警告。
还有环境真tm的复杂,后台前台不是相同的服务器,后台的机器就存储前台推送过来的ID,然后后来更加简单的条件去查出来这些ID,然后根据ID去用远程接口去取数据。前台推送用的是mcq,我们后台机器要启动队列,去写库。这样就造成以换环境就很多的脏数据,测试人员不明白。就去纠结某个ID的记录更改不了。那是因为通过ID到前台取得数据,但是更改的状态在后台是更新的,然后库中没有那条记录,造成更改失效。说到远程接口,很多时候也不是很可靠。就造成我们去痛苦的跟踪。还有很对deal文件,cront,想测试一个东西好了没,得跑到好几台机器去看,排除。继续纠结当中,
有好长时间没有来这了,去想想前段做的东西。
==运行时配置==
scws.default.charset (default = gbk) , Changeable = PHP_INI_ALL
scws.default.fpath (default = NULL) , Changeable = PHP_INI_ALL
有关 PHP_INI_* 常量进一步的细节与定义参见PHP手册。
==资源类型==
本扩展定义了一种资源类型:一个 scws 指针,指向正在被操作的 scws 对象。
==预定义常量==
本扩展模块未定义任何常量。
==预定义类==
这是一个类似 Directory 的内置式伪类操作,类方法建立请使用 scws_new() 函数,
而不要直接用 new SimpledCWS。否则不会包含有 handle 指针,将无法正确操作。
包含的方法有:
class SimpledCWS {
resource handle;
bool close(void);
bool set_charset(string charset)
bool set_dict(string dict_path)
bool set_rule(string rule_path)
bool set_ignore(bool yes)
bool set_multi(int mode)
bool set_duality(bool yes)
bool send_text(string text)
mixed get_result(void)
mixed get_tops([int limit [, string xattr]])
bool has_word(string xattr)
mixed get_words(string xattr)
string version(void)
};
例子1. 使用类方法分词
<?php
$so = scws_new();
$so->set_charset('gbk');
// 这里没有调用 set_dict 和 set_rule 系统会自动试调用 ini 中指定路径下的词典和规则文件
$so->send_text("我是一个中国人,我会C++语言,我也有很多T恤衣服");
while ($tmp = $so->get_result())
{
print_r($tmp);
}
$so->close();
?>
例子2. 使用函数提取高频词
<?php
$sh = scws_open();
scws_set_charset($sh, 'gbk');
scws_set_dict($sh, '/path/to/dict.xdb');
scws_set_rule($sh, '/path/to/rules.ini');
$text = "我是一个中国人,我会C++语言,我也有很多T恤衣服";
scws_send_text($sh, $text);
$top = scws_get_tops($sh, 5);
print_r($top);
?>
注意:
为方便使用,当 send_text 方法或 scws_send_text 函数被调用前,没有加载词典和规则集时,
系统会自动在scws.default.fpath(ini配置)中查找相应的字符集词典。词典和规则文件的命名
方式为 dict[.字符集].xdb 和 rules[.字符集].ini ,当字符集是 gbk 时中括号里面的部分则
不需要,直接使用 dict.xdb 和 rules.ini 而不是 dict.gbk.xdb 。
此外,输入的文字,词典,规则文件这三者的字符集必须统一,如果不是默认的 gbk 字符集
请调用 set_charset 或 scws_set_charset来设定,否则可能出现意外错误。
==函数列表:==
mixed scws_new(void)
说明:创建并返回一个 SimpledCWS 类操作对象。
参数:无
返回值:成功返回类操作句柄,失败返回 false
mixed scws_open(void)
说明:创建并返回一个分词操作句柄
参数:无
返回值:成功返回 scws 操作句柄,失败返回 false
bool scws_close(resource scws_handle)
说明:关闭一个已打开的 scws 分词操作句柄
返回:始终为 true
参数:scws_handle 即之前由 scws_open 打开返回的。
bool scws_set_charset(resource scws_handle, string charset)
说明:设定分词词典、规则集、欲分文本字符串的字符集,系统缺省是 gbk 字集。
返回:始终为 true
参数:scws_handle 即之前由 scws_open 打开返回的;
charset 是要新设定的字符集,目前只支持 utf8 和 gbk。
bool scws_add_dict(resource scws_handle, string dict_path [, int mode])
说明:添加分词所用的词典,新加入的优先查找。
返回:成功返回 true 失败返回 false
参数:scws_handle 即之前由 scws_open 打开返回的;
dict_path 是词典的路径,可以是相对路径或完全路径。(遵查安全模式下的open_basedir)
mode 是可选参数,表示加载的方式,其值有三:SCWS_XDICT_XDB SCWS_XDICT_MEM SCWS_XDICT_TXT
其中 SCWS_XDICT_TXT 表示文本词典,可结合另外2个值使用
bool scws_set_dict(resource scws_handle, string dict_path [, int mode])
说明:设定分词所用的词典并清除已存在的词典列表。
返回:成功返回 true 失败返回 false
参数:scws_handle 即之前由 scws_open 打开返回的;
dict_path 是词典的路径,可以是相对路径或完全路径。(遵查安全模式下的open_basedir)
mode 是可选参数,表示加载的方式,其值有三:SCWS_XDICT_XDB SCWS_XDICT_MEM SCWS_XDICT_TXT
其中 SCWS_XDICT_TXT 表示文本词典,可结合另外2个值使用
bool scws_set_rule(resource scws_handle, string rule_path)
说明:设定分词所用的新词识别规则集(用于人名、地名、数字时间年代等识别)。
返回:成功返回 true 失败返回 false
参数:scws_handle 即之前由 scws_open 打开返回的;
rule_path 是规则集的路径,可以是相对路径或完全路径。(遵查安全模式下的open_basedir)
bool scws_set_ignore(resource scws_handle, bool yes)
说明:设定分词返回结果时是否去除一些特殊的标点符号之类。
返回:始终为 true
参数:scws_handle 即之前由 scws_open 打开返回的;
yes 设定值,如果为 true 则结果中不返回标点符号,如果为 false 则会返回,缺省为 false。
bool scws_set_multi(resource scws_handle, int mode)
说明:设定分词返回结果时是否复式分割,如“中国人”返回“中国+人+中国人”三个词。
返回:始终为 true
参数:scws_handle 即之前由 scws_open 打开返回的;
mode 设定值,1~15。1|2|4|8 = short|duality|zmain|zall
按位与的 1 | 2 | 4 | 8 分别表示: 短词 | 二元 | 主要单字 | 所有单字
1.1.1 起 1,2,4,8 分别对应常量 SCWS_MULTI_SHORT SCWS_MULTI_DUALITY SCWS_MULTI_ZMAIN SCWS_MULTI_ZALL
bool scws_set_duality(resource scws_handle, bool yes)
说明:设定是否将闲散文字自动以二字分词法聚合
返回:始终为 true
参数:scws_handle 即之前由 scws_open 打开返回的;
yes 设定值,如果为 true 则结果中多个单字会自动按二分法聚分,如果为 false 则不处理,缺省为 false。
bool scws_send_text(resource scws_handle, string text)
说明:发送设定分词所要切割的文本
返回:成功返回 true 失败返回 false
参数:scws_handle 即之前由 scws_open 打开返回的;
text 是文本的内容。
注1:系统底层处理方式为对该文本增加一个引用,故不论多长的文本并不会造成内存浪费;
注2:执行本函数时,若未加载任何词典和规则集,则会自动试图在ini指定的缺省目录下查找词典和规则集。
mixed scws_get_result(resource scws_handle)
说明:根据 send_text 设定的文本内容,返回一系列切好的词汇。
返回:成功返回切好的词汇组成的数组, 若无更多词汇,返回 false。
参数:scws_handle 即之前由 scws_open 打开返回的。
注1:每次切割后本函数应该循环调用,直到返回 false 为止,因为程序每次返回的词数是不确定的。
注2:返回的词汇包含的键值有:word (string, 词本身) idf (folat, 逆文本词频)
off (long, 在文本中的位置) attr(string, 词性表示)
mixed scws_get_tops(resource scws_handle [, int limit [, string attr]] )
说明:根据 send_text 设定的文本内容,返回系统计算出来的最关键词汇列表。
返回:成功返回切好的词汇组成的数组, 若无更多词汇,返回 false。
参数:scws_handle 即之前由 scws_open 打开返回的;
limit 可选参数,返回的词的最大数量,缺省是 10;
attr 可选参数,是一系列词性组成的字符串,各词性之间以半角的逗号隔开,
这表示返回的词性必须在列表中,如果以~开头,则表示取反,词性必须不在列表中,
缺省为NULL,返回全部词性,不过滤。
mixed scws_get_words(resource scws_handle, string attr )
说明:根据 send_text 设定的文本内容,返回系统中词性符合要求的关键词汇。
返回:成功返回符合要求词汇组成的数组,返回 false。
参数:scws_handle 即之前由 scws_open 打开返回的;
attr 是一系列词性组成的字符串,各词性之间以半角的逗号隔开,
这表示返回的词性必须在列表中,如果以~开头,则表示取反,词性必须不在列表中,
若为空则返回全部词性,不过滤。
bool scws_has_words(resource scws_handle, string attr )
说明:根据 send_text 设定的文本内容,返回系统中是否包括符合词性要求的关键词。
返回:如果有则返回 true,没有就返回 false。
参数:scws_handle 即之前由 scws_open 打开返回的;
attr 是一系列词性组成的字符串,各词性之间以半角的逗号隔开,
这表示要判断的词只要有一个符合词性要求就立即返回 true,
若为空则只要 text 不为空就返回 true.
mixed scws_version(void)
说明:返回 scws 版本号名称信息。
返回:字符串
参数:无
类对象用法参照函数用法,区别就是不需要传入第一参数(会自动从handle的属性中取值)。Abstract Factory
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
抽象工厂
提供一系列相关或者是相互依赖的对象接口,而无需指定它们的具体类。
Adapter
Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn 't otherwise because of incompatible interfaces.
适配器
将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能在一起工作的那些类可以一起工作。
Bridge
Decouple an abstraction from its implementation so that the two can vary independently.
桥接
将抽象部分与实现部分相分离使得它们可以相互独立地变化。
Builder
Separate the construction of a complex object from its representation so that the same construction process can create different representations.
生成器
将一个复杂对象的构造与它们的表示分离,使得同样的构造过程可以创建不同的表示。
Chain of Responsibility
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
职责链
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的偶合关系。将这些对象连一个链,并沿着这条链传递该请求,直到有一个对象处理它为止。
Command
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
命令
将一个请求封装成为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或者是记录请求日志,以及支持可撤销的操作。
Composite
Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
组合
将对象组合成树型结构以表示“部分-整体”的层次结构。组合模式使得客户对单个对象和组合对象的使用具有一致性。
Decorator
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
装饰
动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更加灵活。
Façade
Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.
外观
为子系统中的一组接口提供了一个一致的界面。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
Factory Method
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
工厂方法
定义用来创建对象的接口,让子类来决定实例化哪一个类。工厂方法使得一个类的实例化延迟到其子类。
Flyweight
Use sharing to support large numbers of fine-grained objects efficiently.
享元
运用共享技术有效地支持大量细粒度的对象。
Interpreter
Given a language, define a represention for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
解释器
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的语句。
Iterator
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
迭代器
提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示。
Mediator
Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.
中介者
用一个中介对象来封装一系列对象的交互。中介者使各对象不需要显示的相互引用,从而使其偶合松弛,而且可以对立的改变它们之间的交互。
Memento
Without violating encapsulation, capture and externalize an object 's internal state so that the object can be restored to this state later.
备忘录
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
Observer
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
观察者
定义对象一对多的描述关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
Prototype
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
原型
用原型实例来指定创建对象的种类,并且通过这些原型创建新的对象。
Proxy
Provide a surrogate or placeholder for another object to control access to it.
代理
为其它对象提供一个代理以控制这个对象的访问。
Singleton
Ensure a class only has one instance, and provide a global point of access to it.
单件
保证一个类只能有一个实例,并且提供一个访问它的全局访问点。
State
Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
状态
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
Strategy
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
策略
定义一系列的算法,把它们一个个封装,并且使它们可以相互替换。策略模式使得算法可独立于使用它的客户而变化。
Template Method
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm 's structure.
模板方法
定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变算法的结构即可重定义该算法的某些特定步骤。
Visitor
Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
访问者
表示一个作用于某个对象结构中的各个元素的操作。访问者使得你可以在不改变各元素的类的前提下定义作用于这些类的操作。
用yum更新PHP,只需用一条命令就可以搞定:
#yum update php经过一番询问,才知道原来CentOS系统的源里PHP仍旧是5.2.1,需要额外的源才能升级PHP。
根据外国网友的介绍,冰古添加了额外的源:
登录SSH后依次运行下列命令:
#rpm –import http://www.jasonlitka.com/media/RPM-GPG-KEY-jlitka
#vi /etc/yum.repos.d/utterramblings.repo #文中这里是使用nano,但VPS不能启动nano,用vi代替也是可以的[utterramblings]
name=Jason’s Utter Ramblings Repo
baseurl=http://www.jasonlitka.com/media/EL$releasever/$basearch/
enabled=1
gpgcheck=1
gpgkey=http://www.jasonlitka.com/media/RPM-GPG-KEY-jlitka再次运行下面的命令就可以完成php的升级了
#yum update php#yum update mysqlRoR的摆设方 案可谓八门五花,有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的10%,并且资本耗损更小。
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便利 啦。
From:http://www.apolov.com/html/2009/06/09/1244533635.html