ExtMail中sort2name问题所在之根本分析(重要)
Hi all:这个问题搞的开发团队非常郁闷,百思不得其解,最后在今天一个不经意之间发现了问题所在,真有点捶足顿胸,无言以对的感觉。sort2name问题整整折腾了团队近1年,随着用户扩大,CGI模式大并发效能不足,FCGI高性能模式需求渐起,sort2name问题开始冒出来了。下面就和大家一起分析sort2name的现象,如何debug,以及产生原因。
[b][size=4]什么是sort2name问题?[/size][/b]
该问题只存在于FCGI模式下,remote方式尤其容易出现,主要现象是点收件箱会出现类似下面的报错:
[quote]
Undefined subroutine &Ext::Utils::sort2name called at /var/www/extsuite/extmail/libs/Ext/App/Folders.pm line 387
[/quote]
这个问题出现后让人非常难以接受,开发团队绞尽脑汁,但各种方法都没有效果,后来只能“怪罪”FCGI模块,也因为毫无办法,上2周才开始发力开发ExtMail CGI server,企图设计一个不用FCGI模块又获得一样效能的架构。结果是实现了,但是,这个噩梦还是没有消除,就在大半小时前我还是遇到了一样的错误,当时真的心都凉了,感觉cgid算是白设计了。:(
[b][size=4]产生原因?[/size][/b]
sort2name报错的原因实在很难想,开始以为是eval()方式会破坏Perl的继承,Exporter导出功能,后来又怀疑是调用sort2name有问题,改为直接Ext::Utils::sort2name()调用,再后来又怀疑是FCGI(一个c/xs写的perl模块),以为是不是因为混合了c模块污染了名字空间?一时间怪异的想法乱飞,简直不着边际。
最后查出来最可能的原因是持续环境下,Webmail和后台的同名perl模块Utils.pm内容不一致导致的。后台的Utils.pm里没有sort2name和name2sort函数的定义,在Perl持续环境下,@INC中第一个能找到Ext::Utils的路径如果不是extmail/libs而是extman/libs,那么对sort2name的调用就因为找不到对应的定义而报上面的错。看看以下@INC数组的调试输出:
[quote]
/var/www/extsuite/extman/libs
/var/www/extsuite/extman/libs
/var/www/extsuite/extman/libs
/var/www/extsuite/extman/libs
/var/www/extsuite/extman/libs
./lib
/var/www/extsuite/extmail/libs
/usr/lib64/perl5/site_perl/5.8.8/x86_64-linux-thread-multi
/usr/lib64/perl5/site_perl/5.8.7/x86_64-linux-thread-multi
/usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi
/usr/lib64/perl5/site_perl/5.8.5/x86_64-linux-thread-multi
/usr/lib/perl5/site_perl/5.8.8
/usr/lib/perl5/site_perl/5.8.7
/usr/lib/perl5/site_perl/5.8.6
/usr/lib/perl5/site_perl/5.8.5
/usr/lib/perl5/site_perl
/usr/lib64/perl5/vendor_perl/5.8.8/x86_64-linux-thread-multi
/usr/lib64/perl5/vendor_perl/5.8.7/x86_64-linux-thread-multi
/usr/lib64/perl5/vendor_perl/5.8.6/x86_64-linux-thread-multi
/usr/lib64/perl5/vendor_perl/5.8.5/x86_64-linux-thread-multi
/usr/lib/perl5/vendor_perl/5.8.8
/usr/lib/perl5/vendor_perl/5.8.7
/usr/lib/perl5/vendor_perl/5.8.6
/usr/lib/perl5/vendor_perl/5.8.5
/usr/lib/perl5/vendor_perl
[/quote]
很明显排在第一位的是extman/libs,对Ext::Utils::sort2name的调用(不管是直接函数名调用,还是连带包名的调用),都会因为perl去查找extman/libs/Ext/Utils.pm(里面没有sort2name定义)而报错!
这个问题一般出现在webmail和后台都同时配置了FCGI或cgi server,然后在某个时刻先访问了后台再访问webmail,造成@INC问题后就容易再次出现了!
[b][size=4]如何debug?[/size][/b]
修改App.pm里的error函数,将ERR => "$@"改为ERR => "$@ INC=@INC",在报错时将列出@INC搜索路径,自然就真相大白。不过目前没有做该故障的重现测试,还不能100%确定是这个问题,但这个解释已经显得非常合理。回头做测试再通报结果。
[size=4][b]如何解决?[/b][/size]
问题一旦确定是搜索路径下Utils.pm缺少sort2name的定义,那么解决就很容易了,无非有几个:
1)将extmail的Utils拷贝到extman目录下,保持两边同步
2)将Webmail和后台公共用到的模块抽出来放到一个公共目录,避免名字空间重叠的现象
3)模块改名,例如extman/libs/Ext/Utils.pm可以改为extman/libs/Ext/ManUtils.pm,避免名字空间重叠 就目前而言,为了检查出webmail和后台包名相同但内容不同的模块,避免出现sort2name的悲剧,请按以下方法检测:
cd /path/to/your/extman/libs
for i in `find . -type f`;do if [ -r "../../extmail/libs/$i" ]; then echo "$i"; fi;done
结果会显示一堆perl 模块名字出来,表明在2个软件里这些软件包同名。但有个别软件包,例如Template.pm是两边不能一致的,因此周一发布一个FCGI/CGI server下解决sort2name这类问题的完整办法(包括补丁)
btw:经过统计,有以下软件包是同名但内容有差异的
[quote]
Encode/IMAPUTF7.pm
Ext/CGI.pm
Ext/Unicode.pm
Ext/Utils.pm
Ext/RFC822.pm
Ext/Template.pm
Ext/MIME.pm
[/quote]
其中Utils.pm是本次罪魁祸首,其他几个包里不同程度都有一些小小隐患,只是平常使用不会遇到问题,只有极特殊情况才会爆发。:'(
其实之前做NGINX+PHP的时候就遇到过这种问题……
不过真的没想到和这个有关系……当时我的解法是用8001 8002 2个端口把他们隔离开来……[[i] 本帖最后由 smallthing 于 2009-12-26 01:50 编辑 [/i]] 顶3楼,没想php也有类似问题,哈哈。我们之所以不用不同port分开,是从节约资源和降低维护成本的角度考虑。
早上7:56分已经摸索到一个简单的办法来针对每个不同请求(包括同个文件名)设置不同@INC的方法,然后再实验下同个包名的情况。:lol :lol 问题知道根源后,就容易想解决办法了 根据请求不同设置不同@INC这个容易实现,但郁闷的是一旦包Load进内存里后,如果下次请求@INC变了,需要访问另一个内容不同但名字相同的包名,似乎是不可能的。这也就解释了sort2name 诡秘异常,时有时无的原因。看来原始程序中问题有2:
1)不同请求下设置@INC会影响其他请求的@INC,导致路径混乱
2)同名包Loaded后就无法Load另一路径下同名包了,导致所需非所供
Waiting for fix ... :Q 好文章呀.辛苦强哥了:lol Believe hzqbbc to be immortalized :lol :funk: :funk: :funk: 英文版信春哥啊?晕啊。 [quote]原帖由 [i]hzqbbc[/i] 于 2009-12-26 07:27 发表 [url=http://www.extmail.org/forum/redirect.php?goto=findpost&pid=71404&ptid=12859][img]http://www.extmail.org/forum/images/common/back.gif[/img][/url]
顶3楼,没想php也有类似问题,哈哈。我们之所以不用不同port分开,是从节约资源和降低维护成本的角度考虑。
早上7:56分已经摸索到一个简单的办法来针对每个不同请求(包括同个文件名)设置不同@INC的方法,然后再实验下同个包名 ... [/quote]
其实作为CGI SERVER的话,在服务器开2个副本 2个端口分别跑2份CGI和开1个副本把所有包用不同名字载入后 占用应该是差不多的吧 毕竟后台用的少得多得多 可以少开很多进程数
顺便提一下
PHP的问题是在FASTCGI之下,假设有2个虚拟主机 如果其中一个站点在他的.htaccess下设置了php的base_dir之后启动,下次这个PHP进程会发生无法访问另一个站点新请求的base_dir路径之外的现象,包括Php_fpm也没有解决这个问题,我认为要完全解决这个问题,启动FASTCGI模块时不能把base_dir等参数导入到cgi中 而应该让php去实时读取这个信息 也可以说PHP-CGI设计的时候并没有考虑到作为fastcgi来跑
Php5.3+正在解决这个问题
Note: As of PHP 5.3.0 open_basedir can be tightened at run-time. This means that if open_basedir is set to /www/ in php.ini a script can tighten the configuration to /www/tmp/ at run-time with ini_set()
Antony Dovgal announces PHP-FPM has been put into a SVN branch in PHP core. This is an exciting development. It will be a while before it hits production status, but this is a great move for the future.
[[i] 本帖最后由 smallthing 于 2009-12-26 19:08 编辑 [/i]] 和和,开2个副本的话麻烦,而且还有一点,如果以后还有第三,第四个应用呢?现在通过了之前提1-3方法中的1)先暂时解决掉,后期会采用2),看来php-fcgi模式的问题也是和perl的一样。
只是perl比php差的一个地方是它本身并没对这种情况做优化,进程不释放的话,模块的载入就不会释放,比较郁闷。 程序设计正是很麻烦 还好我的只是前台用了FCGI,后台没用 强人,辛苦了
页:
[1]