Main

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 17, 2006

wp,真是太棒了

从donews用.text之后,我就时常在嘲笑韩磊。作为一个.net的老程序员,我得告诉你,对.net这个系统我一点也不看好,这东西用来做企业内部的erp,crm什么的肯定是好东西,用来给互联网用户提供公众服务,尤其是一个具有无限增长可能的网站为用户提供公众服务,老天,你不是疯了吧。

一个朋友是.net的支持者,我简单的一句话就击倒了他:“你去看看donews和csdn这两个.net的案例,如此高的出错率,这就是.net的效果。你敢说他们没有技术能力?”。对于韩磊,我最狠的一句话是:"我要是微软,现在我给donews钱,求求你别们用了,你们用这几年,得多少公关费用才能挽回负面影响。"

其实别说donews,微软的spaces还不是一样经常出错?这个问题回头再说,.net就是存在问题,不管那些微软的fans和mvp们承认不承认,这东西就没办法作大型服务,问题不是语言本身,从windows开始,到iis,全错了,从根就错了。要职到,高负荷是一个很重大的课题,说实话,别说.net,就算java我一样不认为能轻松的提供高负荷服务。

或许有人会说是技术问题。没错,或许是技术问题。但这东西上手容易而后面对高负荷的时候学习曲线骤然升高,难道是一种正确的态度嘛?这真令人无法忍受。改日我专作一篇关于对微软的感受之类的东西,微软的东西不是不好,但如果你要面对可能出现的快速增长来使用,你一定要作出在后面的日子里面付出极其高昂的后期成本的打算,这足以抵消前面开发快速上手容易程序员网管便宜而节省的成本。所以,中小型的项目,要考虑微软的产品,大型服务,想都不要想。

无数的血泪告诉我们,谁tmd说微软整体拥有成本低来者?那是pr稿罢了。

当然,这并不能怪韩磊,首先,donews开始blog的时候,实在是没有几套好的blog系统,mt只有2.x,而wp还没个影子。当时也确实没什么办法,.text还是个比较好的选择,相对于自己重头开发的话。再说,2003年我还在迷恋.net呢,也觉得是个好东西,我还没看透这些东西呢。

当我自己在blog.devep.net/virushuo采用mt之后,我就在时常游说韩磊,换mt吧,换mt吧,换mt吧。当然哥们始终告诉我两个字“不换。”。

是,对于一帮.net程序员,学习perl确实有点难度。其实对于我自己也有难度,不过为了打理我的blog,我还是学了点。

后来终于wordpress的声名越来越旺了。于是我就开始对韩磊说,换wp吧,换wp吧,换wp吧。donews并入千橡后,韩磊不再说“你有什么需要.text满足不了,我们可以慢慢开发”,而是开始考虑我这个建议了。某一天,他告诉我,换wp。我真是乐疯了。今天刘韧终于正式确认了这个消息,太棒了,除了这个词我想不出来别的形容词了。

前面大半是开玩笑,我也充分理解donews为了缩小规模,不得不采用快速简单的解决方案。不过我们后来也看到了韩磊为了.text渡过了多少不眠之夜,看的让人痛心。虽然他的程序写的很好,但写程序绝非他的强项 。同样的时间内,他应该用去思考,为大家带来更多东西,而不是以“自己最短的一块板”作苦力和.net搏斗。

未来看起来挺美好,很快我们就有wp用,爱搞搞也答应会跟着修改他们的程序支持wp,真是太棒了。

综上所述,概括3个结论,

1 wp,太好了,donews并入千橡,是好事,过去我仅仅认为理智上是好事,感情上有点难以接受,现在我终于在感情上也明白无误的认为是好事了。

2 你要想提供稳固的,长期的,大容量的服务,千万别用.net,否则你会付出巨大的代价。

3 期望csdn也赶快换。这样我可能会把csdn的技术blog继续用起来。

最后一个建议,刘韧说首先靠用很多机器的办法,以后靠静态化。我得提醒,不要去试图静态化wp,这没有任何意义。php的高性能造成了我们很少看到静态的php应用,不是作不到,而是没必要作到。无论是phpbb,还是wordpress,都是靠cache的方法来解决性能问题,事实证明了这种cache这和静态化性能相差无几(其实也可以看做一种管理下的静态化方案),而弹性却增大了很多。只有.net那种穷头末路的东西才动不动想着静态化呢。

用wordpress,只需要一件事,就是作一个好的linux/freebsd集群,而不是花费时间去静态化。

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