Main

January 21, 2014

Dell服务器和Linux kernel bug造成的CPU性能下降问题

好久没做这种类似技术顾问的工作了。今天处理了一个挺奇怪的问题,我也是第一次碰到这种事情。最后总算比较周折的找到了原因,这个bug比较新,估计碰上问题的人应该不会少,所以我就顺手记在这里一份。

问题是出现在一个朋友的服务器上,这是一台E5-2430的Dell服务器,他给我的描述是"有一台机器非常慢,但负载很低。"按照以往经验,这种问题是出现在程序锁或者IO Block上,一般是某些重要资源的争用导致的。但检查之后这些都没问题。

这种现象我从来没碰上过,直觉上感受就是cpu本身速度很慢,于是,用计算圆周率的办法来测试cpu性能:

time echo "scale=5000; 4*a(1)" | bc -l -q

过了4,5分钟都没算出来结果。基本可以定位问题在CPU上。

查syslog,发现大量的 CPU0: Package power limit notification 这种通知,怀疑是和电源管理有关。

顺着这个终于找到了问题。这是一个目前只出现在Dell服务器上的LInux内核bug,大致是内核在CPU功耗和温度控制上有bug,(确切的说应该是Dell的BIOS造成的,但最终表现出来是内核的bug)没法拿到正确的值,由此导致CPU持续被降频。

最早的报告应该来自Redhat,并且在1个多月之前redhat修复了它

如果是Redhat的服务器,打补丁即可,如果不是Redhat,这里有几种其他解决方案

这件事让我明白了为什么一些做运维的人非要坚持用Redhat,我一直认为Redhat在各方面都落后了。但出现这种问题的时候还是这种商业公司响应比较及时。


多谢 @googollee@yarshure@holly@qianhw 的建议和讨论。

March 23, 2011

关于ruby/ror我的体验和看法

这几天在twitter上说到不少ruby到底好不好的事情,总结一下,写了这个blog。

今年年初开始我打算深入体验一下ruby on rails,对我来说,已经很多年没有接触"新语言"了。在这个深入体验开始之前的几年,也有若干次浅尝辄止,基本都是玩了几下,觉得要学的东西真多,用PHP顺手写一个也很简单嘛,就放弃了。

但我想一个东西迅速风靡世界一定是有原因的,就算我最终不去真正使用它,弄清楚背后的原因也是值得的。于是我就放下了老程序员的心态,像一个初学者一样从最基本的文档看起,尝试各种大家称赞的项目,搞不懂的地方就去问熟悉ruby的人,不仅问如何实现,还要问"为什么会这样","过去什么样","其他语言解决某个问题的方法在这里如何对应"。几个月下来,虽然我还没能用ror真正开发一个完全独立的项目,但总算是有了一个全貌的概括性了解,也知道了历史和文化,体会了社区的氛围。这个时候,我想大概是可以对rails发表一些评论了。

在这一次尝试之前,我最担心的事情是"性能问题",现在看来这是一个伪问题,首先ruby的性能并没有这么差(前几年确实不太好,不过现在已经很好了),其次,用来做一个网站应用,无论是什么语言,最后都要依靠缓存和分布来解决,也就是说最终都要依赖于正确的系统架构,而不是语言本身的性能,最后,就算是性能真的差又完全不可解决的问题,完全可以用效率更高的语言来写,把处理的结果通过各种通讯方式交给ruby就是了。在计算机硬件发展到今天这样,性能问题大多不存在,只有架构师水平的差距。我见过很多用了几十台机器的系统但抗不住几十万的负载,这能怪语言和平台本身的性能差吗?

在所有关于ruby优点中,我唯一不能认同的是"学习曲线平缓",事实上,ruby的学习难度比较高,确实远远高于PHP,要说平缓,大概是相对于J2EE的平缓。我相信这是很多对ruby误解和攻击的来源,从表面上看起来,要学会ruby尤其是ror,至少需要理解设计模式,知道服务器系统的运作方式,甚至需要熟悉操作系统,要用好ror,更需要接触各种项目和各种标准,这些都需要花去很多学习时间。同时配合的还有各种测试工具,部署工具,虽然不是必须,但最终还是要学的(这里我还欠缺很多呢)。我曾经开玩笑说ruby社区的人破坏了英语的纯粹性,因为他们fork新项目太容易,大家都喜欢创造各种开源项目,直接符合功能的名字都被用光了,于是只好用各种奇怪的相关的英语单词,以至于搜索很多英文单词竟然会找出来ruby的项目。这些东西直接塞给一个初学者确实有很大的压力。ruby on rails的Getting start又太停留在表面,勉强算是尝鲜,距离真正实用差的太远。相对来说,PHP就简单多了,页面直观对应在PHP文件上,什么都不用懂也能写(当然写好也很不容易),这造成的结果就是谁都可以懂PHP,但不是谁都能懂ruby。人们为了掩盖学习的痛苦,就容易去攻击对方无用。在我这次下决心深入学习之前,我也觉得PHP最好,记得之前某次尝试的时候 @rainux 跟我说,刚刚上手是很难感觉到ror的好处,需要持续用一段时间才会慢慢发现。

ror在架构和开发流程管理也坚定的支持了很多理论,比如MVC,比如敏捷,比如TDD...这些同样增加了学习成本,当然也增加了被攻击的接口,比如:TDD太扯淡了,所以ror也扯淡。或者:我最讨厌敏捷了,所以也讨厌ror。实际上,敏捷和TDD本身都没错,他们都有成熟的方法论和实践,有足够好的工具配合,能解决很多问题。如果不认同这个,只能说是开发和项目管理经验太少,没碰上过复杂问题。有错的是对敏捷和TDD的错误鼓吹,两者不能混在一起。

另外一个常见说法是寻找ruby的大流量项目案例。但其实寻找ruby开发的"top项目"没意义。DHH和37s创造的都不是适合所有人的产品,他们只愿意在某一领域做到最好。不是所有人都有兴趣创造亿级别产品,何况也没几个人真能做成。对大部分项目,千万是一个很好的级别,那是ror最佳实践领域,而在现实中,大部分项目能做到100万pv,已经算了不起了,想亿级别pv的技术方案未免过早了。

在组织技术团队的问题上,流行语言的难度是差不多的,10个人的团队任何语言都很容易组织起来,但换个角度看,任何语言也很不容易组织10个人的团队,只不过难的方法不一样。A语言可能是找人难,一周就收到1份简历,B语言可能是鉴别难,一天收到200份简历,看都看不过来。其实最难的事情是组织一个10个人的靠谱团队,这个团队用什么语言相对于寻找10个靠谱的人的难度可以忽略不计。

选择一个语言,并不是选择语言本身,同时也是选择社区。ruby社区的活力,友好,规整,统一的审美,给我留下了非常深刻的印象。DHH和37s对这个社区影响巨大,我也相信他们的理念,小公司未必不好,小公司未必不伟大,追求pv不如追求极致的产品。我们创造一个产品,是为了解决一类人的问题,并不是为了超越谁或和谁竞争。

15年前,PHP 3.0之前的版本完全不可用(当然,那时候PHP的简称还叫做Personal Home Page),那时候用c写cgi/isapi或者用perl是网站最常见的开发形式。那时候说PHP是下一代语言,很多程序员都会不同意,他们会挑出来PHP一万个不如c或者不如perl的地方。这些都没错,但世界是会变化的。PHP今天确实成了一种应用广泛,老幼皆宜的语言。我并不认为未来ruby会取代PHP,但至少,我认为下一代高质量的互联网产品出现在ruby社区的机会更大,这就像当年PHP vs perl一样,乃大势所趋。

最后我说一句可能不太好听的话,如果你没有下决心转型成为一个写信教主那样的专门忽悠的人,如果你还觉得技术有点意思,还想自己创造点产品,那么应该保持足够的技术敏感力,认真的学习一些新东西,而不是给自己寻找各种借口。就像这张漫画一样。要多问问自己,是这东西真的不好,还是自己学不进去了?当然,承认后者是很难的。一年前我也确实没学进去。

我学ruby的过程中,很多朋友提供过不少帮助,没有他们的回答和blog,估计会学的更难。在此一并感谢: @rainux @robinlu @moming2k @Anxonli @soulhacker

July 17, 2010

如何使用Core Text计算一段文本绘制在屏幕上之后的高度

Core Text提供了一系列方便的函数,可以很容易的把文本绘制在屏幕上,对于一个Frame来说,一般并不需要担心文本的排列问题,这些Core Text的函数都可以直接搞定,只要给他一个大小合适的CGRect就可以。

但,在某些情况下,我们还希望知道这段文本在绘制之后,对应绘制的字体字号设置,在屏幕上实际占用了多大面积。举例来说,有文本段落a,屏幕大小rect,通常做法是以rect创建path,然后创建CTFramesetter,再然后创建CTFrame,最后用CTFrameDraw画出来,这时候,往往文本段落占用的实际面积会小于rect,这时候就有必要获得这段文本所占用的真正面积。

最理想的情况是使用

double CTLineGetTypographicBounds( CTLineRef line, CGFloat* ascent, CGFloat* descent, CGFloat* leading );

这是Core Text提供的函数,传入CTLine,就会得到这一行的ascent,descent和leading,在OSX上通常可以工作的很好,但是在iOS(iPhone/iPad)上这个函数的结果略有不同。

正常情况下,计算行高只需要ascent+descent+leading即可。在这个略有不同的情况下,leading的值会出现偏差,导致算出来的结果是错误的。如果不管行距,ascent+descent计算出来的Glyph的高度还是正确的。

这样就有了第一步

在创建用于绘图的CFAttributedStringRef时,除了设置字体,多设置一个CTParagraphStyleRef,其中特别应该确定行距kCTParagraphStyleSpecifierLineSpacing。在计算这里时,先逐行计算ascent+descent,累加起来,再加上一个行数*之前设置好的行距,这样算出来的就是这些文本的实际高度,CTLineGetTypographicBounds返回的结果是宽度,这样就可得到文本实际填充面积的Rect了。

但是这还是有问题,因为OSX上和iOS处理不同,所以事实上iOS的模拟器和真机的最终效果是不一样的,这样调试程序很麻烦。
于是还需要第二步

在最终往页面上绘制的时候,不再用CTFrameDraw来一次绘制全部,而是使用CTLineDraw逐行画,在画之前,先用CGContextSetTextPosition来设置好每行文本的位置。这样就保证了在任何平台上绘制效果一致。

问题就解决了。

CoreText在OS X和iOS上实现有很多细节区别,比如说,对CTRun的划分方式也不一样,在iOS上划分出来的Run数量比OSX上少很多,是按照字体划分的Run,按照文档出来,这是正确的,但OSX上是一个字一个Run这样划分的,和文档不符。iOS上的处理显然效率更好。

stackoverflow 上有一个类似的讨论,不过没考虑到iOS的区别

April 1, 2010

一次被钓鱼的实践和心得

今天我在twitter上说我的QQ一年都用不了几次,不如卖掉。 @cosbeta 说他做生意比较需要短点的QQ号,因为在twitter上有了不少了解,我就干脆送给他了。晚上回来给了他相关信息。这时候发生了好玩的事情,竟然有人为了得到这个QQ号,想通过"社会工程学"的方法钓鱼。我虽然不会上当,但是觉得具有一定欺骗性,所以分享出来,提醒大家注意。

邮件很短,截图在此

1. 图中这个邮箱注册的不错。有一定欺骗性。在此也提醒大家,这不是 @cosbeta 的邮箱。要识别是不是常用邮箱,有一个快捷的方法,就是用google搜索一下,看看以前的活动记录。如果没有活动记录,一般是刚刚注册的。

以往的活动记录,在互联网上相当于信用卡账单,这也是我一直劝大家不要太注意所谓隐私的原因之一。你需要给自己留下信用记录,以便别人查对。知道 @cosbeta 真正的邮箱的朋友也可以搜索一下,看看有多少结果。

google一下看活动记录,这个办法甚至可以应对精心搭建的带有反向解析域名相似的邮件服务器(可称为官方钓鱼)。是简单而有效的办法。

当然也有推友提到自己因为防止垃圾邮件,保护的很好,以致于没有任何搜索结果,这种情况是比较特殊的,可以通过其他方法验证,我下面也会提到。

2 注意前面的cosbeta cosbeta,他确实设置了用户名,但正常人是不会这样写名字的。肯定是姓名的形式,就算用昵称,也很少有人重复两遍。碰上这种情况,至少应该注意提高警惕了。这不正常。

3 注意判断对方书写习惯。这封邮件在这方面也略显粗糙。比如 @cosbeta 虽然在成都,但说话利索,很少在后面加"咯"这样的语气词。他的标点都是全角,一般不出现半角。这是由输入法的习惯和键盘布局决定的,也是类似指纹的标记,如果不换机器或输入法,通常不会有太大变化。

4 还有一些其他细节线索可以判断一个邮件是否是钓鱼。这方面大家可慢慢思考,不多说。而我们需要做到的原则是,不要在不能确认身份的情况下说重要的东西。

确认身份的方法包括但不限于:和对方谈一些细节问题,比如谈谈@cosbeta的具体业务,看看对方是否知道。我今天和@cosbeta第一次直接聊天的时候,也聊了不少话。这些对话都可以从细节中确认对方身份。

当然,如果能打个电话问一下,可能会更容易点。对方的声音和说话习惯,是更难伪造的。在电话中多说几句,还可以注意到对方所处环境,是否局促,是否紧张,是否反应速度慢,这些迹象都代表了有异常发生,要特别注意。

最好的方法还是双方都使用OTR签名,这是判断身份和保护信息安全的重要手段,虽然麻烦,但非常有效。当然送个QQ号这种小事就不用麻烦 @cosbeta 专门装个OTR了,我通过对话已经能确认他的身份了。具体做法很多,可以以此为基础,推导出来更多的办法。

一个基本的原则是,要至少通过两个渠道来确认。比如 在twitter上dm一下,然后在gtalk上说一下,在网上说一下,电话说一下。让不同渠道说的内容相关,这样就可以确认出两边是否是一个人。这个思路可以举一反三。


总结一下必须具备的意识: 1 理解重要信息可能被泄露,必须具有安全意识 2 保证安全是繁琐的,但很重要。所以请用多种渠道确认对方身份,这是最基本的一步。 这两点非常基本,但是如果可以深刻理解,确实可以解决很多问题。


技巧说完了,我总结一下教训: twitter上坏人还是存在的,一个QQ号都值得这样做。其他的信息想必也有各种人,出于不同的目的而感兴趣。所以,用不明来源的第三方上推的推友,请记得改密码,没准你的密码早被存下了。我非常非常非常相信,有人曾经用某个第三方或是某个api收集了大量twitter帐号的密码和信息,在某些时候会使用的。

安全意识必须常常记在心中,在关键问题上谨慎对待。注意,邮箱地址是可以伪造的,gmail难一些,但仍然有可能伪造。所以千万注意分寸,什么东西会让你丢钱,什么东西会让你送命。考虑好后果,做事就会谨慎。


有一些不让人反感的确认信息小办法,可以分享。比如我要给别人汇款,对方短信过来一个帐号,怎么确认呢?礼貌起见,可以跟对方说:麻烦您能把具体金额再给我确认一下吗?对方如果能发出来细目,一般就差不多。第一次联系的人可以当作寒暄说一些往事,等等。

一般来说,安全是非常难达到的目标,我们可以默认自己的行为是不安全的,然后通过一系列的手段来降低不安全的概率,最终达到一个比较好的平衡。本文列出的是一些简单,易于实施的办法。深刻理解这些原则,未必可以保证绝对安全,但至少可以应付大部分麻烦。

December 27, 2009

使用php,js来对内容做rsa加密


http://code.google.com/p/phpjsrsa/

这是一个用于文本加密的库,主要用于http协议下的防窃听。一般来说,如果应用https协议可以有效的避免窃听。但有几种情况必须考虑。

(1) 主机同时有https和http协议,部分用户通过https协议访问,获得了保护。但也有用户通过http访问,这部分用户会遭到窃听。除非关闭http请求,全面转向https。
(2) 主机并没有https支持。

很多情况下,我们需要保证主机安全,最好的办法是将其混入数字森林中。即:这台主机输出的内容没有人能看得懂的,只由无意义的代码和数字组成。用户浏览这台主机,不会触发任何关键词扫描。甚至该主机连https协议都不使用,更凸显其低调本色。

换言之,一个网站如果把自己的内容都变成字母和数字的组合,且不使用https协议,那么他就是数字森林中的一片树叶,丝毫不引人注意。

我们的目标应该是传输过程中不引人注意,并非绝对的不可破解的安全。

因此这个库的工作流程是:
1 php对"内容"做rsa加密->将加密结果输出到页面上。
2 用户浏览页面,html代码中的"内容"被加密成数字形态。私钥可以直接输出在页面代码中,也可由用户输入一次,保存在cookie中。使用cookie会降低密钥泄露的危险,更加有效。
3 通过javascript在用户浏览器上将这些数字解密为内容。
4 通过javascript dom来把内容写回到页面上。用户即可浏览。

利用javascript解密,可以把运算负担分散到客户端上。窃听者如要窃听每一个页面的内容,则必须要 1 获得密钥 2 用密钥解密内容

在已知密钥情况下,如客户端的每个页面运算负担为 1 ,页面数量n ,那么窃听者获得密钥之后的运算负担为 1*n。

为了运算效率,使用小质数作为rsa的p,q,理论上窃听者可以通过因数分解算出密钥,其运算负载为k,注意k 远远大于1。

如果每个站点使用不同的密钥,共计m个站点,窃听者的运算负担为 m*k+1*n,且负载集中。

而,如果采用双向可逆加密方法,在得知算法的情况下,窃听者运算负载极小。如果在通过变换算法来增加难度,又无法做到通用,给用户正常浏览造成困难。使用rsa方法,算法是标准的,用户使用成本很低,窃听成本很高。

在项目代码中,我已经实现了这一目标。但仍然有效率问题。

目前问题:

1 在没有bcmath和gnumath函数的php主机上,php加密内容的运算效率很低。和bcmath差距几十倍。好在大部分情况下,主机都是有bcmath函数的。这个问题不严重。
2 JS的bigint运算效率很低,主要是powmod的效率低,而这是rsa解密最频繁的操作。

希望有兴趣的朋友加入这个项目。效率问题解决后,还需要port在一系列常用软件上。比如dabr或twitese等。

另外,需要的质数可以在 http://www.prime-numbers.org 找。

我放了一个demo在: http://blog.devep.net/rsatest/test.php 可以看html代码,里面是没有中文内容的。

update: 使用了 http://www-cs-students.stanford.edu/~tjw/jsbn/ 的大数运算库,效率提高很多。

August 28, 2009

beta技术沙龙的snmp话题的个人总结

如前所述,这次活动我分享了银杏搜索在使用snmp监控方面的一些经验和演变过程。其实起初我认为这是一个相当冷僻的话题,没多少人会有类似的需求或是兴趣。没想到的是还是有很多同学说有用,也在沙龙上碰到了一些正好也在做这方面开发的朋友。非常令人欣慰。

总体来说,这个方案还是有很大的局限性。局限性首先来源于我们的服务特性,可以说是为了解决特殊问题的特殊方案。其次,还有很多地方不完善,仍然需要在应用中进一步完善。

ppt放在这里了:

另外做个广告,敏捷中国大会 2009即将召开,含金量颇高。有兴趣参加的,可联系 agilechina@cn.infoq.com 购票,说是我介绍来的可以有100块钱优惠 :D

July 27, 2009

匿名网民的安全指南(2)

题外话:有人问我,这么极端的安全有用吗? 我的回答是:对某些人没用,对某些人有用,对另外一些人可以救命。

前文:匿名网民的安全指南(1),是关于计算机安全方面的内容

Version: 1.0b virushuo (twitter@virushuo) 20090727

注意:

1 这不是一份完整的安全手册,只是一些知识参考。但他能帮助你了解那些最重要的细节。如果有发现相关部分好的文档和教程,请提供给我,我连接上。
2 本文主要用于商业安全和个人隐私安全(包括你的私人照片,银行账号等的安全),本文不鼓励将这种技巧用于其他用途,使用在其他用途造成的损失和后果与本文无关,正如用菜刀杀人与磨刀的老大爷无关。
3 本人并非专业研究计算机和网络安全。只是觉得略有所知,所以分享一些常识。如有专业人士认为有错漏,请不吝赐教。virushuo( attt ) gmail.com
4 Geek的选择,geekcook.org
5 郭宝锋,你妈妈喊你回家吃饭#amoiist 恭喜你成为本年度收获明信片最多的人
6 本文会时常修补增加内容,要转载请务必注明出处和以上注意事项。


账号安全和如何选择安全的服务

使用互联网,就是在使用各种服务。对于大部分服务来说,账号是唯一用来识别你的方式。账号安全不仅关系到你的在线数据安全,还关系到你的身份。如果对方盗用了你的账号,就很容易利用信任关系,从你的朋友手里盗走更多的东西,并且危害你朋友的安全。这不仅在计算机安全上成立,在现实中也比比皆是,比如如果你的手机落到别人手中,对方也有可能利用你的手机给你家人打电话说你出了车祸,急需手术费用等方式骗钱。在互联网上,不加密的情况下,你并不能知道那个熟悉的账号后面是你的朋友还是江洋大盗。所以,不仅为了你自己,为了你和朋友们的安全,请保护好你的账号。

安全的服务至少应该提供可选的https连接方式和一个足够复杂的验证码。来看看gmail和国内比较常用的新浪,163邮箱的对比吧。

1 复杂的验证码

一个足够安全的验证码至少要包含以下特征:


  • 要有横向和纵向的随机扭曲

  • 位置和大小要随机变化

  • 验证码长度要随机变化

  • 内容应该是字母的而不仅仅是数字

  • 字体要有随机的变化

  • 最好再有足够的干扰图案和条纹

目前看来,能符合以上大部分特征的服务,只有google,yahoo和msn这3家。这样的验证码在目前的情况下OCR出结果的成功率非常低,这样对于防止暴力破解相当有效。换句话说,如果有人想猜测你的密码,如果想解开如此复杂的验证码,只让计算机替他一个个试验是不行的,必须还需要有个真正的人来帮他做识别。这样就让破解的速度大大下降,成本大大上升,距离安全就更近了一步。

图0:看看google的验证码,其实有时候人来辨别也有一定困难。

国内常用的服务商中,sina和163的验证码也算安全,虽然距离gmail尚有较大距离。第二梯队的网站的验证码通常极不安全。

图1:163的中文验证码,其实也并不是很难处理。OCR(图像识别)软件要处理的情况通常比这个更复杂。

看看tom的,是不是觉得惨不忍睹?这种水平的验证码OCR的成功率轻松就可以达到100%,事实上和不存在是一样的。交警用来拍违章车牌的系统,要处理的情况都要比这个复杂吧?识别率不一样在98%以上?

图2:Tom的验证码,不堪一击

2 进行安全登录

常用google的同学会发现,当你登录google账号的时候,地址栏上的http会自动切换到https。也就是说,google是强制使用https方式进行登录的。进行登录的时候,需要把用户输入的用户名和密码发送给服务器,验证用户身份。如果是普通的http方式,所有传输的数据都是明文发送的,包括刚才输入的账号和密码,这种明文信息是非常容易被截获的,数据包所经过的任何一个节点都可以轻松获取,和你在同一个局域网的用户也有可能利用arp欺骗等方式获得这些数据。想想看,你在路边急需上网,突然发现了一个可用的无线连接,是不是很高兴?这时候,如果通过http登录了邮箱,账号就可能被泄露了。所以,强制使用https的方式进行登录,是非常必要的安全措施。google和yahoo都已经是这样的方式了。

同样看看163和sina吧。163是可选ssl的,默认是选中,勉强算合格。sina也可选ssl,但默认是不选中,高下立现。至于tom,似乎那个"增强安全性"被选中之后也不会通过https登录...

图3:sina免费邮箱,"增强安全性"默认是不选中的

3 日志功能

日志可以帮助你了解是否你的邮箱被入侵过。遗憾的是,目前应该只有gmail具有这个功能。把页面拖到gmail邮箱的最下面,可以看到 Last account activity: 1 minute ago at this IP 字样,这里给出了最后一次账号活动的时间和ip。

图4:gmail页面尾部有log功能

按一下Detail,信息更完整,最近的所有访问都有记录,包括IMAP方式,POP方式,WEB方式,一个也会少。有这个功能,可以很容易的知道是否有别人使用过你的邮箱。有人入侵就会留下时间和IP。这些记录是不可被抹掉的,除非入侵google的服务器。这可不是件容易的事。

图5:gmail的活动报告

鉴于以上原因,我强烈建议使用gmail作为你的主要邮箱,这是目前互联网上能找到的安全性最高的服务了。甚至我认为一个有理智的入侵者不应该来打你gmail的主意,因为他们很难成功,即使成功也很难不留下痕迹。

经过以上分析可以发现,除了大的服务商,google/yahoo/msn,包括163/sina..,大部分网站是不提供https方式的登录的。所以,至少应该让你的账号分为2组,不要把普通论坛之类的账号和邮箱账号的用户名密码相同。

update:从留言等反馈中看来,很多朋友推荐QQMail,据说也符合以上特征,不过我没用过,也不好评价。在这些指标上可能没问题,不过根据目前大量QQ群聊天纪录轻松被某些部门拿到手的情况看来,就算指标上没问题,实际应用效果恐怕也要打折扣。所以,还请结合用途,自行判断。

用证书来验证对方的身份

钓鱼攻击 ,通常是伪造一个网站,诱骗用户输入一些类似账号密码的信息。这就产生了如何验证网站身份的问题。我们如何知道打开的页面确实是google,而不是别有用心的一小撮人伪造的呢?

看页面样式?看网站地址?答案是都不可靠。网页样式很容易做到完全一样。地址则可以注册相近的,或欺骗性比较强的域名。比如,注册一个 sina-account-service.com 自称是sina账号服务中心之类。

最稳妥的确认对方身份的方式,是通过查看证书来确认。

大部分浏览器在访问https网站的时候都会在显眼的地方提供查看证书的功能。一般来说应该是一个锁头一样的图标。证书可以帮助你确认站点身份,这样你就不会被钓鱼或DNS欺骗。这些具体的欺骗手法我以后还会说到。只要判断证书是否是由是可信任的机构颁发的,是否是这个网站的正确拥有者所有,就可以知道你浏览器打开的这个网站是不是真身了。证书有问题,那么这个网站一定有问题,这时候就不要进行登录或是找回密码或是重设密码之类的操作了,无论页面上写了什么,牢记这个原则,证书不符合,就是不可信任的。对于163,sina这种邮箱来说,只在登陆的时候提供了ssl加密,用户在输入密码之前甚至没有机会察看一下证书,实在是令人遗憾的事。

图6:在safari中查看gmail的证书

图7:在firefox中查看gmail的证书

简单介绍一下钓鱼欺骗的办法吧。比如说,你收到一封邮件,来自sina-account-service@sina.com.cn,邮件告诉你,你的sina邮箱账号被攻击,需要立刻修改密码。这时候应该怎么办?正确的方式应该是完全不理,因为任何一个邮箱服务商不会这样给你发信。

当然,很多人觉得还是要点去看看的。于是就点了邮件中的连接。看一看应该没什么损失嘛...是的...不过...

这时候有两种可能,一种可能是:你来到了一个叫做 sina-account-service.com 或是干脆叫china-sina-account-service.com 的网站,页面内容和平时你使用sina邮箱的密码修改页面是一样的。这时候你会不会有可能在里面输入了密码?

有人会说了:"光页面像是不行的,域名不对嘛。" 没错,攻击者注册了sina-account-service.com 这样的域名,虽然看起来像那么回事,但是并不能骗住所有人。那么,另外一种情况:你看到的地址真的是 http://login.sina.com.cn/xxxx 这次相信了吗?这时候仍然不相信的人就不多了。

其实域名一样是可以仿造的。最简单的方法,如果对方修改你的hosts文件,把login.sina.com.cn指向了他控制的一台服务器,那么刚才的情景就很容易发生了吧?除了改hosts这种低级手段,还有很多其他的"高级"一点的方法,比如DNS欺骗...总之,这是完全可以实现的欺骗方法,事实也证明是带来损失最大的。

现在我们知道了,通过看域名来验证网站是否为真的办法是不可靠的。最可靠的办法是查看证书。大多数浏览器默认的128位的证书已经非常难伪造了。256位的证书被伪造就更困难了。当然,这里说的"困难"是伪造出一个真正的,完全一样的证书。如果只是要骗过浏览器,还有一些其他办法,特别是IE6,竟然还曾经出现过可以被伪造证书的bug。

总结一下就是:没有证书的网站很可能是假的。错误的证书一定是假的。看起来正确的证书也存在一定可能是假的。

对证书和https方面知识有兴趣的同学,可以去看wiki百科 SSLPKI


保护你的重要账号守则


1 选用安全级别高的服务。邮箱可以推荐gmail,yahoo mail。yahoo mail有出卖用户的恶劣记录,但仅讨论产品,仍然是最安全的邮箱产品之一。

2 如前文所述,保护好你的计算机安全,打开防火墙,注意检测木马。

3 不要在不安全的地方使用你的重要账号。

4 选择一个足够复杂的密码,并根据重要程度增加复杂性,对于最重要的服务,不要和其他服务公用同一套账号密码。如果不能做到所有服务的密码各不相同,至少也应该分成几个级别,对最重要的服务使用最复杂的密码,不要和其他服务使用相同的密码。随手注册的论坛之类可以使用简单密码。

5 时常检查邮箱是否有入侵迹象。

6 不信谣不传谣,什么"中奖了","好友请你输入账号","你的账号被盗用,请在此修改密码"之类的东西统统不要相信,牢牢记住,你唯一可以输入密码的情况是:一台安全的计算机,输入密码的页面证书正确,使用https。除此之外,在任何情况下都不要输入你的账号密码。

7 应该理解,不通过https加密的登录是极不安全的。存在极大的泄露可能。注意检查证书。熟悉你使用的产品的安全特性。

8 多注册几个马甲,分为不同的安全级别,用在不同的地方,这样在最糟糕的情况下,损失也可以小一些。

如何选择密码和密码提示问题

如果你选了一个123456作为密码,恭喜,这是最常被用到的密码之一。如果你用了qwerty,恭喜,这也是最常用到的密码之一。至于用户名和密码一样的家伙们,你确定你真是有隐私而不是暴露狂吗?

好吧,你选了一个6位数字密码。如果你幸运的没有选择你自己的生日,没有选择你的手机尾号,那么运气最糟糕的破解者大概要尝试10的6次方次。仅仅100万次,在这个时代,如果你的服务商没有可靠的验证码,没有限制一天尝试密码的次数,那么这个运气最糟糕的家伙试完全部的100万次排列组合也不太可能超过24小时。这个运算能力空前强大的年代,用反复尝试的办法来破解密码已经越来越容易了。所以必须要让密码变的更复杂。

密码提示问题是容易被忽略的地方。很多人设置一个复杂的密码,却往往设置一个简单的密码提示问题。这就好比你家装了一把防盗性能很好的锁,但是却在门口的地毯下面藏了一把钥匙一样危险。

密码提示问题和密码应该是同等重要的,而不是注册的时候演示的那种"你的爸爸叫什么名字"之类的愚蠢问题。

正确的密码和密码提示问题选择守则

1 够复杂

所谓够复杂,意思是:超过8位,包含字母,数字,不同的大小写和至少一种符号。这样的组合会让暴力破解者累死。如果你跟我一样喜欢用13位以上的长密码,那么试图暴力破解的人可能会等到他老的敲不动键盘也拿不到你的密码。如果不是网站的服务,而是rar压缩包之类的文件密码,就更要遵守这个原则,文件打开的时候可不会问你验证码。

2 不要和你的公开信息有关

如果你遵守了1,那么也就没有这条的问题了。一个那么复杂的密码不太可能包含直接的个人信息。如果你实在不愿意遵守1,那么,至少不要用你的生日,电话号码,门牌号这种非常容易得到的个人信息。

对了,也不要把这些东西作为问题放到你的密码提示问题中。竟然有服务商会把默认的密码提示问题写做"我爸爸的生日",用户也老实的跟着回答了。要知道有种东西叫做户籍系统,你以为真的没人能知道你爸爸的生日?

3 使用不同的密码

不要给不同的服务使用相同的密码。这世界奇怪的事情太多。你很难知道对方是否把你的密码md5之后保存了,也很难保证各服务的安全性能一样好。如果你使用了一样的密码,那么泄露一次,全盘皆输。

如果实在不想记那么多密码,那么选出来最重要的几个,用最复杂的密码。其他的可以简单一些。其实,实在忘记,可以通过邮件来找回密码,这样邮箱安全就更重要了。
按照我的建议,你应该去注册一个gmail邮箱,然后按照原则1来设置一个13位以上的密码,然后再也不要再其他任何地方使用这个密码。

4 别把你的密码记在本子上

我常常看到,有些人使用非常复杂的密码,密码的强度是合格的。但是他们自己也记不住。于是就写在一个小本子上,或者干脆记在一个文本文件里。这是非常危险的习惯。一旦丢失,所有的密码都丢了,而且因为他自己并不能记住密码,小本子一旦丢掉,连去修改密码都做不到了。

最重要的几个密码一定要用脑子记住。其他的密码可以记录下来,但是要选择一个适当的密码管理工具。如果你使用mac,那么就是Keychain Access。这是一个非常安全的工具。如果操作系统本身不带有密码管理功能,还有一些软件可以完成这个工作。但是要注意软件来源安全(以前也讲过这个问题),也要考虑软件本身的可靠性。

下一部分我们来探讨网络传输过程的安全,以及如何做到真正的匿名。


一些相关链接:

gmail 安全检查步骤

检查你的 Google 账户安全

July 24, 2009

匿名网民的安全指南(1)

Version: 1.0b virushuo (twitter@virushuo) 20090724

注意:

1 这不是一份完整的安全手册,只是一些知识参考。但他能帮助你了解那些最重要的细节。如果有发现相关部分好的文档和教程,请提供给我,我连接上。
2 本文主要用于商业安全和个人隐私安全(包括你的私人照片,银行账号等的安全),本文不鼓励将这种技巧用于其他用途,使用在其他用途造成的损失和后果与本文无关,正如用菜刀杀人与磨刀的老大爷无关。
3 本人并非专业研究计算机和网络安全。只是觉得略有所知,所以分享一些常识。如有专业人士认为有错漏,请不吝赐教。virushuo( attt ) gmail.com
4 感谢geekcook.org的支持和建议
5 郭宝锋,你妈妈喊你回家吃饭 #amoiist
6 本文会时常修补增加内容,要转载请务必注明出处和以上注意事项。


网络是怎样传递信息的

整个互联网,可以看作是一台台计算机组成。当然,他们承担了不同的任务。一些机器负责存储你的信息,一些机器负责寻找路线,另外一些则只负责传递。你的信息,或者叫做数据,总是穿过了一台台计算机,才到达最终的目的地。可以想像,在穿过这一台台计算机的时候,是一次多么惊心动魄的历险。每台计算机后面都有一双眼睛,有的还不止一双。这其中可能有你的朋友,可能有完全中立者,不幸的是,你的敌人也往往混迹其中,虎视眈眈。

如果你不采用任何安全措施,则你的信息对于这无数个环节都是透明的。这可能意味着:你的商业机密转天见报,你的情书转天成为办公室趣谈,你的账号上一大笔血汗钱不翼而飞。当然,当然,还有最糟糕的......你还没来得及反映过来,就发现被人塞进车里送到一个不知名的地方。为了不至于成为陈冠希或是被躲猫猫,那么你必须得把这个事情重视起来。

大部分情况下,危害安全的行为都是广泛的,并非针对你个人,比如说,用扫描器来大量寻找具有某些漏洞的机器,或是用爬虫来尝试用123456做邮箱密码的家伙。所以,只要稍微设置一些障碍,对方往往知难而退,去寻找更容易上手的菜鸟。而,特殊情况下,你被盯上了,这就要非常留心了。

一份数据(文档,照片,聊天的一句话,统称数据),从你的计算机上完成编写,经过你的网络发出,进入公共网络。经过无数节点之后,到达某处,等待另外一方获得。这就是进行一次互联网应用的过程。我们来依次看看这个过程中哪里会产生安全问题。

1 你的计算机是否安全?你的操作系统是否有漏洞?你的计算机内是否有木马或病毒?是否有其他人可以使用你的计算机?
2 你的网络是否安全?你的局域网内还有谁?
3 经过的节点安全吗?你的数据是否进行了加密?
4 为你提供服务的服务商安全吗?你所拥有的账号是否泄露?
5 你的IP地址安全吗?你是否愿意被别人追踪到你的IP?
6 除了目标方,还有别人能看到你的信息吗?尤其是email和IM的聊天。

先问问自己这些问题,如果答案不确定,那么就继续往下看吧。

计算机安全

计算机安全是一切的基础,但又往往被忽视。可以想像,如果在你使用计算机的时候,身后永远站着一个人,时刻盯着你的屏幕,那一切安全都无从谈起。所以,首先要保证你有一台足够安全的计算机。这个安全是一系列良好的习惯。绝对不是安装一个杀毒软件就可以高枕无忧的。

1 补丁,木马,病毒,以及其他


对于个人计算机的信息安全来说,最大的挑战是木马。木马真正实现了"你身后永远站着一个人"的效果。相比之下,病毒最多是破坏资料,侵占系统资源,尚无泄密之忧。在这个时代,我认为病毒的安全危害远远小于木马了。现代破坏者们也不再和当年的hacker们一样,乐于通过写一个"得不到任何利益"的病毒程序来炫耀自己的水平。他们更感兴趣是你的银行账号,邮箱密码,网游账号等等真正能变成钱的东西。就算这些都没有,控制你的计算机去帮一些网站刷Alexa排名也是能赚钱的。从几年前开始,新的木马程序和流氓软件产生的速度远远高于传统的病毒,其中"利益为上"是主要原因。

安装一个木马到你的计算机上有很多方法。最简单的方法,就是诱使你安装某个程序。 这个行为又可以分成很多具体操作。常见的有:

a 绑定在正常的软件中。
b 通过常见的聊天工具,利用你的好友给你发送消息,骗你下载(比如QQ尾巴)
c 群发邮件,诱使你运行附件
d 把图片,flash等等打包成exe文件,把木马捆绑上。

最简单的办法永远是骗。千万不要看到"骗"这个词,就觉得技术含量很低。实际上,这个办法学名叫做"社会工程学"。实在是一种专门的学问,不可小看。后面我们还要多次提到这个词,你可以看到,几乎在各个环节,只要有人参与,就有"社会工程学"发挥的机会。

比最简单的方法复杂一点的办法,就是利用浏览器的漏洞。IE是个漏洞百出的浏览器,出现过多次致命漏洞。通过在网页中嵌入一些代码,就很容易利用带有漏洞的IE把木马安装到计算机上,比如"灰鸽子"。

最复杂的方法,就是缓冲区溢出(其实利用IE的漏洞也是缓冲区溢出的一种。),你的计算机上总是运行着很多程序,这些程序,包括操作系统(windows)本身,都可能存在各种漏洞。这些漏洞如果被巧妙的利用,就可以用来安装木马。历史上有几次大的破坏,比如针对windows漏洞本身的"红色代码","尼姆达"。而如果你不小心安装了权限很高,漏洞很多的软件,那么就更容易被攻击。比如我之前说过绿坝的危害。

要保证你计算机的安全,建议做到以下几点:

a 如果可能,不要使用Windows。Mac和Linux的安全强度高的多(很多人认为Mac/Linux安全是因为用户群体小,这个看法是不对的。后面我会用一些例子说明),Windows也并非如大家印象中的那么不安全,只不过需要付出更多的精力来调整和维护。这和一般认为Windows"易于使用"的特性恰恰相反。

b 如果要使用Windows,必须做到及时更新,打所有的补丁。最近看很多人号召关掉Windows自动更新,这是比较危险的行为。我记得360安全卫士提供了给Windows更新的功能,还不错,如果关掉了Windows自动升级,那么就用360的吧。(从目前的情况看来,360不再可信。不要再装了)反正不升级是肯定不行的。

c 正确使用防火墙。Windows XP自带了防火墙,一般来说就不用装其他的了。只要把大部分没用的端口关闭掉,就能应付很多情况了。这篇文章可以参考 如果你有一个路由器,那么配置合理的话可以当作硬件防火墙使用(当然也会导致一些新的问题)

d 不要运行任何来历不明的程序。包括你朋友的推荐来的。如果有朋友通过msn或是邮件发给你一个软件或让你下载,那么问他几个问题,以确认是他本人在操作计算机。必要的时候,甚至可以打个电话给他。不要怕麻烦,很多时候,这也是在帮助你的朋友。

e 如果你非常热爱折腾新软件。那么,准备一个虚拟机,专门用来试用软件。确认安全和有用之后再挪到你的工作系统上使用。相信我,你没有那么多需要的软件。

f 如果可能,尽量使用开源软件,并注意在下载的时候校验MD5或PGP,以便确认你下载的是真正的软件,而不是被别人替换过的。可参考这篇文章 其实,我甚至想极端的建议拒绝从不提供任何校验方式的下载站下载,这种下载站要么缺乏基础的安全常识(这样他们就很难保护你的安全),要么就是根本不负责任。

g 杀毒软件在这个时代用处不大,空占资源,还可能惹出来很多新麻烦。包括把能够保护你的软件悄悄删掉(后文会提到)。所以,如果你做到了前面几点,就别浪费这份钱了。

2 权限,账号和共享


使用计算机是有权限的。一般来说,应该降低权限使用。不幸的是,我在很多地方看到人们大摇大摆的使用administrator(或者是具有管理员权限的其他账号),这是系统的最高权限,就像你这台计算机的上帝,日常用这么高权限的账号,危险啊!更糟糕的是,很多人的administrator账号没有设置密码,或是只有简单的密码,在这个危险的世界里,这是和裸奔差不多的行为。正确的做法是:

a 给自己的日常使用建立一个权限较低的账号。甚至低到这个账号只能运行指定的程序才好。

b 前面说了,尽量不要和别人公用计算机,哪怕是家庭成员,限于现状,实在做不到的时候,也应该给其他人单独建立账号,而不要公用一个账号。这些账号的权限都不应该太高,因为一个安全意识不强的人中招,有可能影响到所有人。一些网吧管理软件可以帮助你更好的完成这项工作。

c 设置一个复杂的密码。如果你的administrator密码为空,或是很简单,那么别人很容易通过访问C$来读取你的硬盘,这意味这对方可以拿走你硬盘上的所有文件,管他是艳照还是商业机密。(至少在XP时代,这样还是可行的),所以,给你的administrator设置一个复杂的密码吧。

d 时常注意检查你的共享文件夹。比如说,你在家里把笔记本上的文档通过共享复制到台式机,而你忘记关闭了笔记本的共享。那么下次你去咖啡厅上网的时候,跟你公用一个无线网络的人也可以轻松拿走这些文件。(但愿里面没有艳照...) Windows 使用管理工具->计算机管理->系统工具->共享文件夹 中可以查看哪些文件被共享了。

顺便说一句,我见过某人,从一个竞争对手的总经理笔记本上,弄到了数G的文档,无所不包,甚至包括该公司员工的工资表,相当壮观。其实,那个可怜的家伙也没犯太大错误,无非是违背了c和d原则而已。

在Mac中,默认系统是不允许直接使用最高权限账号root的,其实这也是unix家族的传统。在需要使用管理权限的时候,会给予提示并要求输入密码(这个特性我4年前写过一篇blog讨论 )。这个小小的技巧让系统安全性大大增加。Windows Vista似乎也采用了类似的方式,不过据说做的不好用。我没用过,就没发言权了。

Mac中,默认的共享只有一个"公共文件夹"和其下的"投递箱",公共文件夹是所有人都可以看到的文件,而"投递箱"是别人给你传递文件用的,顾名思义,放到这里的文件,只有你本人才能访问,哪怕投递者本人,一旦放进去也不能再访问了。这些默认的设置很好的引导用户正确使用共享功能,降低了安全隐患。Windows通过一定设置应该也可以实现同样的效果。


3 公共计算机安全守则


如果万一你需要在公共场合使用计算机,那么往往代表存在着极高的安全隐患。必须要提起重视。你永远也不知道你使用的这台计算机上安装了什么软件。是否有自动屏幕截图?是否有键盘按键记录?是否记录所有发送的数据包?天,这就好像你到酒店开房,永远也不知道房间是否有隐蔽式摄像机和窃听器一样......

当然,对方可能是你很好的朋友,他并无恶意,也没打算窃取你的机密。但是,如果他的安全意识不高,那么他早就成了别人饵下的鱼。你跑上去登录一下你的邮箱,只不过是给幕后黑手多一份惊喜罢了。

所以,同样也遵守一些守则吧:

a 不使用不加密的服务。只访问https的服务,这可以有效的避免有人记录你登录数据,所谓登录的数据就是你的用户名和密码。如果不是https的服务,那么你的用户名和密码是用明文发出的。等于白白送给别人。

b 不要使用你最重要的邮箱。你最重要的邮箱往往是你一系列服务的基础,如果这个邮箱落到别人手里,他可以通过找回密码来搞定你所有的服务。所以,最好准备一个备用邮箱,专门放一些不太重要的东西,以及在这种场合使用。

c 一个小技巧。输入密码的时候,先打开一个记事本,在里面胡乱按很多字符,然后用鼠标把你需要的密码字符一个个粘贴,复制到相邻的位置。然后把他们连同一些多余字符,一起复制粘贴到密码输入框中,删去多余的字符,登录。这个技巧是为了防止计算机后台运行了键盘记录木马,鼠标位置的移动比键盘的纪录难的多,所以经过这番折腾,能给窃取者制造一些麻烦,给你修改密码的行为争取一些时间。复制粘贴一些多余的字符,是为了防止有记录剪贴板内容的木马存在。这样他们得到的密码是比实际多几个字符的。

d 一旦回到你的安全计算机上,必须修改你的密码。要修改的密码包括你在公共计算机上使用过的服务,也包括同名同密码的其他服务。比如 iamhere@gmail.com 和 iamhere@163.com两个邮箱密码相同,你只登录了后者,但是前者的密码一样要改一次。不然,假如后者密码被键盘记录器记录了,那么前者也会被试验出来。c的技巧是为了给你争取到一些修改密码的时间。

公共计算机往往是安全泄露的重要环节,我听说过有人网游账号被盗,有人邮箱被盗,今天还听说了一个朋友因为邮箱被盗,最终导致支付宝被盗。这些尚且是损失一点钱和时间。如果你立志做一个匿名网民,邮箱里面有一些不想被别人知道的文档,那么...诸多血的教训一定要牢记啊。

待续,下一篇中我们将讨论什么样的网络服务比较安全,应该选择什么样的邮箱作为主要邮箱,如何保护你的账号安全等话题。

July 15, 2009

beta技术沙龙:lighttpd , mod_cache , 进程线程以及其他

阙宏宇是我相当佩服的程序员,做同事的一段时间里,让我学到了很多东西。从当年就看他在做lighttpd的缓存扩展mod_cache,也实际使用情况。近几年这东西经过几次改进,越来越好用。甚至创下过单服务器跑满1G网卡的记录。这次很高兴能有机会请他来beta技术沙龙分享一些经验。

他所在的环境也非常好,有相当大的流量可以实际应用,这也是大部分人难得的机会。我看到了很多人在twitter上的相关讨论,如果有机会,希望你们也能来beta技术沙龙,分享下你的经验,不同行业下的背景肯定不一样,这是相当有讨论价值的。

争论最多的地方应该是 线程/进程 。这是永恒的争论,频率大概只小于"java好还是C#好"。这几天在Top Language组里面也在讨论线程和进程的问题。按照我个人有限的经验,我更倾向于多进程,尤其是存在共享数据区域的时候,多线程的写还是挺容易出问题的,更不容易调试。一种说法是:使用锁是基本功,但我宁可采用多进程,分开数据区域。(mod_cache的多个进程之间也是不共享数据的)。当然,这事关应用场合,对于缓存来说,这种处理完全可行。确实有一些情况必须使用多线程写,但这种情况我也没觉得线程比进程能好多少。事实上,除了图形界面程序,我个人不打算在任何地方应用多线程了。

也有说法认为多进程是浪费了多个核。其实不然,服务器程序是连续处理的,虽然进程之间存在等待和数据复制的问题,但多个任务的连续处理正好可以弥补这个空闲,计算资源不会被浪费。用个不怎么贴切的例子来简要说明一下吧:文件下载的时候,如果下载一个文件,分开多个线程,同时下载多个块能加快一些速度,而如果有多个文件下载,那么所有文件都单线程下载,但多个文件同时进行,最终速度是差不多的。前者需要处理锁,后者什么都不用管,简单很多。

这样做设计上可能需要花一些心思,不过我觉得比处理锁还是容易点。如果说"用好锁是程序员的基本技能",我认为能够正确的划分数据区域和任务,降低耦合也是必须的基本技能。这样划分好之后,程序是可以分布在多台机器上(当然通讯开销更大),未来的扩展余地更大。(仍然是要看应用场合的)

twitter上看有朋友提到更好的解决方式是erlang。不过这个我尚未涉足,不甚了解。不敢乱说,还希望有实际经验的朋友能写点东西分享。

继续说回lighttpd和mod_cache。这个组合其实是相当不错的,也有过很大的应用考验,可以放心用了。基于lighttpd,我也写了一个mod,用来做转发。用途是从某个url pattern转向某个端口。一般人可能用不到这个功能,也没什么成熟产品。于是我就基于lighttpd写了一个,效果很好,目前已经运行了500多天无故障。

在大量并发请求的时候,我认为event base是个不错的选择。我对lighttpd 1.5的实现不太满意。但1.4是相当不错的。现在作者不怎么活跃,不过已经足够用了,代码也相对简单,有问题可以自己改改看。是个不错的选择。

这次活动有视频记录,ppt和视频都在这里可以看到。

另,下次活动主题是:大型网站的lucene搜索实战,欢迎到时候来参加和讨论。

July 1, 2009

xcode,cocoa开发:如何使用第三方的dylib

update:留言中jjgod的方法更为正宗和简单。请参考。不过如果dylib之间有依赖关系,你又不想重新编译。那大概还是要用我这个办法的。

所谓dylib,就是bsd风格的动态库。基本可以认为等价于windows的dll和linux的so。mac基于bsd,所以也使用的是dylib。

如果你需要引用一个第三方的dylib,在xcode下编译出cocoa程序,在本机上运行是不会出问题的。但是发布出来,给其他用户用,就可能出问题。因为用户不一定有这个库。

这个问题给我造成了相当的困扰,我到现在也没找到正规的方法。但是我确实解决了这个问题,虽然方法不一定正宗。不管怎么说,写下来,如果暂时没有更好的办法,那么先这样做。如果谁有更好的办法,也请一定不吝留言或邮件给我

我的办法是这样的:


1 otool -L yourapp.app/Contents/MacOS/yourapp

这一步的意思是对你编译出的app使用otool命令,以便获得依赖哪些dylib的信息。注意这个路径。cocoa的app在命令行下表现为目录。所有相关的东西都在里面。
结果如下所示:
yourapp.app/Contents/MacOS/yourapp:
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 12.0.0)
/usr/local/lib/lib01.dylib (compatibility version 0.0.0, current version 0.1.0)
/usr/local/lib/lib02.dylib (compatibility version 0.0.0, current version 0.1.0)
/usr/local/lib/lib03.dylib (compatibility version 0.0.0, current version 0.1.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.0.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 227.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 476.0.0)
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 949.0.0)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 677.12.0)

注意我标红的地方。假如lib01,lib02,lib03是本程序引用的第三方库,那么在这个程序里面,他们的引用地址是位于/usr/local/lib上的。这是开发机上的安装情况。而使用这个程序的客户机未必安装这些东西,所以程序就要出错。

显然,我们需要做2件事。a 把这些库附带在app上 b 把他们的引用地址修改到正确的位置。

2 mkdir yourapp.app/Contents/dylib

在编译出来的app中,创建dylib目录

然后把所有需要的库复制过去

cp /usr/local/lib/lib01.dylib yourapp.app/Contents/dylib/
cp /usr/local/lib/lib02.dylib yourapp.app/Contents/dylib/
cp /usr/local/lib/lib03.dylib yourapp.app/Contents/dylib/

3 install_name_tool -change /usr/local/lib/lib01.dylib @loader_path/../dylib/lib01.dylib "yourapp.app/Contents/MacOS/yourapp"

install_name_tool 是苹果提供的用来修改dylib安装名称的命令。这个命令执行之后,再用otool -L 就可以看到变化了

yourapp.app/Contents/MacOS/yourapp:
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa (compatibility version 1.0.0, current version 12.0.0)
@loader_path/../dylib/lib01.dylib (compatibility version 0.0.0, current version 0.1.0)
/usr/local/lib/lib02.dylib (compatibility version 0.0.0, current version 0.1.0)
/usr/local/lib/lib03.dylib (compatibility version 0.0.0, current version 0.1.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 111.0.0)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 227.0.0)
/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 476.0.0)
/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 949.0.0)
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 677.12.0)

注意标红的位置。已经变化了。@loader_path 指的是应用程序运行的位置,也就是yourapp.app/Contents/MacOS/yourapp,所以要用一个..,以便定位到第2步创建的dylib目录。
重复这个命令,修改lib02,lib03


4 otool -L yourapp.app/Contents/dylib/*.dylib

继续用otool 来检查dylib下面使用的第三方库是否还有其他依赖,install_name是否正确,重复1,2,3的步骤,把所需要的dylib复制过来,修改install_name。

如果都改对了,那么这个app就附带上了dylib,可以在其他机器上正确运行了,不用非要寻找/usr/local/lib下面的库了。


刚才我们修改的结果是一个build的结果。当然,每次build都这么折腾一下很麻烦。所以继续这样做:

1 前面的步骤得到了一个完整的dylib目录。把这个dylib复制一份备用。比如放在你的xcode项目下面。

2 编写一个脚本:

mkdir "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/dylib"
cp -f /your/path/to/xcode_project_name/dylib/*.dylib "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/dylib/"

install_name_tool -change /usr/local/lib/lib01.dylib @loader_path/../dylib/lib01.dylib "$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME"
(用这个格式重复前面对app使用过的dylib)

3 在xcode中,展开targets节点,右键点工程名称,在菜单中选Add->New Build Phasa->New Run Script Build Phasa,在打开的对话框里面,把刚才的脚本贴进去。如图所示。

这个脚本会在build之后自动运行。不过我这里有个奇怪的问题,如果Shell里面写了/bin/sh,会报告找不到这个文件(实际上存在),而让shell为空,反而可以正确的运行shell命令。

经过这些处理,每次编译出来的app就可以拿到其他机器上运行了。可真够麻烦的...

June 15, 2009

绿坝的技术错误及其可能导致的后果

简单谈几点绿坝的技术上的错误。如果不改进,我相信未来会造成巨大的损失。

1 不应该用木马的方式来保护自己。

站在绿坝的观点上,认为不能被孩子随便卸载和杀掉,所以就要强力的保护自己。于是用了一系列只有病毒和木马才会用的办法。这是典型的方法错误。正确的方法应该是给孩子一个单独的账号,降低此账号权限。然后绿坝以管理员身份运行。这样就不用靠流氓手段保护自己。家长和成年人也可不受其干扰。眼前使用的办法,其实也保护不了自己的安全和不被卸载。

2 缓冲区溢出的问题必须要重视。

到目前为止,密歇根大学提出的缓冲区溢出漏洞没有被修正。而根据电脑报文章,用户在正常使用中时而会导致浏览器崩溃。这大概也是溢出的迹象。在我的周末的简单研究中,觉得确实如密歇根大学报告所说"使用了较陈旧的编程方法",系统中存在缓冲区溢出的机会大大增加。

简单的反编译就可以发现,程序中大量数组和内存操作是不安全的。导致缓冲区溢出的机会很大。还有相当数量的硬编码,把一些密码之类的东西直接写了进去。这些不良的编程习惯都会带来安全问题

缓冲区溢出并非让程序崩溃退出那么简单,事实上利用这种漏洞是可以完全控制一台计算机的。历史上几次大的病毒传播,比如nimda,比如红色代码,都是缓冲区溢出引起的。缓冲区溢出不仅困扰小公司,对微软这种大公司也是巨大的安全威胁。但是像绿坝这样,把漏洞百出的代码做最广泛的推广和强制安装,实在是匪夷所思。如果真的这么推广下去,真的都安装了,未来还不知道出什么事。

如果被用来攻击电子政务系统或是骨干网的路由设备,造成的损失很非常大。不知道到时候政府会不会后悔现在的行为。

3 不加密的升级方式很危险
绿坝的升级都是通过http的,并且频繁调用gethostname来做dns解析。一方面,简单的dns欺骗就可以让系统安装恶意代码。另一方面,如果升级服务器被攻击掉,也很有可能导致前几天的"暴风断网"事故重演。规模恐怕还要大的多。

4 杀毒软件怎么办
绿坝的很多行为,和木马很相似,这些行为必然会被杀毒软件和木马清除软件识别。如果工信部强令杀毒软件把绿坝加入白名单,最终就等于在系统里面为病毒制造一个保护伞。病毒和木马可以通过附着在绿坝上面,躲避杀毒软件的扫描。这也是严重的安全问题。

总结:一般来说,大规模安装的软件要特别注意安全问题。最有效的办法是降低自己的权限,那么就算出现问题也不至于影响太大。但是绿坝反而用不正当的方式获得了很高权限。而高权限的软件则要至少能保证自己的安全,绿坝显然也做不到这点,绿坝调用各种dll并没有做校验,这都是非常容易被利用的。

一个具有高权限,又被普遍安装的软件,一定会吸引大量的攻击,这个诱惑太大了。这在windows一贯历史上都得到了验证。而一个不严谨,具有高权限,又广泛安装的软件,一旦被攻击,结果会怎么样呢?历史上没有过先例。但我想一定不是个好结果。

另外,在我研究的这部分(xnet2.exe,gn.exe,xdaemon.exe)中,我没有发现往服务器发送用户隐私的行为。但这不代表其他部分没有。大正的引擎是和绿坝是完全独立的,只是通过exe进行了简单调用,这部分也存在前面提到的问题,很容易被冒充或注入。在卸载方面,我看到了绿坝的卸载代码,就软件开发者来说,他们确实是想完全卸载,不留痕迹的。但是因为技术原因未必能做到。而且在某些其他程序干扰下,可能卸载也不能顺利完成。这也造成了"不能卸载"的假象。

如果工信部真的想解决未成年人保护问题,请将此软件开源,以便保证其中立和完善。苹果操作系统甚至自带了未成年人保护功能,这确实是应该的。但不应该是用目前这种危险的方式进行。

说到担心计算机被政府控制用来限制言论自由的观点,我觉得也不用太在意。事实上,所有政府都希望做到这事,但在操作上是如此的不具备操作性。如韩寒所说,计算机上有个东西叫做键盘。这就决定了不可能有超越用户存在的软件,哪怕是最厉害的病毒。一个试图当上帝的程序,最后的命运往往是去见上帝。

计算机是完全开放的,互联网也是。他们的体系和架构就决定了这一特点,任何与这个特点相反的努力都是徒劳的,好像螳臂挡车,阻挡历史的车轮。必然是不可实现的。

May 29, 2009

beta技术沙龙-iPhone开发

这次Beta技术沙龙是关于iphone开发的。对iphone开发有兴趣的人非常多,一方面是appstore造成的财富神话,另一方面也确实是有iphone的人越来越多。大家都偶尔想写点小程序玩玩吧。

作为一个mac用户,能感觉到企业对苹果平台的兴趣在逐渐变大,甚至连腾讯这种以不兼容为美的公司都做了QQ for mac/QQ for iphone。几个做共享软件的朋友跟我说,iphone程序是个富矿,很可能重现当年共享软件的疯狂。(其实国外现在做共享软件也是不错的)

我虽然用了很多年苹果,不过一直也没开发过"真正的"苹果的应用程序。我已经很多年不写图形界面的程序了,通常只写人们看不到的东西,就算有需要,也是三两下扒拉出来一个命令行程序用。而Tiny自从抽奖赚到了一个iTouch之后,就一直尝试着iPhone的开发,我偶尔去看看他做的东西,总觉得Objective-C这东西很古怪,非常不符合我对C的审美,因此也更加没兴致尝试了。

在这次沙龙活动中,听了robin和Tiny的介绍,倒是发现了一些以前没注意到的东西,所以觉得还是值得做个笔记。

Objective-C的古怪之处,主要是一些语法看起来很晦涩。奇怪的定义和符号混合起来,让这种语言看着一点也不像C。Tiny在演讲中多次强调了,阻挡人们学习Objective-C的主要原因就是这种奇怪的语法,容易让人不适应。

当然,仔细想想,似乎所有用来写图形界面的语言都扩展了大量的独有特性。这句话也可以反着理解成:凡是没做特殊扩展的语言,在写图形界面程序的时候都比较困难。

以我的认识,这种困难主要来自图形界面设计的复杂性,和消息/事件传递的难度。如果不写图形界面程序,C足够完成任何功能。而一旦要处理复杂的界面和消息响应,复杂度陡然上升,这就需要面向对象了。在以往,delphi改造了pascal,VC改造了C++,主要的目标都是为了基于面向对象,简化消息处理,事件相应和界面设计。MFC固然难以理解,不过还是比手工用API处理消息容易一些,程序越复杂,这个差距就越明显。

Objective-C要解决的问题也差不多是这些。Objective-C用可视的方式设计界面,用拖拽的方式来创建事件关系,扩展了一些语法,把C改造成了面向对象语言。用引用计数和autorelease池,使内存管理变的简单,对于字符串处理这种容易出问题的操作进行了封装。感觉用起来很像delphi。有人甚至做了一个Objective-C和delphi的语法对比,确实有很多相似之处。

和C++不同,Objective-C是基于消息传递的,后来我猛然想到,这是来自Smalltalk的思想(其实Wikipedia中,关于Objective-C的介绍第一句就是"Objective-C is a reflective, object-oriented programming language, which adds Smalltalk-style messaging to the C programming language."),想到了这个也就知道那些古怪的语法是怎么来的了。在Smalltalk中更极端,连运算都是以消息形式完成的。Objective-C中似乎还没到这个地步。但是已经有Smalltalk的感觉了。这些特性让Objective-C成为一种非常具有动态特征的语言,这也造成了一些错误在编译检查是正确的,但是到运行时出错。所以写程序的思想也要随之调整一下。

明白了这些东西,也就更容易理解Objective-C的思想。觉得别扭是大家都被C++和Java带到坑里了,其实Smelltalk才是纯粹的面向对象语言。如果对Smalltalk不太熟悉,可以来看看徐宥的这篇关于Smalltalk文章,明白了这些思想,对理解Objective-C有很大帮助。

写程序最难的事情,就是逻辑的描述,内存管理和字符串处理。Objective-C很顺利的解决了这几个问题。所以按照我的判断标准,这就是一种不错的语言了。就目前的语言特性看,不太容易成为C++那么混乱和让人抓狂的语言。再加上苹果的精心维护和推动,未来应该很看好。甚至将来成为最常用的语言之一也说不定呢。

如果你来参加了这次活动,应该已经能动手写iPhone和mac程序了。其实一些要点点破了之后,mac/iPhone的开发一点也不难,比其他平台上简单的多。程序员可以不太关注语言的多义性和复杂表述,更容易专心于写程序本身,也更容易获得高质量的程序。

PPT在这里:http://club.blogbeta.com/63.html

另请在twitter上follow betasalon : http://www.twitter.com/betasalon

May 22, 2009

优化和架构之服务切分

切分是最基本,且最多变的思路之一,说基本,是因为在学习程序设计的第一天就应该知道,说多变,是因为在未来的很多年里,你会不停的应用这个方法来解决问题。不幸的是,切分这个思路并没有得到应有的重视。

大概是因为这个词比较土,说起来也比较普通,远不如并行,集群,负载平衡这些词听起来大。所以碰到一个问题的时候,往往被拿出来的解决方案会是以上3个大词之一,很少有人去认真的考虑切分问题。但事实上,这3个大词所需要的技术,其实也是建立在良好可切分的系统之上的。

最近碰到了2个项目,都是典型案例。

案例1 ,小公司,发展的不错。一台服务器眼看不够用了,于是就买了第二台,希望能做一个"负载均衡"系统。很多人大概认为负载均衡,是类似自来水一样的技术,只要打开笼头,清水就汩汩涌出。往往忘记了水龙头后面的水管和自来水公司。

一个服务器上放了很多个服务,是很难应用负载均衡这种技术的。必须先要把服务拆开,找到性能薄弱的点,对这个点进行负载均衡,才能得到比较好的效果。否则很可能用了80%的力气,但是只得到20%的结果。

案例2,某外企。之前我们给他们做过咨询,解决了一次问题,上次我们找到了系统最慢的地方,去掉了老系统中性能消耗多的地方,用通用的缓存(squid/memcached)系统来代替他们用php写的硬盘文件缓存,上次的改动让性能困扰远离了他们一年,随着业务发展,性能又显得不够了。

比起来一年前,他们的服务器数量增长了一倍。其中大量服务器用于做mysql集群和web集群。服务器是复杂的网状关系。前面说过,这种不分主次的架构往往只能解决20%的问题。而且很容易到达瓶颈。

套用回那3个大词,这个系统确实是集群的,负载平衡的。因为所有服务器的功能是完全一样的,任何一台单独拿出来,都是完整的系统镜像。看上去很美,但实际用起来可没有这么理想。在实际应用中,一台服务器出了问题,几分钟之后,所有的服务器都出问题了。并没能如预期想像的,损坏一两台不影响正常工作。

这个案例中,实际使用了6台服务器做mysql集群。每台服务器的数据是完全镜像。可以想像,如果6台服务器中有一台出了故障,所有的压力就会被平均分到其他5台上。在集群负载大的情况下,增加1/5压力,很可能让另外的5台直接被压垮。这时候发生的情况,就好像最近株洲的塌了的大桥一样,一个桥墩一个桥墩,好像多米诺骨牌一样,一路塌了下去。

从任何角度看来,这都不是一个好架构。别说稳定了,连基本的服务都保证不了。

对于2个案例,我给出的解决方案都是先做切分,然后再分别进行优化。首先可以按照业务切分,把现有服务器分成多个组。对于web前端和mysql集群,每组2~3个足够应付大多数情况。每组服务器数量太多,不仅造成单点故障的概率增加,也容易在自身的数据同步中消耗大量的资源。

比较以下两张图:

改造前:

改造后:

之前的结构是所有web服务器放着同样的应用,所有数据库服务器放着同样的数据。改进后的结构是将整个系统按照服务内容和性质拆成了几组。从ServiceA到Service n 采用了不同的配置方式,简单的只用一组机器,复杂的用多组。在实践中,其实还可以分的更细致,甚至几个Service公用一组机器。(之前beta技术沙龙,有道讲他们的阅读器架构的时候,也提到了类似的办法)

后者的好处很明显,最明显的:

1 可以有效的使用资源。比如:服务A是低负载型的,并非核心业务,可以给予较少的资源。(较少的机器,较差的机器) ,而服务B是核心服务,需要全力支持。
2 可以简单的监控性能。所有服务都混在一起的时候,碰到性能问题很难下手分析。很有可能是最不常用的服务A中有bug,导致了整个集群性能低下。寻找这种bug要耗费大量的人力和时间。
3 易于备份,升级时切换服务平滑。
4 为下一步优化做好准备。综合以上2点,下一步可以集中资源,对重要服务专门进行优化,保证稳定和性能。


在以上的例子中,我们可以看到,专门对重要服务B进行负载均衡,无论在成本上还是性能受益上都比对整个应用进行处理划算的多。

除了根据明显的业务边界进行切分,还有很多种方式,比如按照时间,按照读写频率等。


这种思路也不仅仅应用于web server/database。比如,设计缓存的时候,也应该进行一定的切分。以squid为例,应该把文件大小差不多的文件放在一个squid中,把更新频率接近的文件放在一个squid中。这些都是让架构清晰,性能提高的办法。

从系统设计的初期,就应该贯彻这个思路,用子域名来标记不同的服务(img.yourdomain.com,bbs.yourdomain.com...),比用虚拟目录的方式(www.yourdomain.com/img,www.yourdomain.com/bbs)更便于切分。

很多人咨询过我这个问题,不过他们都是从seo和产品方面考虑,从来没想过这是和技术相关的。如果所有的应用都放在www之下,未来一旦想把一个服务挪到另外一台服务器上,就很困难----不是没有办法,只是难度比较高。 子域名只需要设置一下域名解析,把要挪走的服务指向新的ip,几分钟就搞定了。

这个办法的实质,仍然是降低系统耦合度。必要的关联数据,宁可采用http请求的api形式,也不要跨数据库进行查询。http请求可以很方便的跨服务器,可以设置页面缓存等,跨数据库请求则只能在数据库上花力气,最后又退回了对全部数据做数据库集群,master/slav的老路上e(当然,这不是一条原则,还是要根据应用的具体情况而定)。诸如此类,尽量要将思路放远,不要贪图眼前的小得失。(常见的说法:数据库怎么也比http快吧...这没错,不过要看应用场合)

能在一个单一的服务中承载尽量多的数据和请求,这当然是所有技术工作者不懈的追求。在此之前,如果先进行合理的切分,再令每个服务的性能最大化,一般都能得到更大的好处。简单是美,简单就是力量。

May 8, 2009

在你自己的软件中应用snmp和agentx协议传递信息

我曾经想自己写一个udp server来接受我的服务器程序发回来的状态,然后做跟踪和统计。后来被Fenng批评为自己重复造轮子。于是转向试图利用snmp协议来做。

用snmp有几个原因:

1 snmp是基于udp协议的,具有udp的所有好处(数据状态这种东西,用tcp是不合适的)
2 snmp比较成熟,周边的软件非常多,后面可以很方便的处理收集数据,绘图等等一系列的功能。(rrdtool/cacti....)
3 因为我们要监控的服务非常多,所以希望能设计一个尽量免配置的方案,snmp的树型结构正好获取节点下面所有的被监控值。


snmp在网管系统中有很大规模的应用,但是似乎没什么人用来监控软件服务。而,目前我们知道的对snmp和cacti的应用,似乎大部分都只停留在了用来监控服务器状态(mem,load average,net io,disk)上,有点可惜。

在这篇blog里面,我提到的用法应该不是最正宗的,但是有效。其实很多大厂商甚至自己修改snmp协议,做出自己的分支协议来。比起他们,虽然我做的不太正宗,但是也不为过。

我要实现的东西大概可以用这张图来描述:

概括起来:很多个服务分布在很多台机器的很多个端口,所以我们需要在每台机器上放一个snmp代理,得到他们的状态,然后再统一通过snmp协议获取这些状态。由于服务太多了,必须要免于配置,这就更显示出了snmp协议的优点。

扩展snmp有几种方法,不过综合起来,我认为最简单和灵活的还是agentx协议。agentx完全是独立在snmp server之外的,可以一层层的套起来。其他的协议要么要在snmp上加东西,要么要写mib的模块文件,都相当麻烦。

不过,最常用的snmpd,反而对agentx支持不好。至少他们自称还是试验性质,不建议用。我试图在snmpd.conf中允许agentx协议,也几经挫折。最终决定找别的方案。似乎大家都习惯把snmpd当作唯一的snmp server,其实类似产品还有很多,snmpd时间比较久,但我认为并非最好的。在ubuntu的apt源里面,都有几种其他方案可用。

当然,由于我并非希望取得系统的硬件状态,所以我也并不一定要找一个通用的产品。所以最后我决定用jagentx(http://eden.dei.uc.pt/agentx/)。

jagentx实现了snmp v1 协议和agetnx协议,一般来说足够用了。他们网站首页的那张图,很好的说明了agentx方式的系统结构。

jagentx提供了比较友好的api和demo,不过还是有一些bug,我做了一些修改。

1 jagentx的master(相当于server),对于master mib的读取,只能支持snmpwalk,不能支持snmpget。其实是一个状态设置错了。
在 Master_Engine.java中

if (!any_subagent_queried){
int error_index = 1;

int error_status = Pdu.NO_SUCH_NAME;

后面,加上

if(value != null)
{
error_status = Pdu.NO_ERROR;
}
就可以了。

2 是关于session的。如果客户端退出,没有注销,就要等自己的session超时,不然连不上。这个在实际应用中很容易出问题。服务不正常退出的情况发生几率很高。于是我做了一些改动,可以通过sessionid来注销其他进程。这样确实会造成一些安全问题。不过可以用防火墙或是其他方式弥补。总比之前要等超时好。
改动的地方分布在几个文件里面,就不挨个列举了。

本来我给jagentx的开发者发了个邮件,告诉他这些改动,不过被退信了。所以我就把代码放这里,有需要的可以下载

修改了这两处之后,这个东西很就好用了,jagetnx网站上的文档有demo代码,可以参考。

为了让我的山寨服务器正规点,我还是去申请了一个enterprise number。申请的方法是到iana 的申请页面添一张简单的表格,几天之后能收到回复,如果顺利通过,就会分配你一个数字号码,以后这个号码的snmp消息就代表你的组织的了。全部企业的enterprise number可以在http://www.iana.org/assignments/enterprise-numbers 这个页面查到(很大的文本文件)。可以看到,我们的银杏搜索ginkgotek是33364。

所以我们的snmp消息就在1.3.6.1.4.1.33364这个节点下面,我用snmpwalk就可以获得所有节点,包括新增的,这样就达到了免配置的目的。当然,到目前为止,我们还只是自己用于服务监控。


一切就绪之后,就可以选择你喜欢的工具来收集监控数据和画图了。比如常见的cacti。

这里是一张隐去了部分信息的cacti做出来的图。数据是从前面写到的这套东西收集上来的。

总结起来,这种办法适应于:

1 你有一个长期需要稳定运行的服务程序
否则你根本不需要监控

2 该程序有大量实例在运行
否则你用脚本把数据输出到snmpd都可以,没必要和我一样希望免配置

3 你不希望通过http或tcp连接来干扰该程序运行
否则你没必要用udp

写完了这篇blog,我也很希望能够侧面证明ginkgotek在保护客户利益上做的比别人多一些,选择我们的服务是可以放心的。

April 3, 2009

beta沙龙-手机之家架构的发展和变化

这次beta沙龙请了高春辉的团队来讲他们的经验。本来我是希望老高讲,不过他说最近的系统主要是许超前在带人开发,所以实际的演讲人是许超前。

国内最早让大家意识到网站的发展阶段的文档大概就是于敦德翻译的LiveJournal发展历程的ppt。这次许超前的演讲非常类似LiveJournal的那篇。


手机之家用了7年时间,发展到1000万以上用户,3000万以上帖子,1.1TB附件,每天780万以上的PV这个规模。这个数字虽然比不上大型互联网公司,但是对一个只有几个人的技术团队,已经是一个很令人骄傲的数字了。

LiveJournal在发展中一边用着开源软件,一边造轮子,最后造出来了memcached这个简单而强大的工具,最终成了这一代网站开发离不开的东西。

这次演讲有意思的部分就是从memcached相关的事情开始的。

一 关于memcached的应用和管理。

memcached确实是个简单,好用,见效快的东西。不过简单也有简单的问题,程序员各有各的习惯,结果导致key很不规范,用什么方式的都有。这个问题恐怕用过memcached的人都深有体会。当然,用开发规范来限制程序员的行为是一个办法,但这不是技术的方法。技术的方法,是增加一个层。于是手机之家开发了一个cache管理器,把程序员和memcached隔离开,由这个层来统一管理缓存。

这是个不错的思想。当key被一个中间层接管了之后,事实上就可以给被缓存的对象实现更复杂的结构了。memcached是扁平的,只有一层。数据保存的方法仍然可以保持一层,但是通过对key的结构设计,就可以实现多层的结构,甚至在层和层之间实现继承关系,或是树结构。手机之家称之为namespace。通过这种方式,可以批量的管理和控制缓存对象。

换言之,索引结构可以复杂,但存放对象本身的地方是个key-value型的。friendfeed关于mysql应用的文档也提到了这种方法。用这种方法,可以在一个比较小的索引库中进行各种操作,无论是遍历还是查找,甚至处理一个节点下所有的值。最终得到key-value库的key。这个思路应该可以用于很多地方。

手机之家在cache上下的功夫很多,演讲稿中很多页是在讲cache系统的演变。对他们的应用方式来说,如果cache被击穿了,所有的压力到达了数据库,那么性能就会急剧下降,所以对cache的管理变的尤其重要。手机之家的这种尝试确实解决了很多问题。

二 仍然和cache有关

完成了前面说的cache管理器,他们还想更近一步,把数据访问,缓存,尤其是对库的切分整个包装起来,给程序员一个透明的接口。这样程序员不用关心到底存在多少个库,怎么分布,是否需要从cache中取数据等等细节问题。

这也是个常见问题。所有从数据库取数据的地方都要先从memcached读一次,代码很难看,也很罗嗦。这个包装完成之后,这就是一个能提高生产力的工具了。直接有效,也确实节约了开发成本。这个被他们称做DAL,很俗的名字啊:D

三 这样用java

php用来做以上的2个部件,效果并不好,效率很低。事实上php就不是干这个用的。最后他们用java+nio实现了一个性能不错的服务。把所有问题都解决了。

用在网页上java/jsp确实不如jsp,但是用来做服务器还是很合适的。我询问过他们是否在GC的时候遇到性能问题,答案是目前还没有。如果稳妥起见,对于这种方案,我更愿意采用C,不过考虑到团队情况,和java程序员比C更容易找到,手机之家用java来开发也是一个折中方案。

总结手机之家经验分享的意义,我觉得在于,我们很少有机会看到一个网站从0做到接近千万的PV。就算在一个大的互联网工作过,所看到也只不过是冰山一角,很难有操刀动手的机会。手机之家的成长用了7年,老高始终在带着团队做优化。这种经验很难获得。

我们常常讲,做网站好像爬山,一个陡坡,一个缓坡。我见过很多网站在50万pv之下就被技术卡住了。很多很多的公司,就在某一次上坡的时候死了。

沙龙结束后,晚上,我和老高在gtalk上又聊了很久。我觉得他们做出来的这套方案,对很多公司都有帮助。至少跨越几个困难阶段。这就好像memcached带来的,在没有memcached的时候,很多网站抗不住10万这个级别的pv,有了memcached,哪怕用的很糟糕,50万这个级别也能到了。老高这个方案或许能让很多网站进入"百万俱乐部"。

说起来做互联网,中国人比外国人难很多。我们面对的用户规模比他们大的多,但是广告价值小的多。我们做一个网站,可能在第一年就碰上了第一次性能极限,但这时候钱还没有影。外国人可能3年才到达,但是人家已经赚了不少钱,也有足够的资源来解决问题。对中国的互联网来说,有一些这种解决方案确实很有帮助。

当然,未来还是未知的。老高和手机之家是如何期待这个产品的未来的?是开源,还是做解决方案?不管怎么说,至少我相信,他们做一个不错的轮子,应该不至于只给自己用。

更细致的还是请看他们的ppt吧

手机之家还在招聘 PHP/Java 人手,有意者给老高发邮件: gaochunhui@gmail.com


其他相关的:

大辉的:手机之家的架构分享
余晟的:老高之野望
老高的:与"手机之家新系统介绍及架构分享"有关

March 23, 2009

弄了个让csdn blog输出全文的东西

某一天,喝了半瓶大二。然后我就想,我这到底算能喝酒还是不能喝酒呢?前几天喝的还没这个多,怎么就把笔记本丢了呢(这故事回头有时间再写)?

很久以前,喝多了酒,我就喜欢随便找个不规则物体,用微积分来算体积玩。后来就不玩这个了。现在比较喜欢写点好玩的程序或是找个命题来算概率。

想起来xuyou推荐过g9的blog,而csdn blog因为不输出全文,基本被我排除出了订阅列表。于是就顺手扒拉了一个小程序,用来把全文抓出来,塞在rss里面,好订阅用。

结果今天比较清醒的看了看那天扒拉出来的程序,竟然还真能用。

所以就放这了。

这东西是基于magpierss做的。没什么特别的,写了一个基于token的解析器来解析文章,把需要的部分抓出来。这种方法在做语法分析器的时候很常见。当然了,您不能对醉鬼要求太高,所以我这个解析器只分析了单层标签,如果想支持多层的,只要加一个栈就可以了。我暂时用不到,不加了。

说起来php真是个不错的语言,简洁有力。字符串处理很强大。计算机这个家族里面,很大部分的机器是在做字符串处理和数据库工作,还有很大的一部分在做排序工作。所以一个字符串处理功能强大的语言就是最容易流行的语言。

我做了几个我要订阅的rss:

g9的: http://blog.devep.net/tool/magpierss/rss.php?url=http://blog.csdn.net/g9yuayon/rss.aspx

阿朱的: http://blog.devep.net/tool/magpierss/rss.php?url=http://blog.csdn.net/david_lv/Rss.aspx

用法很简单,就在后面加上csdn blog的rss地址就行了。你可以下载代码回去自己搭一个玩玩。

代码在:http://blog.devep.net/tool/magpierss_withparser.tgz

很简单,很粗暴,很粗糙,也没什么版权,拿去随便干点什么用吧。

March 15, 2009

rss解析器magpierss笔记

magpierss是一个不错的rss抓取/解析工具,我本来打算自己写一个php的rss解析工具,不过试了一下magpierss,觉得还不错,能满足需要。

顺便看了看代码,记点笔记。

1 乱码问题
magpierss过去时常出现乱码问题,从0.7版本解决了这个问题
Version 0.7
-----------
- support for input and output charset encoding
based on the work in FoF, uses iconv or mbstring if available

0.7之后的版本可以指定输入和输出字符编码,然后使用iconv或mbstring函数进行编码转换。这个工作在 create_parser 函数里面完成的,如果两个函数都不存在,可以在rss_fetch.inc 中让MAGPIE_DETECT_ENCODING为false,不检测和转换编码,否则就会出错。

一切正常的情况下,把rss_fetch.inc中MAGPIE_OUTPUT_ENCODING定义成你需要的输出编码,比如UTF-8: define('MAGPIE_OUTPUT_ENCODING', 'UTF-8'); 就可以获得正确的输出结果了。

2 抓取方法

magpierss用了Snoopy作为http客户端来抓取rss。这个库比较完善,支持https,支持gzip。

需要注意的是,Snoopy用exec命令调用curl,然后返回结果,而不是使用编译进php的curl函数。默认的路径是"/usr/bin/curl",如果这里没有curl或是没有执行权限,就可能失败。不过只有https需要用到curl,普通的http访问是用fsockopen的。

同时,Snoopy可以使用代理服务器,但是magpierss没有使用,如果需要可以在rss_fetch.inc中的_fetch_remote_file函数里面添加$client->proxy_host 和$client->proxy_port。

3 缓存

magpierss设置了一个默认3600秒超时的缓存。在./cache下放了一堆文件,文件名是md5之后的url+MAGPIE_OUTPUT_ENCODING,格式是php的serialize。

所以cache目录要可写。超时时间在rss_cache.inc的var $MAX_AGE = 3600;这行设置,也可以在创建cache对象的时候设置。


综合起来,这个库还是不错的,优点很多,也考虑了主机的各种情况,兼容性很好。函数形式的接口,很容易调用。不过应该先用head来取rss的http header,根据Etag来判断是否抓整个页面回来,这样效率还能再有提高。这个改动之后,cache就可以存在更长的时间,而不是一个固定的3600秒。同时我比较想把文章保存起来,以便以后用,这就需要数据库了。

我将来应该会基于这个东西发布一个新版本,把我想要的功能加进来。

March 9, 2009

关于有道阅读的beta技术沙龙

第一次beta技术沙龙邀请的是有道阅读来分享他们的技术和产品设计。阅读器这个东西听起来不大,但是技术含量并不低。

下面这个图是我根据当时听的情况随手画的,不一定很正确,但是应该基本能够看到产品的架子。简单说来就是前端,web server ,缓存,存储。当然了,任何网站都得这么设计架构。

From blogimg

大概可以分成几个部分:


  • 存储(这里主要说的是文章的存储),是基于关系型数据库和key-value库的结合。这里可以简单理解为mysql+bdb,关系型数据库负责保存索引和基础信息,占用存储最大的文章全文在key-value库里面保存。使用的时候,根据查询结果,通过key到key-value库中取回全文。基本可以保证灵活性和效率,也是相对比较廉价的方案。

  • 缓存直接使用了memcached,memcached现在已经成了网站标准配置,在谁的架构中如果看不到类似的东西存在,反而会有点奇怪。有道阅读采用了一组memcached作为缓存,特别值得一提的是,他们在缓存中保存了24小时内所需要用到的全部数据。据说是经过多次实践的结果。我估计这个缓存的尺寸也不小,而且未来还会增加,不知道未来会通过什么方式来处理。但至少目前的一段时间内是够用了。围绕memcached的相关解决方案和patch已经相当成熟了,看起来有道也没有进行特别多的定制和改动。

  • web的部分比较值得一说。为了保证升级版本的时候,服务失效时间尽可能短,web的处理部分被拆成了一系列的Service,每个Service至少有一对实例承担正常的请求,在升级或是对某些模块进行改动的时候,可以依次对一对(或更多)实例中的某一个进行操作。这样整个应用并不会因为对某个Service的操作而停机。新旧模块的切换只在很短的瞬间(有道称之为"影子服务")。这种思想很类似SOA,不知道实际用起来感觉怎么样。我觉得接口的定义难度还是很大的。否则新旧Service切换的时候,其他相关的Service数据结构一旦不兼容,后果可能很严重。SOA的标准那么复杂,主要就是为了保持兼容和接口标准吧。当然,在一个产品之内,这种接口的定义相对容易点,毕竟复杂程度还是有限的。

  • 用户界面部分使用JQuery,实际用起来响应速度相当不错。JQuery也是成熟的标准产品了,还是值得推荐的。
  • 最后就是备份,除了几层日常备份,全部数据还在网易的存储系统上有一个更全面的备份,目前已经有20T的数据。在大公司内做产品就是有这种好处,轻松的解决了最困难的问题,实在令人羡慕。

总体上看,这个结构很规矩,没什么奇怪的地方,大量采用了现有的开源产品和方法。很务实。成熟的开源产品确实让开发工作变的简化了很多。

时间关系,还有很多细节没有说清楚。比如Service接口怎么定义,如何标准化,公司内部怎么来管理这些接口和文档,每天大量的文章如何抓取回来,如何管理等等。将来有机会再学习吧。

下面是现场的Keynote:。除了上面说的技术方面,在产品方面还有很多值得看看的。尤其是"12个有趣的发现",这些发现是来源于有道阅读的数据统计的。做产品的同学们可以注意看看。

ppt在此

January 12, 2009

力破困局千万重-技术咨询手记 1

一 从何开始?


2006年10月,我和tiny创立银杏咨询,开始提供技术咨询服务。到现在,已经2年多了。时间过的真快!

这2年中,我们逐渐开始了站内搜索SAAS服务 ,但是咨询的服务也仍然在做。积累了2年,见过了太多正常的,古怪的,常见的,重复的,白痴的问题,现在,我打算开始慢慢的写出来,给别人参考。

当然,惯例声明。本文所述一切情况,并不来源于某个特定的团体或公司,是我们2年工作经历的组合,也就不用猜测到底是谁了。万一有雷同绝对巧合:D

记得我跟Fenng聊到过,写技术,尤其是优化和架构相关的文章,总显的很无聊。因为懂的人不说也懂,不懂的说了还不懂。新手的php程序员们不分场合的迷恋模版和MVC,而全然不知其所以然。所以后来我那个《让你的网站快100倍》,就没有继续写下去了。还是跟着实际的案例来看,会比较有趣一点,你说呢?

可以说,我们处在一个非常幸运的行业中,因为这个行业发展的太快了,人才缺口实在太大,大到历史上任何行业都没有过的程度。很多公司甚至糟糕到了没办法判断一个人是否能够胜任的地步了。某个公司的创始人跟我说,我们最大的问题是,根本没办法鉴别招聘来的这个程序员是高手还是忽悠。

这就是我们面临的情况。无数的新手,无数的忽悠,就这么草草上阵,开始为无数伟大的构想和商业模型搭建基础。如果不伟大的项目还好,早早就死掉了,也没有后面的问题了。伟大的项目,就会突然碰到瓶颈,这时候,大家都傻了。

我们的客户往往就是这类,他们是伟大的项目,所以他们碰上麻烦了。

“好了,咱们从何开始呢?”,会议桌对面的客户满脸的痛苦,想了半天,说出来这么句话。

我和tiny互相对视了一下,说:“就从你最头疼,影响最大的地方开始吧”。

这是一个媒体网站。按道理来说,媒体网站不大应该碰上性能问题,媒体网站更新量往往并不大,访问量也不大,但是人群比较集中,价值也比较高。这是网站里面活的最潇洒的一类公司,他们用数量小,价值高的pv获得可观的广告收入。对于硬件投入也比较舍得。但是现实总是这么荒唐,这种从来不愁钱的网站,碰上的问题最多。

他们最头疼的问题,就是网站会突然挂掉。但是找不到原因。

好吧,就从这里开始。

背景资料:本网站用java+jsp开发的。基于某古怪的架构(架构的古怪之处随后慢慢说),每天更新量只有几十篇文章。访问量在百万以下的级别。没什么交互。

看起来很简单?清点一下系统拓扑图可不得了,竟然有10多台机器趴在机房里!而且还使用了CDN。饶是如此,仍然摆脱不了3天一小死,5天一大死的噩梦。

我们开始的方法很简单。查log。我们希望通过分析访问log,来获得系统各部分的负载状况,从而进行分析,找出薄弱环节。

log呢?竟然所有的服务器都没有记录log。

很多人认为,记录log会消耗更多的资源。其实并非如此,如果log非常大,比如几个G,确实会让写入的时候变得有一些慢。但是大部分情况下,如果定时清理,或是做了切分,不应该对系统性能造成什么影响。

平时不记录log是可以的,但是很多情况就错过了,将来需要回溯寻找问题的时候,就没有了依据。这很糟糕。google analytics很好,但是它是用来分析统计情况的,不是用来寻找系统问题的。你完全可以用google analytics来代替awstat之类的分析软件,但绝对不能替代原始的log。

针对这个客户的情况,我们记录了几种log。

1 java application server (tomcat/...) 的access log 和error log
2 apache 的access log和error log
3 mysql slow query
4 sar 记录系统活动
5 vmstat 状态 ,没什么现成的工具,我写了个脚本,供参考。


采集这些数据就可以进行初步分析了,修改好了所有配置,部署好了所需要的脚本,剩下的就是等待了。

等待出问题。

我心里偷偷的想,如果客户知道我们从这一天开始盼望着出问题,会不会恨我们.....

September 30, 2008

一个用来监视和收集服务器某种异常状态的脚本

有一台服务器,间歇性出现io急剧升高的状态,一直没找到原因。所以做了这个脚本。一下就找到了。放在这里共享给大家,谁有用自己改改用吧。


monitor.sh

#首先从vmstat中取得需要的数据。vmstat命令的结果如下:我们需要最后一行的数字。本例中是第一个r。(r: The number of processes waiting for run time.)
#procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
# r b swpd free buff cache si so bi bo in cs us sy id wa st
# 0 1 0 6350084 206432 5896400 0 0 0 77 6 8 1 1 91 7 0
#
vmb=$(vmstat | tail -1 | cut -c5-10|cut -d" " -f1)
# r 大于5的时候开始做log
if [ ${vmb} -gt 5 ]
then
date >> /root/all.log
vmstat >> /root/all.log
netstat -anp >> /root/all.log
ps -aux >> /root/all.log
#分别输出 时间,vmstat的全部数据,netstat的连接状态,所有任务状态等。你可以自己加需要的。
logdate=$(date +%Y%m%d)
echo '---------' >> /root/all.log
tail -100 /var/logs/http-access_log.${logdate} >> /root/all.log
#这部分是为了取得异常时候的访问log。因为我的日志是带有日期的,所以先用date命令来生成当前的时间
fi


然后放在crontab里面,每秒运行一次就可以了。很简单的脚本,不过很实用。一下就找到了问题是sendmail发送大量邮件造成的。

钱宏武老师对此亦有贡献。

March 10, 2008

从SOA说开去

余晟昨天跟我讨论SOA问题。我说了说自己的观点。后来想想,应该整理一下。于是就有了这个blog。关于SOA是什么,请自行google,不多说SOA。

首先值得肯定,SOA试图解决的问题是正确的。SOA所关注的问题,是确实存在的。其实说穿了,所有的开发方法,项目管理方法,框架,工具,无非都是为了解决这些问题。抽象点概括出来不过就是可扩展,易维护,稳定性高,可复用这么几种而已。

所以说,SOA所希望解决的问题,说回根本上还是这些,其实这是废话,因为计算机诞生起,人们就在为了解决这些问题不懈努力着。不错,SOA是一种尝试,但是在众多厂商的推波助澜之下,这种尝试变的有点吓人,看起来似乎不再是一种尝试,而是解决问题的银弹。这当然是不对的。

使用计算机,最终还是为了解决现实世界中的问题。而用计算机,需要用计算机世界的逻辑。这个工具的逻辑和要解决的问题域模型非常不吻合。所以试图在计算机中模拟真实世界,就成了一个难题。(题外话,所谓面向对象,其实可以看作人们试图在计算机世界中重现现实世界的一种努力)。因为这种思维转换的难度,使得程序设计变成了一门专门学科。回想历史,只能写机器码的时候,人们利用计算机解决的是需要快速获得运算结果的大数据量运算的问题,比如导弹的飞行轨迹之类。现在需要解决的问题种类就很多了,运算量也越来越大,当然需要更好的工具。

这是一个有趣的循环,有了更好的工具,就可以解决更复杂的问题,而更复杂的问题又让更好的工具变的束手无策。想想你在写程序中遇到的困难,有多少是问题本身的困难?有多少是写程序这件事本身的困难?换句话说,写程序本身在某些情况下变的比要解决的问题更加困难。这里余晟概括说:好像你会写文章,但是不会用某支笔写字。

我们来看看语言的抽象层次。从机器码,到C语言,到更高级的语言,到动态语言,从底到顶,语言越低级,越接近计算机世界,越高级,越接近于现实世界。我常常说,C很好,不过只适合写系统级别的东西,不适合写应用。一样是因为这个原因。C是对计算机系统的最小抽象,所以用来描述系统级别的问题很轻松,但是用来描述现实世界的问题就很吃力,好比用笔画来写字没问题,不过只用笔画来写文章,那就累人了。

当然,理论上任何语言都是一样的。你当然可以用C来做网站。不过因为模型的不符合,这会让你付出更多的时间和钱,还有失败率。

SOA试图通过一系列的描述来解决接口混乱和扩展困难的问题。同样的情况发生在webservice上过,发生在ejb上过。同样的厂商喧嚣也同样在webservice和ejb上出现过。(甚至,厂商们还往往在试图让ejb标准变的更加难用而不是更加容易)与其引入更复杂的问题,不如让工具本身更富有表现力。这是我近年推崇动态语言的原因。语言够简单了,阅读就容易多了。想像一下,一个python写的程序和一个汇编写的程序,哪个更好懂,更好修改?

我认为,目前的程序设计,只要解决了3个问题,就可以让很多普通用户进行开发,解决自己的实际问题了。这3个问题是:数据类型和函数,内存管理,进程管理。可以说,这3个问题都是目前计算机语言中和现实世界的经验最不符合的,所以导致了理解困难,没经过一定训练的用户难以完成这个思维转换。

SOA试图让接口描述和数据统一,这种统一的办法是xml。而如果开发足够容易,代码本身比那一堆包含无数规则的xml描述更富有逻辑和表现力,更容易看懂,这时候,还有人想去看xml吗?

这就好比开自动挡的车,你只要关心路线和目标,操作本身可以不用那么关注。你要完成的任务是解决问题,而不是为了学习SOA。

因此,了解SOA的思想,并应用于实际开发。但是,最好别被那堆规范吓倒。要知道,SOA的思想和SOA绝对是两回事。

July 16, 2007

让你的网站快100倍! (2) web程序的流程和层次

我面试程序员的时候,最爱问的一个问题是:“请描述一下一个php(或.net/jsp...)页面被用户访问后,服务器都做了什么。”

这个问题看似简单,但是很多熟手都说不清。这也就说明了很多程序员并不真正理解web程序是如何工作的。不懂这个,就分不出层次,自然也就无从下手优化。这次我们先要把这个问题说明白了。

静态内容当然是这样。


有程序的就这样



有数据库的就这样



多个数据库作了复制或是集群或是负载均衡的就这样。



多个web服务器作了负载均衡就是这样


类似的东西还有很多。不过本质是一样的。都是用户请求到webserver,经过相应的处理模块(静态直接返回,cgi运行程序,动态脚本运行,虚拟机执行字节码生成html代码),将结果返回,这个过程中如果需要数据库,程序会连接数据库处理,取回结果。这个简单的过程就是web程序的本质。

无论多么复杂的结构,都是这些过程的组合。通常顺序也不会发生变化。知道了这个,我们回头看后面几个架构,就会发现他们做的事情都一样,就是把负载分开。当然这里分散负载的方式有很多,但原理无非就是2种,一种是分散开的各节点内容一样,所谓集群或是分布。这种做法好处是可以有通用产品。因为模式是统一的,就是复制出来多个一样的节点而已。另外一种是拆分服务,把不同的服务拆开到不同的机器上。这样做的好处是效率提升更大,每个部分都可以再进行单独优化,比如把负载最重的部分复制多个节点出来分散压力等等。后面我们讲到架构设计的时候再细致讨论这些问题。这里只做常识介绍。

负载问题的解决,常用的也是两种办法,一是开源,二是节流(其实所有问题解决都是这两种办法,企业管理也好,个人财务也好)。放到我们这个特定问题中,所谓开源,就是提高单个部分运行效率,让该部分能负担更多的工作。比如优化一下程序,优化一下数据库,都属于此类。所谓节流,则指降低对高资源消耗部分的使用次数。通常采用缓存的方式来处理。

我们沿着一个web程序的运行过程来看看具体的东西。这时候,我们先忘掉所有的负载平衡,集群之类的东西,集中精力来解决一个最简单的应用过程中的性能问题。

整个层次用图表示就是这样的:

蓝色是必备的层次,绿色是后期可加入的缓存,黄色是可以进行优化的部分,黄色的比例是一般情况下优化能带来的效果比例。看起来很简单?但实际做起来也没那么容易。

1 用户访问:我们当然可以通过降低用户重复请求同样的内容的方法来提高用户浏览速度和降低压力。
两种办法。一种是利用浏览器缓存,正确的设置http header中相关设置,让浏览器不重复请求没变化的内容。一般来说图片倒是可以这样做。页面也可以作一些,不过考虑到pv就是广告费的问题,还是让用户多多直接访问页面吧:D
另外一种办法是利用js,将部分数据存放在js脚本里面(js会被浏览器缓存),或是存放在cookie里面,用js取回。由此降低用户访问次数。

虽然有一些效果,但这些办法解决不了太实际的问题。所以不放在最优先考虑位置。

2 http server
用户的请求到达了web server,在这个层面,可以去硬盘读取静态文件返回给用户,也可能交给cgi程序继续处理。我们注意到,无论哪种处理方式,返回给用户的都是一个已经完成的html页面(或文件)。换句话说,就算是动态脚本,也是把处理结果生成html文件返回用户。如果我们能不重复这个生成的过程,那么对后端所有模块的压力都降低了。这个办法就是反向代理缓存。在http server之前使用。

3 cgi/vm
应用程序请求到达了cgi程序。那么最直接的考虑就是提高程序运行效率。不过,程序运行效率的提升多在几个毫秒的级别。除非程序非常糟糕,否则提升余地不大。而我们通常所感觉到的“程序响应慢”的原因,其实往往是在数据库上。

当然,这个层面会有一些特定的解决方案,比如zend对php的那些加速方案。(其本质仍然是不同层面的缓存)

4 database

终于到达了数据库这层。脚本和cgi程序访问数据库,获得数据才能生成html页面返回。这层的负担很重。

数据库优化是有效的。设计的好的数据库结构,搭配正确的sql语句,可以比最糟糕的设计提高数百甚至上千毫秒的时间。访问量大的时候,这个累计效应非常明显。

数据库设计往往决定了性能。鉴于目前关系型数据库的普及,很多程序员就把数据库当作了唯一的存储解决方案,任何东西都存放在数据库里。这当然是不对的,存储要结合应用特点,数据库不是万能的,很多操作在数据库中耗费资源巨大而性能非常低。例如全文like的搜索就非常消耗资源,远不如采用倒排索引的查询方式。

除此之外,数据库层面也可以进行缓存,例如memcached,或是在程序中缓存部分数据等。

5 高级优化

这些做起来就有些难度了。当所有性能都压榨完,我们只能回到web server和cgi程序本身。所以大型网站往往使用自己开发的web server(自行定制apache等),采用裁剪过的php库。

在我们还没有解决完其他部分之前,暂且不考虑这个。


顺着这个流程看,最有效,最值得做得事情显然是2和4,这些都是对系统架构改变不大,但是效果明显的方案。因此,下一章我们就从反向代理的应用说起。


ps: 另请转载此文的,请连图转走,或是留下本文链接。以免影响别人阅读。谢谢。我不在乎这点流量,所以转也就转了,但是不能因为少了图让别人看不懂。


tinyfool对此文亦有贡献

July 9, 2007

让你的网站快100倍! (1)

让网站快100倍,看这个题目,就知道我们在讲优化。没错,正是网站优化。

优化是银杏咨询的一块重要业务,也是我目前工作的重点之一。而优化这个东西是非常体现短板效应的,对知识的要求非常全面,而且要融会贯通,真正理解才能做得好。相关全面的资料很少,市面流传的东西谬误又颇多。所以我考虑把我这几年工作的经验,以及这半年来银杏咨询碰到的问题总结一下,写这个系列的文章,希望能对同行们有所帮助,抛砖引玉是最好不过的。

这系列文章里面,这第一章自然是说说大体的情况,说说常见的错误思想,统一了思想才好进行。
第二章我会讲解web应用程序的常见架构,然后分析哪里是薄弱环节,哪里有可能改进,哪里是关键点,形成一个总体认识。
第三章开始将一些实用技巧和工具,思想。
后面的还没想好写什么。且写且考虑吧。

这是第一部分,先统一思想。

1 这个时代,优化还有没有意义?

计算机芯片性能很低的时候,优化是万分重要的。性能就意味着产品质量。所以很多C程序员宁愿放弃程序的可读性,也要变态的追求性能。早期作游戏的家伙,甚至把乘法都写成加法,以期多获得几个指令周期的性能。

在这个计算能力无限廉价的时代,优化所代表的意义其实已经不同了。互联网服务是性能的压榨机。相对传统的企业应用,互联网服务的逻辑简单,结构扁平,而性能要求尤其突出。同样,这个时代多机集群,负载平衡,不再是奢侈的东西,已经变成了寻常物件了。常常听到的一个观点,就是性能糟糕不怕,与其浪费时间优化,不如去买更好的机器好了。反正硬件比程序员便宜的多。

这种极端的观点是害人的。在目前,硬件仍然是有极限的,硬件升级带来的性能提升最多只能达到几倍的程度。而集群技术,且不说对程序的要求更高,我们就说在可实现的情况下,如果我的应用比你的性能高100倍,就意味着我用1台服务器完成的工作,你需要用100台。硬件成本暂且不论,那么管理成本呢?用什么样的技术手段去管理100台服务器?用多少网管来管理100台服务器?所有规模快速上升的模式都会遇到这个问题,就是管理成本达到了极限。而任何东西一旦达到极限,就往往不能简单用钱解决了。

所以,为了避免达到极限,我们还是应该尽量去提高单机的负载能力。对于中小网站,这更是意义非常。或许长期可以用1,2台服务器解决问题,可能永远都不需要投入相对昂贵的集群技术。这对成本降低大大有利。


2 优化是相对长期工作

做优化过程中,我们最常见的就是“按下葫芦起了瓢”,解决了A点的性能问题,B点立刻成了主要矛盾。解决了B点,C点又来了。优化了很久,服务器的负载一点都没有降低。没错,因为产品特性,这种短板先漏水的现象非常常见。甚至可能做了一个月的优化工作,看起来完全没进展。比如,B点消耗系统50%的性能,但是因为消耗20%的A点造成的速度慢,用户从来不能打开B点所在的页面。这样解决掉A之后,反而暴露了B,性能反而还不如过去。

这种情况都可能碰上,所以要认定,优化是相对长期的工作,要花时间,认真细致的进行。

3 让数字说话

科学是不靠猜测的。所有工作开始和完成,都要用数字来衡量。数字最有说服力。眼睛和经验通常不可靠,甚至会让你做无用功。

4 优化是综合问题

优化不仅仅涉及到程序,数据库,还有系统架构,甚至产品设计。很多时候需要综合考虑,设计方案。这不是一个简单的问题,而是相互牵扯和影响的一系列问题。所以一定要细致的分析,不要想当然。设计方案要合理,力求用最简单的方式解决问题。

5 二八原则

二八原则同样适用于这里。80%的性能消耗是因为20%的问题引起。找到当前的主要矛盾是重要的。去研究代码,获取几毫秒的性能,不如去看看数据库,干掉一条执行超过1000毫秒的语句。

6 容忍缺陷,不要追求完美

比如,一个页面,实时计算需要耗费20%的性能,那么定时计算就明显节省了很多。当然效果上会有差异。这时候应该容忍缺陷。选择合理的折中方案。而技术人员也应该学会足够的沟通技能,让产品人员明白两者的差距。

而,通常一个看起来完美的架构,在实际应用中是不堪一击的。

7 记录是好习惯

无论是内部的文档系统,自己的blog,或是邮件列表,一定要记录下所做的改动。这样的资料对于以后的查错,升级,分享都很重要。

8 语言不是最重要的因素

总有人问,.net好还是java好,php快还是jsp快。在我们这个系列中,这不是重点问题。其实在真正的实践中,任何语言的差距都是相当有限的--只要你会用。优化本身是不在乎语言的。比语言造成的影响大得多的因素有的是。

当然,限于不同语言的特点,各有应用领域。而.net/java之类的语言确实在web开发上不如php那么舒服。不过这真的不是最需要解决的问题。


9 这系列文章会讲真正有用的东西吗?和银杏的业务是否冲突。

答案是,当然会讲真正有用的东西。虽然银杏咨询的业务也是这些,不过我们仍然愿意让大家多了解这些知识。理解了之后,你自然可以自己完成,但采用我们的咨询服务显然是更简单和稳妥的方案。所以这与我们的业务没有直接冲突,我将努力把我知道的东西写出来。如果有不完善和错误的地方,那一定是我水平有限,到时还请多指点。:D

原则目前就想到这些。我们会在以后的讲解过程中逐渐体会这些原则的意义。

最终,我们一定可以实现让网站快上100倍这个目标。

May 8, 2007

startJVM出错的解决方法

startJVM是加载jvm用的方法。在JPype,apache mod等等很多地方都用到。但凡要用其他语言来加载jvm进程,就要用到这个。
可惜往往会出错。一般都是

Unable to load native library: libjvm.so: cannot open shared object file: No such file or directory

但是libjvm.so确实存在啊。

解决方法很简单:
在/etc/profile里面设置:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/java/jre/lib/i386/client:/usr/local/java/jre/lib/i386
这样就能找到其他的链接库了。就没问题了。倒是简单,但是错误信息是libjvm.so ,而且No such file or directory ,所以很让人迷惑。

March 11, 2007

libwww,libcurl以及其他

最近我需要写点页面分析的东西,这些东西某些程度上类似搜索引擎的“爬虫->parser->存储”的过程。

过去我常用的抓取页面的库是libcurl,这个东西是unix常用命令curl的基础,curl被称做“命令行浏览器”,功能强大,支持的协议也全面。遗憾的是libcurl仅仅是个支持多协议的抓取库,不能做解析。

找来找去,发现了w3c的Libwww库,这东西功能强大的吓人,不仅有解析,还有robot(也就是爬虫了,或是叫internet walker)功能。在Libwww基础上完成的程序很多,最著名的大概是字符模式的浏览器lynx。我几乎就觉得这就我需要的东西了,立刻dive into。

一整天之后,我终于能用这东西抓下来页面,并且从html页面中分析出来一些信息了,但是想更进一步就变的异常困难。因为这个库功能太复杂了。这东西文档不详细,被人提及的也少。Libwww最近的Release 5.3.2,发布于2000年12月20日。一个有这么多年历史的东西,竟然没多少开发者在讨论,非常不正常。

找来找去,最后在libcurl的FAQ里面看到了和Libwww的比较,精选的读者来信告诉我,不仅仅是我一个人被Libwww的复杂弄的晕了头脑,我才花了一整天,写信的那个哥们竟然用了一人月,还是在里面打转,直到换了curl才好。虽然这是libcurl推销自己的方法,不过这些失败的前辈的经验让我对自己的智商重新有了信心。看来这东西没多少人讨论是正常的...

好吧,我也投降,libcurl没html解析功能,这没关系,我找别的办法好了...这么复杂的库,再好我也实在没办法忍受下去了,再说我需要的功能其实也真没Libwww那么复杂的。

写程序其实很容易迷失,你会看到一个似乎很完美,什么都能做的东西,一下子就喜欢上它,但是最后往往还是无福消受。往往是那些,不那么成熟,多少有点小毛病的库,组合在一起才是真正的解决方案。

March 1, 2007

简单的cms:cmsmadesimple

cms made simple ,物如其名,果然是让cms变的非常简单。

这段时间,我尝试了无数个cms系统,从大到小,最后唯一看中的就是这个cmsmadesimple。

无论是xoops,drupal或是manbo,都太复杂了。他们其实是portal软件,而不仅仅是cms。要说cms,我觉得只要能灵活的管理文章和生成各种需要的页面就可以了。实在是不应该太复杂。至于“CMS要有很多复杂的功能”,这种话绝对是蒙人的。

cms made simple提供的主要功能:文章库,发表文章,页面模版,用户管理。

简单?但是搭配起来可不简单。

cms made simple的文章库中的文章是有层次结构的,而且是无限分层。

基于smarty的页面模版非常灵活,而且可以为每篇文章分配不同的模版。

就这两点,就可以做出来各种效果了。包括频道和专题。

更让我有兴趣的是,这东西开发和扩展都很容易,代码质量高而且风格统一。

当然了,cmsmadesimple也支持module,也有很多人开发了很多插件出来。由于良好而简单的接口定义,写起来也不复杂。

这绝对是个值得尝试的产品。

安装的时候我碰上2个问题,纪录在这里,供参考。

1 install.php怎么不能运行?

打开install/install.php
把这段代码注释掉,就可以安装了。(奇怪,干嘛要判断这些啊?)
// Test for sessions if this is the first page of the install
if (1 == $currentpage)
{
@session_start();
if (!isset($_GET['sessiontest']))
{
$_SESSION['test'] = TRUE;
$scheme = ((! isset($_SERVER['HTTPS'])) || strtolower($_SERVER['HTTPS']) != 'on') ? 'http' : 'https';
$redirect = $scheme . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . '?sessiontest=1&' . SID;
header("Location: $redirect");
}
}

2 UTF-8
mysql数据库中文问题存在了很久了。mysql 4之前采用的方式是用latin1的库来存放utf-8字符。这样当然也没问题。不过现在比较好的解决方式是把库也建成UTF-8的。

方法很简单:create database YOURDBNAME character set UTF8;

但是这样的库,按照以往的连接方式是不行的。必须也要在UTF-8方式下连接。所以要修改一下adodb的driver(CMSMadeSimple使用了Adodb lite库)。

打开lib/adodb_lite/adodbSQL_drivers/mysql/mysql_driver.inc

找到函数 function SelectDB($dbname) ,在$result = @mysql_select_db( $this->database, $this->connectionId );前面加一行:

@mysql_query("SET NAMES 'utf8'");

就可以了。


February 24, 2007

从apache扒出来一段string代码

c 里面字符串处理很讨厌,比如要append东西,就要memset,memcpy,malloc来回折腾,很麻烦。

我问tiny,这种东西大家每次用都自己写一个,难道就没有一个通用的吗?都这么多年了...tiny说都是自己写的。说的也没错,其实这东西就是基本功,应该顺手就能写一个linebuffer出来。但是他还是麻烦啊。字符串处理是内存相关的,大概是c最难掌握的技巧之一了。

我觉得C++比C好学的原因之一就是C++的std下面提供了很好的字符串类。

翻了几个著名的opensource项目,最后我从apache httpd里面扒出来这几个字符串处理函数,还不错。用起来也很简单。
就这样就可以:

MWA_STRING string;
apr_pool_t *pool;
init_string(&string, pool);

append_string(&string,"1111",0);
append_string(&string,"2222",0);
printf("%s",string.data);

用append往上增加字符串。

代码在:
http://blog.devep.net/virushuo/2007/02/24/mwastring.c
http://blog.devep.net/virushuo/2007/02/24/mwastring.h

update :
令狐虫 提醒,这个模块是mod_ WebAuth的一部分。他说的是对的。我是从apr_pool_t开始扒的,故有此错误。谢谢提醒。

关于许可证的问题,我怎么记得mit/bsd都是非继承的许可证呢?应该不用再声明吧?

February 14, 2007

drupal 在 php5.2.0 下session丢失的处理办法

php 5.2.0中,drupal的session总是被设置为uid 0 由此造成了用户无法登录和获得权限。

这似乎是5.2.0中改动的内存管理所导致的。

解决方法很简单,用session_write_close() 即可。

在index.php的 drupal_page_footer(); 之后,加上1行: session_write_close();

就可以了。

http://drupal.org/node/92802

这里有更详细的讨论。

update: 更简单,更可靠的办法:

修改includes/session.inc
在function sess_read($key) 中,加上:
register_shutdown_function('session_write_close');

March 23, 2006

计算机的本质

计算机的本质,是提供运算能力的机器。
人们编写各种各样的程序,在机器上运行,于是产生了各种各样的应用。
为了更方便的编写应用,让应用程序的开发门槛降低,我们迫切的需要一种中间层来隔离硬件,于是操作系统出现了。

有趣的是,几十年间,操作系统这个名词虽然存在,但其形式和概念已经发生了翻天覆地的变化。30年前,无法想象会出现现在的windows或是linux这样的操作系统。要知道,最初的所谓操作系统只不过是一个时钟调度(可以看作批处理)的简单程序而已。

这种变化来源于几个方面,一方面是计算能力的提高(内存越来越大,CPU越来越快),允许我们在计算机上赋予更多的内容,开发更复杂的应用,另一方面来源于硬件种类越来越多。

与其说是计算机需要操作系统,不如说应用程序需要操作系统。现代操作系统不是在一夜之间产生的,而是随着历史的发展,缓慢的形成的。简单说来,计算机的本质是应用,操作系统只是提供了应用的运行环境。

java芯片是一个非常有趣的东西,在这个芯片上,可以直接运行java的字节码,这种芯片不在需要一个强大的操作系统来管理硬件资源了。其本身就提供了java运行环境。(事实上,如果你的汇编足够强,那么就可以直接在任何芯片的计算机上直接编写应用程序--就像在单片机上作的那样--而不需要任何操作系统的支持,只不过,这比较麻烦而已。)

java的创举不是创造了虚拟机,事实上虚拟机的概念从smalltalk就存在了。java的创举是将虚拟机放到了不同的层次,在操作系统之上的层面,有jre环境,在操作系统之下的层面,有java芯片。对于不同的操作系统,有不同的jre。

未来的运行环境是什么呢?在大型机的年代,只有主机上上有真正操作系统,客户机只不过是登陆上去,使用主机的资源而已。技术历史不过是一个绕圈子的过程,一圈绕回来,上了一个台阶,但是本质在相当长的一段时间内,仍然是不变的。

有感而发,无意争论。

March 13, 2006

一本属于历史的新书


这个拗口的标题来源于这本书:《白领就业指南 c++builder 6.0 设计师之路》,电子工业出版社出版。

今天在西单图书大厦拍到的。

且不说这个土气到了极点的名字,就说这书现在有没有存在的价值吧。

C++ builder 6 发行于2002年(感谢猛禽提供这个确切的时间),但是始终也没有被业界(尤其是中国的业内)大规模的应用过。我虽然是borland的fan,但是实在是没见过有几个公司真正用C++builder作为开发工具。虽然,delphi作为borland的王牌开发工具,获得了非常广的应用的。

2004年开始,borland主推C++ builder X,从此C++ builder 6 这个采用VCL库的C++产品事实上已经走到了尽头。同时,随着微软.net的发布,borland阵脚大乱,大有抛弃全部,一心拼.net之势。

终于,2006年2月,borland正式宣布退出开发工具市场。从此C++ builder/delphi便成绝响。

那么,这本书出版于什么时候呢?

版权页显示:2005年11月第一次印刷

那么,这本书值多少钱呢?

版权页显示:26元

所以,我不得不说,这是一本属于历史的很贵的新书。

看了图书大厦“新”书,我再次对计算机图书市场失去了信心。

新手买书,可真是步步地雷。

最后我买了两本书:

《中国建筑史》
《离散数学极其应用》


March 5, 2006

用sql选取日期为某一天的记录

sql里面选择某一天的记录,我一直都不知道怎么作比较好。数据库里面保存的一般是:2006-03-05 0:00:00这样的格式,那么 where xxx='2006-03-05'显然是得不到结果的。看到很多人用 where xxx>='2006-03-05' and xxx<'2006-03-06'之类的方式,很难看。

今天翻postgresql文档的时候发现了以下解决方法:

用date_trunc函数选择精度
where date_trunc('day',xxx)='2006-03-05' 就可以了。

9.9.2. date_trunc

date_trunc 函数在概念上和用于 数字的 trunc 函数类似。

date_trunc('field', source)

source 是类型 timestamp 的值表达式(类型 date 和 time 的数值都分别自动转换成timestamp或者interval)。 用 field 选择对该时间戳数值 选用什么样的精度进行截断)。 返回的数值是 timestamp 类型或者interval,所有小于选定的 精度的域都设置为零(或者一,如果是日期和月份域的话)。

field 的有效数值是∶

microseconds
milliseconds
second
minute
hour
day
week
month
year
decade
century
millennium

例子:

SELECT date_trunc('hour', TIMESTAMP '2001-02-16 20:38:40');
Result: 2001-02-16 20:00:00+00

SELECT date_trunc('year', TIMESTAMP '2001-02-16 20:38:40');
Result: 2001-01-01 00:00:00+00

January 11, 2006

Fedora Core 运行 JDK 出错(java.net.SocketException)

第一次在FC3上装JDK,运行tomcat的时候发现出错,写了一个java的测试程序去读某个网页地址,也出错。
错误是一样的:

java.net.SocketException: Invalid argument or cannot assign requested address

似乎就是无法访问端口。

经查,原来是ipv6的问题。如果系统安装了ipv6,jdk就无法连接任何ipv4的主机了,于是出错了。

解决方法是,在环境变量中加入:JAVA_OPTS="-Djava.net.preferIPv4Stack=true"

这样就一切正常了。

奇怪的是,我查这个错误,基本没有找到解决方法,国内的几个大论坛都有人提及,但他们都是在tomcat关闭的时候碰上的错误,别人给出的 解决问题的办法竟然是用kill来关闭tomcat。未免太有点不求甚解了吧?

December 31, 2005

freebsd.org已经被屏蔽

今天发现,freebsd.org无法访问。这是不可能发生的事情,穿墙访问成功。确认被封。

上freebsdchina.org论坛,发现全国都不能访问了。看来是被封了。

我除了骂人真是无话可说了。难道他们对free这个词已经过敏到这种地步了??

打电话到网通投诉,等回复。

如果您也是freebsd的用户,请分别致电当地网通/电信以及集团公司投诉。或许能有点帮助。我真得很悲观。哎。

December 29, 2005

备案促进ipv6发展

当我们想到一些好名字,通常会去注册,然后随便解析个地方,留着以后用。

现在这样不行了。

www.365kit.com 被封ip的事情,今天终于搞清,就是有一个没用的域名转到了这台机器。什么时候转的已经不记得了。这个域名只是随手注册的,甚至已经忘掉了,根本没想起来备案。365kit的两个域名均已备案。而这个没用的域名也没有web站点对应,仅仅是在域名注册商那里填写了这台机器的ip而已。就因为这个原因, 365kit.com服务器被封ip。

千万注意,有未备案的域名转到你得ip,你的ip就会被封,这是目前的中国式规则。

这衍生了2个问题。

1 用这种方法害别人,比任何供给方法都来得快,来得有效。如果觉得还是慢,可以直接举报,似乎立刻生效。
2 注册了新域名,可能主机都没有,ip添什么。

第一个问题,我没想到解决方法。有人来害,只能挂掉了。换ip,不知道目前ip资源紧张的情况会不会进一步加剧,不知道最终会不会推动ipv6的技术发展。很有可能到不了1年就把中国的ip地址都封没了。

第二个问题,比较好解决。我目前几个没用的域名,分别指向了信产部网站,监察部网站,公安部网站。我实在没办法,我不用,但是也不好害别人啊。他们一家人,估计抗得住。只能靠他们帮忙了。

所以,切记,
1 不要得罪人,否则人家注册一个域名指向你,你就挂了。而且无解。
2 注册域名的时候,ip请指向中央部委网站ip,否则会连累别人。

总的来说,靠这种目前无解的攻击方式,确实解决了信用问题,如果你骗人,别人就用这种方法让你挂掉。看来备案还是达到了预想的目的的。非常和谐。

December 23, 2005

在freebsd 上安装lighttpd

听说lighttpd这个web server很好,速度很快,内存占用小。于是打算装一个看看。

lighttpd已经port在freebsd里面了。到/usr/ports/www/lighttpd下面make config ,选定需要的东西,然后make install安装就可以了。

装后需要一个配置文件,可以先用默认的。
cp /usr/local/etc/lighttpd.conf.sample /usr/local/etc/lighttpd.conf
然后用
echo lighttpd_enable=\"YES\" >> /etc/rc.conf (是>>不是>,千万别错了)
/usr/local/etc/rc.d/lighttpd.sh start
第一次启动,可能会碰上一些问题。

我碰上的依此有:

1
# /usr/local/etc/rc.d/lighttpd.sh start
Starting lighttpd.
2005-12-22 23:18:30: (server.c.418) Are you nuts ? Don't apply a SUID bit to this binary

这个错误原因是/usr/local/sbin/lighttpd 的owner是root,而lighttpd.conf里面写了:
## change uid to <uid> (default: don't care)
server.username            = "www"

## change uid to <uid> (default: don't care)
server.groupname           = "www"

所以我们需要chown www /usr/local/sbin/lighttpd

2 # 2005-12-22 23:32:36: (mod_accesslog.c.512) opening access-log failed: Permission denied /var/log/lighttpd.access.log
2005-12-22 23:32:36: (server.c.670) Configuration of plugins failed. Going down.

这是因为www没有权限在/var/log建立文件。同样还有lighttpd.error.log也会报出来类似错误。
touch /var/log/lighttpd.access.log
touch /var/log/lighttpd.error.log
同样给www读写权限。
chown www /var/log/lighttpd.access.log
chmod 666 /var/log/lighttpd.access.log
...
这样就可以了。


再来/usr/local/etc/rc.d/lighttpd.sh start 就可以启动了。

December 7, 2005

foobar的来源与历史

foobar是每个程序员都常常见到的,其知名程度不逊于Hello world。一般我们常见的用法有分开的foo,bar,也有合起来的foobar。这个词一般可以用来代表计算机领域一切需要命名的东西,变量,函数,文件名,总之是代表什么都可以。

到底这个词是什么意思,什么来源,似乎很难说清楚,所谓难说清楚,并不是指没人知道,而是一人说来一个样。

RFC3092专门讲述了这个问题,洋洋大观,有的认为先有foobar,有的说法认为先有foo,具体的解释从二战时期军队的脏话(Fouled Up Beyond All Repair),到电子学名词(inverted foo signal),foo传说还来源于军队对于不明物体的代称(UFO),有的说是一种狗的名字,有来源于流行歌曲和漫画的说法,还有各种各样的名词缩写(比如FTP Operation Over Big Address Records),说法实在太多了。


我个人认为比较容易接受的说法应该是:

上世纪6,70年代,随着DEC的手册传播开的。早年间,hacker(多指偏向软件)一般用FUBAR,而计算机工程师(多指偏向硬件)则用foobar。后来逐渐foobar就取代了FUBAR的用法,越来越广泛。

如有兴趣,可参阅RFC3092

November 16, 2005

只允许cookies进行安全传输

setSecure(true);

给cookie设置了这个属性,就只有https连接才会让浏览器保存cookie,http连接会丢弃掉。可以增强cookie的安全性,

November 15, 2005

unix中root登陆安全的dirty case

我相信很多人不同意这是个dirty case。不过我仍然归为此类。其实dirty case并非贬义,甚至还带有一点点褒扬的意味,一点点hack的幽默。

unix最早是可以用root进行远程登陆的。后来这招有点麻烦,因为无数人用程序来猜测密码,任你密码再强,这种愚公移山的精神也不得不怕。

假如我们不知道后面的故事,这时候,解决方案大概有几种:

1 禁止root远程登录
2 设置验证次数,超过一定次数失败就封ip
3 设置ip过滤,只允许某些ip连接
4 要求root密码不的短于10个字母
5 ...

其实以上所述都在不同的地方用到。但是我最欣赏的,还是freebsd目前采用的方法。

freebsd设置了一个叫做wheel的组,然后禁止了root的远程登录,允许用户的远程登录,但只有wheel组的用户才能在登录之后通过su命令获得root权限。

这个办法简单粗暴,但是效果出奇的好。用户几乎没有成本,只是多记了一个用户账号。好处很大,禁止了root远程登录之后,就很难猜测了。首先要猜到属于wheel组的用户,然后才能有机会猜测root密码。猜root密码大概还容易点,猜测用户就不太可能了,谁知道用户名会用什么呢?

相比起来用证书来验证登录,或是限制ip,或是检测入侵等等相对正统的思路,这个办法真的要算个dirty case。效果如何呢?
假设第一个用户的用户名长度为6个字母,6位长的密码,那么就是猜测到这个用户的机会就是1/(1!+2!+...+6!)^2,于是,有可能猜测到root密码的几率就增大了(1!+2!+...+6!)^2倍。效果很可怕吧?

这个思路的形成,我简单的推测一下,大概是这样的(未必符合历史本来面目,只是猜测):

1 root登录危险
|
|--禁止root登录 (1)
|--允许登录但有限制 (2)

这是第一个分支,选择1还是选择2?
这时候,选择1,似乎简单一点。如果选择2,我们马上面临规则问题,而规则是非常难制定的,要引入大量的逻辑判断,引入逻辑判断则不可避免的要带来bug。两者相较,选择1似乎好一点。

于是,禁止了root登录。

2 禁止root登录
|
|--改良(1)
|--创造一种新方法(2)

选择2,就要创造一种不用密码登录的方法。这个方法也是成功的。就是通过rsa密钥访问。这个方法是最正规的,不过对于用户的要求也多了不少。

通过(1)是否能够演化出折衷的方案呢?这时候应该可以比较容易想到先要允许一个较为难以猜测的账号登录,然后神不知鬼不觉得让他具有root的权限。于是,wheel组这个方案就出台了。

虽然未必真正符合历史,想必也能提供一些思路上的帮助吧。


November 14, 2005

关于dirty case 王八拳

那些精巧的,庞大的架构,固然值得我们学习。但是,工作了一段时间就能发现,最能解决问题的,往往不是那种漂亮的架构。造成这样的情况原因很多,有时间因素,有成本因素,无论因为什么,事实就是如此,有些时候,不好看的方法,硬是顶用。Diaty Case不是一个专门的词,是我生造出来的,这个词最能表现这种中用不中看的方法和思想。

江湖中传说有一种拳法,叫做王八拳。其实这也不是传说,市井打架的时候随处可见,这拳法样子难看得很,但是威力却异常强大,很多江湖高手初次碰面的时候也照样被打于马下。

我们需要的也是这种东西,相信你一定有体会,太多的时候,没有时间去完善的钻研一个体系,更不可能花上几个月把相关文档都看完。那些时候你一定在抓狂,老天,不要跟我谈什么基础,谈什么架构,我就要解决这个问题,什么方法都行,只要能立刻解决。我们要的就是这种一招制敌的效果。难看?等我有了时间再说吧。不正统,但是的确能救命。这就是软件开发中的王八拳。

当然,王八拳并非不要基础,一个人高马大,脚跟扎实的壮汉打出来,跟弱不禁风的孩子打出来,效果完全不一样。所以,有时间的话,多看书,打好基础,实在碰上搞不定的问题,那就来dirty case吧。虽然这些解决方案难看,写出来也并不丢人,一是能解决别人的问题,二是也能让别人指点得失,也是学习的方法。

于是,这份电子报就专门来讨论dirty case了。写自己的dirty case,评看到的经典dirty case,开发的乐趣,也尽在其中。

请关注:王八拳打天下:软件开发中的Dirty Case

November 10, 2005

如何编译FreeBSD 6.0支持无线网卡

如何编译FreeBSD 6.0支持无线网卡

FreeBSD 6.0 Release加入了对无线设备的支持,今天尝试了一下。

刚刚安装好的内核是不支持无线设备的,需要重新编译。

如果没做过,可参考http://www.freebsd.org.cn/snap/doc/zh_CN.GB2312/books/handbook/kernelconfig-building.html

简单说一下:
先复制一个配置文件
# cd /usr/src/sys/i386/conf
# cp GENERIC MYKERNEL
然后修改MYKERNEL,里面选项的意思参考前面的文档。

编译无线支持需要保留device wlan,device pci,然后按照自己的设备类型选择。
我用的是intel网卡ipw,这个配置文件中没有,所以自己加上device ipw。

确认无误用make buildkernel KERNCONF=MYKERNEL编译。
安装:# make installkernel KERNCONF=MYKERNEL

修改/boot/loader.conf,加入

if_ipw_load="YES"
wlan_load="YES"

重新启动之后,ifconfig应该就能看到ipw0这个设备了。
这时候还不能用,要加载firmware。我是通过安装ports/net/ipw-firmware实现的。

安装后,用 ipwcontrol -i ipw -f /usr/local/share/ipw-firmware/ipw.fw 加载。

最后用ifconfig激活设备就可以用了。
需要注意的是,如果使用WEP,需要先加载相关模块。
在/boot/loader.conf加入wlan_wep_load="YES"
然后用:
ifconfig ipw0 inet 192.168.0.20 netmask 0xffffff00 ssid my_net \
wepkey 0x01020304050607080910111213 deftxkey 1 wepmode on

注意:6.0之前用的是weptxkey ,现在变成了deftxkey。

这样就可以用了。

参考资料:

http://www.freebsd.org/cgi/man.cgi?query=ipw&sektion=4&manpath=FreeBSD+6.0-RELEASE

http://damien.bergamini.free.fr/ipw/ipw-freebsd.html

October 19, 2005

mysql 4.1 windows版本客户端登录错误

Mysql 4.1版本,用客户端连接的时候(用phpmyadmin等等php的程序也有一样的问题),会出现这个提示:
Client does not support authentication protocol requested
by server; consider upgrading MySQL client

原因:
MySQL 4.1 and up uses an authentication protocol based on a password hashing algorithm that is incompatible with that used by older clients. .....

大概就是4.1 采用了新的加密算法(是什么新的呢?不都是md5吗?),但是客户端的加密方法仍然还是旧的算法。所以就出错了。

解决方法:
先用mysql -uroot -p 连接到数据库,然后
mysql> SET PASSWORD FOR
-> 'some_user'@'some_host' = OLD_PASSWORD('newpwd');

或是

mysql> UPDATE mysql.user SET Password = OLD_PASSWORD('newpwd')
-> WHERE Host = 'some_host' AND User = 'some_user';
mysql> FLUSH PRIVILEGES;

这样就能解决了。其实就是用这条语句把密码重新写到数据库一次。
但很烦的,以后如果修改了用户的密码,或是新增加了用户,都要这样处理一下才可以。

May 21, 2005

Bindows 很强,然后呢?

随着ajax被提起,被讨论,被应用,新的一轮RIA(富客户端)的热潮开始了。

Bindows最近也是出镜率很高的词,2年来,几乎每次谈到RIA,Bindows就会露露脸,但随后就被大家忘掉了。从来没听说过有人真的应用过。

的确,Bindows很强,但然后呢?

我始终质疑在浏览器中模拟一个"应用程序-like"的UI,是否有实际意义。事实证明了这种意义不大(始终没有真正的应用案例)。B/S从一开始,就是带着很酷的外观出现的,比 起应用程序界面的方方正正的灰色框框,第一代浏览器中完成的界面就已经大大超越他了。抛开美观不说,为什么浏览器流行,而应用程序不流行?是因为浏览器的 界面比应用程序简单,有人说“只要一步步的选,点就行了”。看其本质,应用程序是以功能为导向的(必不可少的元素:菜单,工具条),而浏览器是以内容为导 向的(Document)。用户使用浏览器,大多是为了“获取内容”,和使用应用程序的“完成某种操作”从本质上就不同。试想:如果有个基于浏览器的光盘 刻录程序,你会用吗?当然,如果作的足够好,也可能会用,但是,比起应用程序,他有什么优势呢?刻录光盘是一种“操作”,并非获取内容,不是浏览器的典型 应用环境。浏览器归纳了“获取内容”这个行为的基本操作(访问,前进,后退,刷新,停止),并封装成一套界面元素和操作习惯,这样,B/S应用就成形了。

而Bindows只是简单的模拟,虽然代码很牛,难度很高,但是实际意义很小。所谓RIA,我认为,应该是获取内容的操作方式,结合上一些应用程序的优 点,目前看来主要是事件模型(Event Model),如能解决这个问题,至少目前看来足够用了。Gmail把现有的事件模型和数据模型结合起来,创造了一种新的操作感受,很成功。ajax正是 为了试图解决这个问题而创造出来的模式。

具体的应用环境比技术本身更重要,不存在好的技术或是不好的技术,只有适用或者不适用。我不得不再次重申这句话。Bindows,恐怕只能是一个玩物。

May 19, 2005

ajax有罪?说点反对意见

 刚刚看到

http://duduwolf.winzheng.com/post/115.asp

说点反对意见。

罪之一:对搜索引擎的支持不好

为什么要支持搜索引擎?为什么不是搜索引擎支持站点?别忘了,是先有的站点,然后才有的搜索引擎。搜索引擎不能适应网站的发展,那自然是搜索引擎的问题。事实上,据一些迹象分析,google似乎已经通过mozilla的的script解析器来抓去网页内容。

进一步说,希望搜索引擎抓到的东西,就别用ajax了。不过,凡是需要ajax的地方,如果不用ajax,似乎完成的方法也是使搜索引擎抓不到的办法。

罪之二:编写复杂、容易出错

复杂吗?ajax把部分逻辑放在了客户端,过去,这些用户界面的逻辑也要放在服务器端,哪个更复杂一些呢?
我看ajax这种方式才是符合mvc架构的思想。至于调试,unix程序员都是靠print()和log来调试的。谁在字符界面下用debugger呢? 其实,如果不是gui和逻辑再加上多线程混在一起,大部分情况没有用debugger的需求,在关键的点获得关键的值,这才是所谓调试的核心工作。 firefox的js console加上alert或是textarea输出,足够了。

罪之三:冗余代码更多了

同上,ui代码放到客户端,减轻了服务器的运算压力和带宽占用。比起aspx的不停的往服务器进行数据请求的所谓服务器控件,ajax强得太多了。

罪之四:破坏了Web的原有标准

<span> 一样可以用<a>完成,用户动作->事件->数据,这是接近于传统应用软件的模型,事件驱动是非常好的架构,其实,w3c也在制 定事件标准,但支持还不够广。大家说了很多年富客户端,没想到用几件东西拼拼凑凑就能实现,这有什么不好呢?

标准也是在进化的。

罪之五:缺少一个没有标准之争、没有back和history的浏览器

这涉及到具体的应用环境,有些时候,我们就希望浏览器没有back和history。不要只去看cool的一面,要去看放在典型应用环境的地方。比如gmail,说了这么多,有没有人觉得他不好用?反正我是很喜欢。

罪之六:XML只是用来打幌子

这条罪名只是用来打幌子。

只驳斥2点。
1 xml是用来描述复杂数据的,不是用数组就能装下的那种简单数据。数据足够复杂的时候,你自己处理数组的效率,我相信不会比得上dom或sax。
2 服务器端封装xml,谁说非要使用xml解析器了?直接用字符串拼接行不行?解决问题的方法是多种多样的。

罪之七:世界这么大却找不到自己的家

是吗?

java和.net,与ajax似乎并不冲突吧?相反还应该是有利的补充。要说刺,顶多也是刺到了.net的服务器控件。sun的态度,应该是非常欢迎ajax的。不信?看是什么?  

一个flash占用的客户端运算能力有多大,占用的带宽有多大,刷新一次一个500k的页面需要多少客户端运算能力,占用的带宽有多大?同样情况ajax呢?算一算就清楚了。

综上,总结几点

1 请分清楚服务器和客户端
2 请搞清楚web程序到底在做什么
3 请想一想web应用(特别是server-side)的瓶颈和最大负荷在那里
4 任何技术都不是万能的,凡事均有其典型应用场景。炒作名词中国人最热衷的,抨击也是。


May 12, 2005

ajax 乱弹

所有的web程序,都是在致力解决2个问题。
1 获得数据。
2 将数据表现出来。
交互正是在一次次的进行1和2的过程中体现出来的。
从古老的perl,c,tcl程序,到现代的java,.net,python,无一不是在作这件事情。他们所改变的,大概只有程序架构,效率,开发难度,维护成本。

真正伟大的变化在浏览器这边发生,而不是在服务器。而,这么多年我还一无所知。我长时间的不屑于任何客户端的脚本程序,能编译的东西才叫做程序,然而,错了。

就算到了今天,抛弃java,.net之类,继续用c去做服务器应用,还是能够完成的。(注意,这里说的是*能够*完成,成本上,效率上必然有问题,但,任何应用用C都可以完成。毫无疑问)。

然而,客户端可不是这么简单。如果没有浏览器一代代的改善,恐怕现在还要用lynx看文字呢。

ajax, 概念很简单,用起来也不难,但,这个词汇从原来客户端所支持的一大堆乱七八糟,五花八门的标准中准确地选择出了最有意义的组合方式。所以,我看ajax这 个概念并非很多人所认为的不值一提。世上的可以吃的东西太多了,但那些东西能组合成美味的蛋糕呢?相信不是谁都知道,虽然看到配方之后都是些平凡的东西。

a 解决了用户在等待数据之前,什么都不能做,只能等待的问题。
j 解决了网页上的交互问题。曾经一度我认为xslt更好一些,不过,w3c的Event模型还需要很长的时间才能真正可用,推广,并被广泛支持,所以,在此之前,还是让xslt去做他最擅长的转换工作吧。
x xml解决了数据问题。因为标准,所以才能广获支持,如果浏览器分别支持不同的标准,那么你的服务器段数据就要准备多份,这太令人郁闷了。幸好这个问题上,微软和w3c没什么分歧。msdom我想应该是微软搞出来的最符合标准的东西了。

因为有xml,所以有了dom,有了dom,网页就具备了更改自身的能力。于是,美妙的应用诞生了。

事实上,ajax其实减轻了网站架构,页面清晰了很多,逻辑清晰了很多,数据量减少了很多。这些,都是web应用的基石。

March 20, 2005

使用https方式证书的一点问题

如果要启用tomcat的https方式,首先要创建证书,使用的工具是jdk中的keytool ,用法不多说了,到处都有。

 使用keytool创建证书的第一个问题是:您的姓名是什么(竟然是中文的!),这时候,理所应当的是回答自己的名字。配置完成后,用浏览器访问,一切正常。但使用http client 写程序访问的时候,问题就出来了。

一般表现形式是这样的: 

type Exception report


message


description The server encountered an internal error () that prevented it from fulfilling this request.


exception


javax.servlet.ServletException: HTTPS hostname wrong: should be <localhost>


 ......


root cause


java.io.IOException: HTTPS hostname wrong: should be <localhost>

 ......

看到这个问题就能猜到是证书问题。但无论如何也猜不到证书到底有什么毛病。事实上,令人难以置信的是错误就出在创建证书的第一个问题--你的姓名。其实这个“姓名”应该是域名。比如说localhost或是blog.devep.net之类的。输成了姓名,和真正运行的时候域名不符,当然会出错。浏览器无所谓,弹出一个对话框,用户按一下就行了。但http client就直接抛出了上面那个错误。

真令人啼笑皆非。

 

又及:好久不去sun中国社区,去了一趟觉得很搞笑,sun竟然在用人民币来奖励回答别人问题或是发表文章。感觉真像sun的风格,直来直去的。微软中国社区也有激励机制,但人家只不过是可以在微软的商店里面购物的点数而已。sun则是“赤裸裸的人民币交易”:D

December 17, 2004

胡侃一下数据仓库

最近,因为某些原因,接触了一些数据仓库的东西,整理了一下思路,胡乱写写。决不抄概念,就说我的理解。

首先说数据仓库。数据仓库是一种专门用来辅助决策分析的数据库。其数据量一般比较大,数据来源比较多,也许是数据库,也许是文件,总之,就是能用到的所有信息,甚至包括历史信息,都在这里保存。为什么要保存这么多?就是为了用来做复杂的分析和决策。

数据仓库有了,需要装入数据。前面说数据来源比较多,这句话说得很简单,但事实上,很复杂。因为,企业可能会有很多现存的数据库系统,甚至是一些excel表格,没准文本文件也有。这些格式,接口各不相同的数据,如何变成符合数据仓库模型的数据,并装入这个数据仓库,实在是个问题。

于是,ETL出现了。ETL就是数据抽取(Extract)、转换(Transform)、清洗(Cleansing)、装载(Load)的过程。一般来说,这是一个工具。也许每个程序员都有“倒库”的经验,这是个常见的动作,甚至你换了论坛的数据库,都需要做一次这个工作。这,似乎可以算作最简单的ETL。当然,ETL是比复杂得多过程,而且,应该为客户提供一个ETL工具,支持脚本也是必要的。这样,用户就可以定期或实时将数据库的数据抽取到数据仓库中,用户数据就是这样装载进来的。

数据有了,下面做什么?用户如果只是需要简单的查询,那当然简单,不过如果这样,也就用不着数据仓库了。使用数据仓库,一定是要进行复杂的决策分析。比如说,销售额,时间,人员,产品种类,销售渠道,要用这些属性进行汇总分析,这就是2维的报表和查询难以实现的了。那么,OLAP出场。OLAP(联机数据分析),OLAP一般是建立一个计算模型,然后把大量的数据转化为多维数据,保存在一个多维数据库中。这就不需要每次都要重新计算了,速度也快多了。事实上,如果不怕速度慢,不厌其烦,不怕计算复杂,不使用OLAP,都自己计算,也不是不可能,不过,这何必呢?

这些都完成了,最后做什么?数据展现。一切都要以客户的眼睛为准。用户说不好看,说不好用,你的系统再NB,你也死定了。所以,数据占现是非常重要的。其实,数据展现也就是报表,只不过,要具有展现复杂数据的能力,速度还不能太慢。切换维度也要方便。

概念都说完了,那么,一个数据仓库的工作过程,大概就是:数据仓库建模,ETL,OLAP,展现。大部分项目中,最重要的过程是ETL。毕竟,这个事情千变万化,可能会因为用户的现存系统,导致非常复杂的情况。也会耗费大量的时间。其次重要的,我认为应该算数据展现,刚才说了,好系统的意思不是技术高明的系统,而是用户说好的系统。所以,强大不强大放在一边,先搞漂亮再说。(就凭这一点,oracle就应该出局了)。大部分项目中,其实OLAP是很可以偷工减料的,大部分客户,其实并不需要OLAP这么复杂,只不过,因为某些目的(把项目搞大,骗投资,厂商蛊惑,显示自己牛),非说要OLAP。事实上他们的数据维度和数量,远远达不到,可能几年都未必能达到。那么。。。偷工减料吧!作项目,省点是点。

看完了本文,你未必学到了什么东西,但是,至少你应该学会吹牛。我坚信,国内80%的项目,其实根本用不到这么复杂的东西。你只要能应付好,其实都可以简单搞定。

-------

December 10, 2004

奇文共享--IIS使用十大原则

最近开始使用apache,找资料的时候找到了这篇文章,实在是笑得不行。贴出来大家看看吧。

  1. 自定义错误页

  虽然自定义错误页很简单,但只有少数管理员有效地利用了它。管理员可以在MMC中将HTTP 错误信息映像到服务器上的绝对URL或是某个文件,更为详细的信息可以在这里找到。如果你嫌这太麻烦,想要更简单的方法,或者你希望开发者自己定义错误 页,同时又不想让他们具有使用MMC的权限,你可以使用类似Customer Error 这样的工具。

  2. MetaBase研究

  如果你认为Apache功能强大是因为它有一个配置文件,那么你应该看一看IIS的 MetaBase有多棒。使用MetaBase,管理员可以完成关于IIS的所有工作,例如,建立一个虚拟目录;停止、启动或暂停Web站点;建立、删 除、禁止或启用应用程序。微软提供了一个可视化工具MetaEdit帮助你读写MetaBase,你可以在这里下载它的最新版本。为了更有效地利用 MetaBase,你应该试一下命令行接口---IIS Administration Script,简称为adsutil.vbs,你可以在C:\inetpub\adminscripts或者%SystemRoot%\system32 \inetsrv\adminsamples目录下找到它。
  注意:MetaBase对Web站点的正常工作非常重要,千万不要破坏它。切记:做任何修改前必须先备份。

  3. 自动纠正URL的拼写错误

  Apache的拥护者总是在吹嘘Apache的一些小功能,其中最酷的就数"自动纠正URL拼写错误"了,现在,IIS管理员也可以把玩这些小功能了,使用URLSpellCheck,自动纠正URL拼写错误小菜一碟。来做一个试验:www.urlspellcheck.com/fak.htmwww.urlspellcheck.com/faq1.htm ,怎么样,第二个错误的拼写将被自动纠正。

  4. 重写URLs

  Apache的拥护者一直就吹嘘mod_rewrite的强大功能,现在,用于IIS的这类产品有一打那么多,很多比mod_rewrite要好用,因为使用mod_rewrite,你必须熟悉正则表达式。试一下:IISWrite或ISAPI rewrite。

  5. 探测浏览器

  假设浏览站点的每一个人都使用同一种浏览器或屏幕大小都一样显然是很愚蠢的,你可以使用 javascript可以对访问者的浏览器进行检测。如果你使用的是IIS的话,你将会有更好的选择---Cyscape公司的BrowserHawk, Apache世界里没有与它相比较的产品。Cyscape公司最近推出了一款新产品叫CuntryHawk,它可以用于探测访问者所在的区域(国家)。很 可惜,至今我还没有语言敏感或者区域敏感的内容需要使用它。

 6. 站点内容压缩

  IIS 5有一个内置的压缩功能,可说实在话,它简直就糟透了,使用pipeboost吧。

  7. Web应用程序缓存

  你可以把不同的文件或目录设置过期时间,打开IIS信息服务器,右击站点内容,单击属性,在跳出来的窗体中你就可以进行相应的设置了。如果你想让开发者自己设置,请使用CacheRight 、XCache这些软件。
   有效地利用缓存是要花费一些时间和钱的,但当你看到访问量攀升,可站点日记因为没有数不清的304回应而变得很小,带宽流量也大大下降时,你就会体会到 为什么必须这样做了。设置了良好缓存的站点不多,相反,关于它的好处的文章在网上却多如牛毛,去看一看这些:Brian Davidson's page,Mark Nottingham,和 what AOL has to say 。

  8. 调谐服务器

  调谐服务器不是一个小题目,需要一本专著来说明它。在网上有一些很好的基础教程和帮助,比如Brett Hill和微软自己的Knowledge Base article 。当然,如果你不想花这些时间的话,用这个--XTune。

  9. 加强站点的安全

  现在攻击站点的人可真不少,但只要你愿意付出一点点的努力,你就不会是一个只会坐着挨打的傻瓜。找出 你的服务器信息和操作系统信息是攻击者的第一个目标,所以,首先,不要暴露你的HTTP头让别人知道你运行的是IIS,使用 ServerMask这类软件将HTTP头删除或替换掉。其次,你可以通过删除不必要的文件扩展名来进一步安装你的服务器环境。另外,你还可以扫描有问题 的URL请求,微软为你提供了一个免费工具--URLScan。

  10. 补丁,补丁,补丁!

  你应该下载安装最新的补丁。你可以到微软的站点,也可以到http://www.cert.org/,用IIS作为关键词查询。
  好了,这就是我总结的IIS管理10则。这10条中一些在IIS 6中没有必要了,但对于W2k和NT IIS管理员来说,运用好这10条可以让你好好的睡上一个好觉。

April 16, 2004

聊聊我的技术书架–C++(2)

继续写我的C++书籍


上次写到了深入浅出MFC,不过昨天翻抽屉的时候,又找到了一本漏写的——《Visual C++ 6.0技术内幕》英文版叫做《Inside Visual 6》。


这书也算赫赫有名,是侯捷所谓“MFC四大天王”之一。Kruglinski著,微软出版社出版。此书强于Document-view,细致入微,由浅入深。初学到精通都和是看。实在是本好书。比较糟糕的是翻译得太差。所以只能算7折的效果。这本书说来惭愧,我多次试图看,但始终未能看下去,最终只看了一小部分。基本还是本新书。
现在用VC也少多了,就算用,也用不着查这本书了,这书放在我手上有点浪费。干脆,送人了吧。
看到此篇blog的朋友,如果你VC处于刚刚入门的状况,而且又有志学好,学精,请你跟我(huoju@bj1860.net)联系,此书免费奉送。(最好是北京的,否则太麻烦)


再往后,就是一本《Think in C++》。Bruce Eckel著。此君最为有趣,写了书先公布电子版,而且随写随发布。完全不怕别人看了不去买。甚至还宣称,如果你打算打印几页带在路上看,那用电子版正好,比纸书方便。这种Open精神实数难得。不过,看了之后去买书的人照样多的不得了——没办法,人家的书好。不买一本总觉得不踏实。满心欢喜,买了这本书,价钱还算公道。当时觉得赚了。拿回去看的时候,可有点不对劲了。至于怎么不对劲,一哥们总结说:“全是中国字,每个词都明白,但连一块楞是看不懂!”。为什么啊?这还用说,翻译的差啊!看这书也有一个好处,可以提高英语水平。凡是看不懂的,请先想想这个词的英文,然后再想想这个英文单词其他的意思,往往能豁然开朗。在这种状态下看了半本(能坚持这么久,可见其内容之精彩!),我终于受不了了。彻底放弃了。当时还不知道这书能下载到,也真愚昧的可笑。后来知道了有下载,赶快去找来了英文版看。总算圆了这个心愿——尽管也没看完。


下一本不能不说的就是《The C++ Programming Language》,C++之父的作品。英文影印版。这书厚度实在夸张,买回来也没敢看,就当字典备用的。据说国外是学生的教材,真令人惊叹。这书内容同样也是包罗万象,而且权威的要死要死的。凭我的感觉,当字典最合适不过。


还有几个小薄本值得详细说说,分别是《Essential c++》、《Effective C++》和《Inside The C++ Object Model》。都是侯捷译,华中理工出版。
看起来很相似的3本书,其实味道大有不同。第一本简单,入门书。是C++委员会成员之一的Lippman所著,据说是Lippman写了《C++ Primer》之后,自己也觉得实在太厚了。打算给初学者一本快速入门的书。所以就有了此书。侯捷也是抱着同样的态度翻译的。(向二位致敬)。此书绝对是C++入门的绝佳通道,轻量级,浅显易懂,而且字字句句紧扣C++标准,从一开始就能养成良好的习惯。我的C++是不够纯的。代码属于C和++混合的OO类型。读这本书的时候也有不少问题存在。所以,别以为自己都明白了就看不起入门书,Lippman的书,还是值得一看的。
《Effective C++》,作者是Scott Meyers,成名作。这书不适合初学者。个人认为,应该算中等水平者的饭后甜品。初看其中都是写经验技巧之类,但宝贵之处在于没有停留在“知其然”的水平,而是力争知其“所以然”。作者对每个问题都去探讨其成因,以及相关问题,看似简单,其实涉及到的方方面面完全不小于一本百科全书。这本书,我的读法是放在枕头边,每天睡觉前读上几页,不费太大精力,还能知道不少东西。比较遗憾的是也没读完,后面一些大的主题没能尽读,着实是件遗憾的事情。只能期望将来有时间再返回来看。此书决不送人:)
至于《Inside The C++ Object Model》,可一定要小心。这书可是一块硬骨头,当年不知好歹,拿来就读。真是差点甭了牙齿。这本书的主题是C++的对象模型,可以说,读了这本书,就能自己搞出来一个C++实现。探讨的都是C++的实现机理,所以说是块硬骨头。作者也是Lippman。这书能看完的人估计真没有几个。反正我是没看完。当年好像问过Miracle,他说他也没看完,这让我多少有点平衡。^_^ 总之,没点功底看这书纯属打击自信+浪费时间。到一定水平来看,还是收获多多的。我不算高人,没收获到什么,只是吹牛的时候多了一项——我看了多半本《Inside The C++ Object Model》,你看过吗?嘿嘿。
以上基本都是侯捷译著。没错,我喜欢看侯捷的书,选材精良,翻译精细,文字考究。敬业的作者,优秀的书。可惜没机会跟侯先生当面表达一下敬意:)
最后一本,大部头《C++ Primer》,前面说了,Lippman所作。也是百科全书一类。其实有《The C++ Programming Language》就用不着这本了。但风格上,我更喜欢Lippman。所以又买了一本。中国电力出版社出版。(嘿嘿,这个出版社还真有不小的缘分呢)。C++工作者必备的案头书,没有的话赶快去买。后来同事adex想学C++,我推荐的就是这本。甚至利用点关系搞来一本送他。初学用?没错,这本书就是可以初学用的,就是厚了点,但是内容并不艰深,引导性很好。此书的特点就是全面,太全面了。所以,即可入门也可提高,这书买的还是划算得很。
我的C++书籍大概就这些了。(也许还有,散落天津北京2地,有一些没在手边,记不起来了)。从此之后,没有再买过。这些,足够用了。唯一可惜的是没看过Exceptional C++。去年曾经想买,不过看看china-pub上放出来的样张就倒了胃口。这类书,还是要看侯捷翻译的好。
另一个遗憾,则是没有好好研究stl,只能停留在用的层次。侯捷也有好书,可惜没精力研究,也就不买了罢。
从此之后,用C++的机会越来越少。大半都是自己的玩具类作品。于我来说,这门花费时间最多,投资最大的学问已到积累到一定水平,可惜无用武之地了,这总是件令人憋闷之事……但愿此文能给后来者节约点时间,能学得更好一些。


附:
推荐读法 [Essential C++ & C++ Primer]->C++ Primer(精读)->《Effective C++》&《More Effective C++》。这些读过之后,可读《Inside The C++ Object Model》。当然,到这个时候,如果前面的书都读完大半了(别学我,我哪本都没看多少),就不用我给建议了,你已经是一代高手了:)。

April 13, 2004

聊聊我的技术书架–C++ (1)

今天很想写点什么。抬头看看书架,就写写我书架上的书吧。很多技术短期内基本不会用到了。也许再过一段我会忘记。那是件可惜的事情。就让我记录于此,也算和后来者共享一点点经验。


熟悉我的人都知道,我在技术方面是个比较杂的人,应用的范围也算深入了解了几个不同的领域。所以,就按照技术的分类大概写写,然后再按照应用范围大概写写。如果能有人觉得这篇小东西有一点点用途,那也不枉费我敲了这些字。


C++我用的年头最多,也算掌握得最为熟练。我的C++生涯开始于95年。当时国内还属于“信息的荒岛”,internet刚刚再物理所有了第一个节点,国内大家正迷恋着CFido。资料少的可怜。精心制作的好书也少。我的入门书是一本《Visual 1.0程序设计》(大概是这个名字,年头太多,记不清楚了。)。作者是洪锦魁,台湾人。此书从安装,配置介绍VC,确实是入门到精通的好书。内容细致,举例恰当。读起来也不觉的枯燥。不过遗憾的是,仍然用C的思维来用C++,所以也只能算本入门书了。此书估计现在早已无处可寻。


此书读后,自己摸索着,看着例程,倒是也能写一些小东西。后面一段时间没买什么书。反倒是买了不少D版光盘。其中大半都是有代码文档等等,台湾的居多(大陆网络不发达,但台湾很好。这些信息大半来源于网站)。从中学到了不少东西。


96,97年,VC2.5,VC4相继推出,MFC唱起了重头戏。这时候的VC,我俨然就是不会用了。是啊,从VC1到VC2的差距不小,技术上变动也大。手边也没有找到好书。一次去朋友家玩(可能不少人认识,就是 没脾气 ),看到一本《VC2 21天自学教程》。翻了2页感觉不错,就借回去看。前面1天看了7章,后面一章看了7天。后来还书的时候,没脾气说跟他的感觉完全一样,也正是如此,我才记住了这两个颇似巧合的数字。此书其实就是烂书一本。作者名字记不清了。从这个时候,开始学会了点挑书的技巧–翻译书质量远高于国内原创书。


在这2个年头里面,因为没有研究明白VC,转而改用了BC++。始终为在dos下作了一套直接写屏的函数库颇为自得。BC++很不错,看随机文档就很好,所以也没有买什么书。


98年开始,发现了Borland C++。好东西,兼具C++和delphi的优势,第一眼看到就算迷上了。好不容易才弄到了一个。然后去买了两本书《C++ to How Builder》和《C++BUILDER 4 技术内幕》。前面那本书是问答形式,介绍了大概100多个VCL的小技巧。后面着本可是鼎鼎有名。英文名字是《C++Builder 4 Unleashed 》作者是Charlie Calvert,borland的资深工程师。还记得这家伙吗?kylix bate2的演示就是他做的,当时borland的fans肯定都曾为此激动过。我甚至保存了一张当时drbob上面放出来的照片:

这是我看到的第一本好的技术书籍。作者承诺给读者一个轻松的阅读环境,让读者可以远离电脑,轻轻松松看完。这书正适合当时的大学生活–没有电的时候我就爬在床上看书。


书中探讨了很多内容,包括代码的写作方式,什么样好,什么样不好,关于OOP的好的设计和不好的,VCL的事件委托模型,VCL对数据库的包装等等,可谓事无巨细,一应俱全。其中无数次夸赞Borland工程师的伟大设计——当然,他们确实很伟大。后来看英文版本时候,发现其中对MS的冷嘲热讽颇多,做中文版的时候这些话都被删去了。虽说和技术无关,不过也算比较遗憾。


这本书当时是学校给买的,我那时候正在给学校做一个科研项目。毕业之后把书还给了学校。遗憾了很久。待到毕业前夕,有人到学校卖旧书,碰巧看到了此书。竟然要价只有10块钱。当时一阵心酸。当即买了下来,就算作为永远的纪念吧。


在此之后有一段时间没有买教程类的书籍,一是穷学生,二是看帮助能解决大半问题。只买了一本《BORLAND开发人员指南》和一本坚果出版的 Ray Lischner 著的《delphi技术手册》(此书应该算delphi,但我delphi的书只有这一本,而且是为了CB才买的,也一起算入C++了)。此书是Miracle老兄推荐的,当时他跟我说“想买一本函数库的书,看中了这本。没想到买回来大跌眼镜,不过倒是赚了”。闻听此言,我就去书店翻了翻,当即决定买下。此书详细的介绍了Object Pascal的知识,然后对RTTI做了比较深入的探讨,还有一些比较深入的细节,现在记忆已经开始模糊了。总之是值得一读的书。我喜欢她还有另一个理由,书中每项技术难点都是用C++和delphi对比来讲。这不仅帮助我提高了C++水平,更帮我无意间掌握了delphi,甚至后来用delphi做过2个项目。


在此后,大概是99年,开始发现了侯捷的书很好看。可惜开始大陆没有,只能去网上看电子版。这时候,由于技术的提高,我又开始用VC做一些小型项目了,梦寐以求的想得到一本《深入浅出MFC》。还好侯捷老师开放了电子版。于是每天晚上对着计算机猛读一阵。这本书读得非常爽,读的速度也比较快,始终沉浸在一种打开宝库的发现的快乐中,其间夹杂着对MFC之精巧的敬佩,并为候捷耐心所折服。这本书的内容不用多说了,网上夸赞的文章大把的是。后来终于简体版面世,我第一时间就去买了一本。虽然这本书并没有再读多少次,虽然电子版早已熟读,但,这本书,我一定要买的,以此来表达对侯捷开放电子版的一点敬意。就算如今,已经到了vs.net的时代,如果有人用VC,我仍然会说此书必读。不读则难以懂MFC,不懂MFC则不能懂VC。书扉页上的“山高月小,水落石出”令人心旷神怡。至今我在剖析复杂的技术时候,仍然以此自励。

继《C++BUILDER 4 UNLEASHED》和《DELPHI技术手册》之后,这是另一本对我造成很大影响的书。


…待续

March 10, 2004

数据库空值比较

今天碰上一个bug,后来找到原因是跟数据库的空值比较有关。

当SET ANSI_NULLS 之后,关于空值的比较就有点特殊了。

抄录帮助贴在下面,看看你是否了解?
比较空值

比较空值时必须小心。比较行为取决于选项 SET ANSI_NULLS 的设置。

当 SET ANSI_NULLS 为 ON 时,如果比较中有一个或多个表达式为 NULL,则既不输出 TRUE 也不输出 FALSE,而是输出 UNKNOWN。这是因为,未知值不能与其它任何值进行逻辑比较。这种情况发生在一个表达式与 NULL 单词进行比较,或者两个表达式相比,而其中一个表达式取值为 NULL 时。例如,当 ANSI_NULLS 为 ON 时,以下比较总是输出 UNKNOWN:

ytd_sales > NULL

只要变量包含 NULL 值,下列比较也输出 UNKNOWN:

ytd_sales > @MyVariable

使用 IS NULL 或 IS NOT NULL 子句测试 NULL 值。这将增加 WHERE 子句的复杂性。例如,表 Northwind Customers 中的 Region 列允许空值。如果使用 SELECT 语句不仅测试其它值还测试空值,则必须包括 IS NULL 子句:

SELECT CustomerID, CompanyName, Region
FROM Northwind.dbo.Customers
WHERE Region IN ('WA', 'SP', 'BC')
OR Region IS NULL

Transact-SQL 支持在与空值进行比较时,允许比较运算符返回 TRUE 或 FALSE。通过设置 ANSI_NULLS OFF 可将该选项激活。当 ANSI_NULLS 为 OFF 时,如果 ColumnA 包含空值,则比较操作 ColumnA = NULL 返回 TRUE;如果 ColumnA 除包含 NULL 外还包含某些值,则比较操作返回 FALSE。此外,两个都取空值的表达式的比较也输出 TRUE。当 ANSI_NULLS 设置为 OFF 时,SELECT 语句将返回 Customer 表中所有 Region 为空值的行:

SELECT CustomerID, CompanyName, Region
FROM Northwind.dbo.Customers
WHERE Region = NULL

不论 ANSI_NULLS 如何设置,对于 ORDER BY、GROUP BY 和 DISTINCT 关键字空值总被视为是相等的。此外,允许 NULL 的唯一索引或 UNIQUE 约束只能包含一个带有 NULL 键值的行。后面带有 NULL 的行将被拒绝。属于主键的任何列中都不能含有 NULL。

涉及 NULL 的计算均取值为 NULL,这是因为只要有一个因子为未知,结果肯定是 UNKNOWN。例如,如果 column1 为 NULL,则 column1 + 1 取值为 NULL。

当搜索的列中包括定义为允许空值的列时,可以通过下列模式查找数据库中的空值或非空值:

WHERE column_name IS [NOT] NULL

请参见SQL server 手册

空值

IS [NOT] NULL

February 15, 2004

利用晚上的空余时间写个小东西

今天有了比较大的突破,比较重要的几个图都画好了。全部用GDI绘图的方式一点点画的。

累得够呛。

贴图庆祝。




February 13, 2004

C#随机数生成

刚才听戴飞说起随机数生成,翻出来一段以前帮大鱼儿写的代码。没准别人能用到,贴在这里吧。

public static string GetRandomPassword(int length)
{
byte[] random = new Byte[length/2];
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
rng.GetNonZeroBytes(random);

StringBuilder sb = new StringBuilder(length);
int i;
for (i = 0; i < random.Length; i++)
{
sb.Append(String.Format("{0:X2}",random[i]));
}
return sb.ToString();
}

January 5, 2004

O/R Mapper和代码生成器有什么不同?

原文:http://dotnetjunkies.com/weblog/seichert/posts/4699.aspx


  上一个贴子里面,我大概介绍了O/R Mapper。一些回复认为O/R Mapper类似于代码生成器。
O/R Mapper和代码生成器有什么不同呢?
  代码生成器使用一些设置好的模版来生成一串完成的代码。当一些部分有所变化的时候,需要代码生成器重新生成全部代码。
  O/R Mapper是一种组件framework,这种framework可能使用运行时代码生成方式来辅助从对象到关系数据库的映射,但是它不仅仅只是把DAL代码生成出来。让我们来用一个简单的例子说明。
  在我的O/R Mapper中,我使用了运行时代码生成的方式。生成的代码不会被使用Mapper的开发者看到或人工编译。下面我们一步一步来看我的mapper是怎么样作代码生成的。

一步,如前所述,生成传统代码,生成器建立对象和对象的属性和进行mapping所需要的属性。对象建立之后就可以被编译到程序集中了。这时候,还不存在
用于从数据库保存,删除,读取对象的代码。当应用程序运行时,一旦程序保存对象信息,动态代码和SQL即被framework生成,并且被编译为动态程序
集。动态程序集被缓存,然后被framework用来处理并发请求,保存对象数据。



  • 开发者不需要看到,也不要关系这部分代码。

  • 开发者不需要用另一个工具来生成代码。
  • 生成出的程序集永远是新的。

  总之,代码生成器就是代码生成器,但O/R mapper可能会在其framework中使用运行时代码生成器 辅助对象到数据库映射,但,他不仅仅是代码生成器。
  你不会看到他生成的代码(一般来说),他减轻了开发者编写和管理DAL代码的负担。与其考虑写(或使用代码生成器生成)保存,删除,读取的代码,不如多去考虑业务逻辑和程序需求,让O/R Mapper去做那些事情吧!

到底什么是 O/R Mapper

为什么要翻译这个?

一次和一个群里面的朋友聊天,有人说最近发现了新的设计数据库方法,就是把数据库的列和对象属性一一对应,这样设计很方便。我说寒,那有这么容易的,实际情况复杂去了,怎么能一一对应。

然后老兄大骂了我不懂,然后找来一篇文章作论据,我打开看看,是讲O/R Mapper的。我寒的不行,没敢再多说话。

印象中始终缺乏好的关于O/R Mapper的中文文章,所以造成很多人概念不清。O/R Mapper到底是什么?

Steve Eichert这一系列文章讲得不错。打算跟着翻译一下,给自己增加点认识。当然也希望对更多的人有所帮助。

原文:http://dotnetjunkies.com/weblog/seichert/posts/4677.aspx

到底什么是 O/R Mapper

 

  最近社区里面讨论O/R Mapper的越来越多了,但是,到底什么是O/R Mapper呢?
  让我们从O/R开始。字母O起源于 "对象"(Object),而R则来自于"关系"(Relational)。几乎所有的程序里面,都存在对象和关系数据库。在业务逻辑层和用户界面层中, 我们是面向对象的。当对象信息发生变化的时候,我们需要把对象的信息保存在关系数据库中。

  当你开发一个应用程序的时候(不使用O/R Mapper),你可能会写不少数据访问层的代码,用来从数据库保存,删除,读取对象信息,等等。你在DAL中写了很多的方法来读取对象数据,改变状态对象等任务。而这些代码写起来总是重复的。
   如果打开你最近的程序,看看DAL代码,你肯定会看到很多近似的通用的模式。我们以保存对象的方法为例,你传入一个对象,为SqlCommand对象添 加SqlParameter,把所有属性和对象对应,设置SqlCommand的CommandText属性为存储过程,然后运行SqlCommand。 对于每个对象都要重复的写这些代码。
  除此之外,还有更好的办法吗?有,引入一个O/R Mapper。实质上,一个O/R Mapper会为你生成DAL。与其自己写DAL代码,不如用O/R Mapper。你用O/R Mapper保存,删除,读取对象,O/RMapper负责生成SQL,你只需要关心对象就好。

  好,还有什么问题?哈哈,我从没有讨论过Mpaaer这个部分。现在,我们有对象,有关系数据库。在他们之间我们有自己的O/R Mapper。很多时候,对象的属性不可能100%和数据库的列相符。为了把我们的customer对象的FirstName属性保存到数据库的 t_Customer表中的first_name列,我们需要做一些"Mapping"。我们需要让O/R Mapper保存FirstName属性到t_Customer表中的first_name列。我们在O/R Mapper中设置这些Mapping。除了mapping对象属性,我们还需要定义keys及对象关系。

  优秀的O/R Mappers会提供设计器,帮助我们设置所有需要的mapping。一些不成熟的O/R Mappers(大多是FREE的)用xml文件来定义对象和数据库的mappings,另一些用自定义属性完成。通过什么方法mapping不是最重要 的,重要的是这种O/R Mapper允许我们设置哪些mappings。

  定义好所有的mapping之后,这个O/R Mapper可以帮我们做很多的工作。通过这些mappings,这个O/R Mapper可以生成所有的关于对象保存、删除、读取的SQL语句,我们不再需要写那么多行的DAL代码了。
  O/R Mappers远不止我所讨论的这些内容,在以后的贴子中,我将继续讨论O/R Mappers提供的其他功能。

December 26, 2003

从timer看一眼.net的编译机制

    因为工作需要,制作了一个定时发送数据的windows service,C#开发。其中碰上了一个很有意思的问题。


 


先看看程序大概的结构:


 


初始化


              private void InitializeComponent()

              {

                     this.timer1 = new Timer();

                     this.timer1.Elapsed += new ElapsedEventHandler(timer1_Tick);

              }

       serviceonStart() 

protected override void OnStart(string[] args)

              {

                     timer1.Interval = 6000;

                     timer1.Start();

                     EventLog.WriteEntry(ServiceName +"Event","timer started 1");

              }

定时器触发

              private void timer1_Tick(Object source, ElapsedEventArgs e)

              {

                     DateTime time = DateTime.Now;

                     TimeSpan dayspan=new  TimeSpan(1,0,0,0);

                     time=time.Subtract(dayspan);

                     DateTime dateTime1=new DateTime(time.Year,time.Month,time.Day,0,0,0);

                     DateTime dateTime2=new DateTime(time.Year,time.Month,time.Day,23,59,59);

                    

                     if (time.Hour==8&&time.Minute==30&&lastupdate.Day!=DateTime.Now.Day)

                     {

                           

try


                           
{


                                  
PSOInterfaceWraper PsoWraper = new PSOInterfaceWraper();


                                  
PSOInterfaceWraper.GeneralLedgerAsyncResult Result;


 


                                  
DateTime StartDate=dateTime1;


                                  
DateTime EndDate=dateTime2;

                                   int amount=LedgerAsyncBLL(StartDate,EndDate);

                                   Result = PsoWraper.GeneralLedgerAsync("00",StartDate,EndDate,this.TranslationCount,amount*100);               

                           

                                   EventLog.WriteEntry(ServiceName +" Vnet","Result:"+ Result.Result+"  "+Result.ErrorDescription+"Count:"+this.TranslationCount.ToString()+"---Amount:"+(amount*100).ToString()+"  date:"+StartDate.ToString()+"~"+EndDate.ToString());

                                   if (Result.Result==0)

                                          lastupdate= DateTime.Now;

                            }

                            catch (Exception err)

                            {

                                   EventLog.WriteEntry(ServiceName +" Vnet","Error:"+"  "+err.Message);

                            }

                     }

              }

             

实际运行中发现,       EventLog.WriteEntry(ServiceName +"Event","timer started 1"); 运行正确,但是timer1_Tick中的代码并没有运行。

后来分析发现,工程的Refences中引用了VNetPSO.dll,但是没有把相应的文件copy,到相应目录。也就是说,运行时找不到此文件。

观察代码可知,引用VNetPSO.dll部分的代码只存在于timer1_Tick中。复制VNetPSO.dll到正确位置,问题排除。

由此发现.net所谓即时编译的一些特性。

InitializeComponent()中,可看到this.timer1.Elapsed += new  lapsedEventHandler(timer1_Tick);按照.net的即时编译的思路,开始认为这段代码会导致timer1_Tick被编译。但是如果是这样的顺序,在InitializeComponent中就会出现错误,导致程序不能正常运行。_那么OnStartEventLog.WriteEntry(ServiceName +"Event","timer started 1");

也就不可能运行了。

     所以,timer1_Tick实际是在Timer第一次触发的时候才被编译的。上面程序的例子中,就是在timer1启动了6秒之后。这时候发现VNetPSO.dll不存在,故此这部分代码被编译器忽略。也就造成了最开始出现的情况。

当然,前面只是一些推测,并没有方法能得到可靠的验证。但是,我认为推测应该基本合理。

至少,可以推测这么几个事实:

1 .net中的代码只有实际用到才会编译,除非强制进行预编译。

2 由于以上机制定时器变得更加不可靠,因为在定时器第一次触发的瞬间会导致编译,对于要求时间很高的程序可能会导致错过第一次执行的时间。

about me:
me.jpg
CC License. Some rights reserved.
署名·非商业用途·保持一致
本站之所有未作特别说明的内容均使用 创作共用协议.
POWERED_BY_MT_3.2