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 23, 2009

email问答: 最快的seo方法是什么?

说实话,我曾经想写过很多个系列文章,但最终都是挖了个坑,没坚持下来。综合起来,大概就是话题太大,或是涉及的方面太多,太难表达清楚。速食年代,太长的东西没多少人愿意看,写起来压力也太大。

这次试试短点的。几乎每天,都有朋友会发邮件或是msn问我一些问题,从技术架构,语言细节,学习方法,seo都有,比较杂。其中颇有一部分是值得琢磨和分享的。好吧,我也学学"我爱问连岳",做个问答,但是隐去所有真实信息。希望能对同样困扰的人有帮助。

---------------------------------

问题:你说seo没什么快捷的方法,我们公司新上线的频道a,仅仅半个月,pagerank就达到了8。这是怎么做到的?

回答:

从pagerank上看来,确实有8。分析一下:

(1) 用google的link:命令,搜索到这个a频道子域名的连接,发现有接近2w个。2w个连接,如果质量不错,确实能达到8。

(2) 用google的site命令,看看收录数量。发现只有2。这就有问题了,如果有8的pagerank,那么收录应该的数量应该相当大。

(3) 看一下这个网站其他频道,发现首页也是8。再用link来查看首页的连接数,发现和到a频道的连接数量一样。打开几个快照,可证明,确实是连接到首页,而不是a频道的。

结论:曾经有人用重定向把a频道转向了首页,所以导致google认为2者完全等价,于是给予了一样的pagerank。这个pagerank当然不可能被复制出来2份。所以解除重定向之后,要么pagerank变成本页真实的,要么就再也不抓取这个页面上面的其他连接,这取决于当初用的是301还是302。

------------------------------

这大概是seo的常见手法。这事情和技术无关,只和逻辑有关。只要有逻辑,几下就能看穿原因。遗憾的是受骗的人还是相当多,其中不乏大网站。就好像"中奖骗术"虽然老套,但永远有效一样。究其原因,无非是总有人喜欢捷径,喜欢快速见效。那自然有人投其所好。

银杏咨询早期做过一些SEO项目,效果不如预期。某著名音乐网站至今仍欠我们的咨询费未付。[1]

原因嘛,在我们的观念上,SEO不是小项目,需要产品,技术,公司经营者的共同努力,而且至少要有半年才能有比较好的效果。紧一段松一段是没用的,做做表面功夫是没用的,想快速见效是不可能的。但如果真的严格按照我们的方法来做,半年之后你一定会惊喜的发现,效果非常好。

可惜,人家不信。

换个说法,如果这世界上真的存在一种有效,快速,可复制的方法,那么这方法一定所有人都知道了,包括你在内。比如"去携程订酒店比你自己去柜台便宜"。

假如你月薪3000,这时候如果有人跟你说:"你给我2万块钱,我教你个办法,半个月之后,你就能每月赚至少30万。" 估计谁也不信。同样的道理,换成pagerank,通过SEO带来多少流量,怎么就有人信了呢?[2]

笑来老师讲英语学习,也讲过关于捷径这事。道理和现状何其相似。昨日不可重现,时间只有一次,所以一定要选好道路,不要忽悠别人,也不要被别人忽悠。


----
[1]曾经著名,但现在已经被后来者迅速超过。这个力量对比显然也不太可能有变化了。
[2]这个例子不夸张,要达到 pagerank 8 ,至少是门户级别的网站。这样的网站,按照每月收入30万来算,实在是少的不能再少了。

June 15, 2009

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

June 12, 2009

新浪科技,您这事做的也太扯淡了吧

新浪科技,做了一个"传刘晓人涉嫌集资诈骗被检察院批捕"的专题。其中红鼎投资的公司里面,我们的银杏泰克赫然在列。还给放了一大图标。

我翻了翻此专题的所有文章,这些文章都是来自传统媒体的(好像...sina是没有新闻采访权的,只有转载权吧?),所有文章都没提到我们公司。那么sina科技是从哪转载来的呢?这还真不知道。

当然了,写就写了,不过作为一个最大(新闻)门户(之一)的最大频道(曾经的之一),至少应该有基本的严谨性吧--当然,新浪科技也一直不怎么严谨--我的意思是说,消息不确认就写了,那也就罢了,但您至少应该给我们加上连接吧?一方面,这是互联网的基本道德准则,另外一方面,这也是为了方便读者嘛。要不您的读者为了知道银杏泰克是谁,还要专门去百度一下,那事实上不等于新浪科技频道为竞争对手百度创造了流量进而创造了广告收入吗?这种自毁城池,吃力扒外的事,咱不能干吧?

这篇也就算个声明吧。银杏泰克并非红鼎投资所投,而是另外一位相当负责的天使投资人投资的,公司运营状况还不错。看起来也不像要倒闭的。所以我们的客户也不用担心什么。


顺便再说一下sina科技的报道中关于我们部分的其他错误吧:

时间:2007年5月1日 <- 本公司是2007年11月成立的
融资性质:VC <- 本公司只接纳过一次天使投资(并非红鼎),没有进行过VC
涉及轮次:Series A <- 这当然也不对了,不过现在有人有兴趣投Series A,我们还是愿意谈谈的


总的来说,我对这个事情的概括是:别的无所谓,但是新浪科技欠了我们一个连接。

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 29, 2009

一次足够了

一次就够了

本来我都忘了这个故事了,不过,因缘巧合,看来还是值得讲一下。

这个故事是我和tinyfool刚刚开始创业时候的事情。众所周知,我们做了一家站内搜索的服务商,主要靠按月收服务费盈利。

某年某月某日,碰到一个客户。此客户在行业内很有名,也有钱。是某著名外企旗下网站。我们当然非常欢喜,这个生意显然是个赚钱又赚声望的事,也就特别的留意。

客户负责技术的人,曾经在某个会议上认识,这又让我们觉得不错,熟人好办事嘛。合作就这么开始了。

没花多少时间,一切顺利的上线测试。按照我们的习惯,免费试用15天,之后开始收费。这位老兄就说了,能不能多让我们试一段时间,比如*半年*什么的。我们当时就一楞,还有这么提要求的?

不过听听解释,也合理,诸如公司大,要汇报,决策比较麻烦,等等。此君几乎就是拍着胸脯说,咱是朋友嘛...

我们思量了一下,觉得也未必真能用半年时间吧,且就这样吧。反正早晚的事嘛。

随后,搜索效果很好,流量暴涨。我们的带宽被用光了,于是我们又买了一些带宽。不过付款这事看起来还是没什么消息。

后来又涨,我们实在是买不起了,于是跟对方协商,能不能先付一部分款。那位老兄也不含糊,当即答应准备一台机器,放在他们机房里,这样通过内网访问,没什么带宽压力了。

好吧,是个办法。转天,新机器就位,我们把服务部署上去了一套。

此网站更多的频道开始用上我们的搜索,客服,支持,都好一阵忙乱。我们还跑了几趟,和他们部门的一些同事开会,讨论怎么用搜索。此间再次提到付款问题,不过还是老样子,再让我们用几天。另外还应承,我们母公司还有不少其他网站,也可以介绍用你们的东西,别着急。

听起来不错。

半年时间,说快不快,说慢不慢,就这么到了。

某一天,我们突然发现流量下降了。某个频道的搜索完全没有流量了。还以为是对方网站出问题了,赶快联系。那位老兄没接电话。后来找到了他们部门的程序员,说了说情况,当时觉得有点奇怪,躲躲闪闪的。过了2天,也没见变化。我们考虑要找到这个"好朋友"问个究竟。手机仍然是没人接。于是我们请前台转他的座机试试看,竟然有人接了。

听到了我的声音,对方明显慌乱。一通问,跟我们猜的差不多。人家号称自己做了一套。不用我们的了。然后这人就又跟消失了一样了。

我这才明白,原来上来人家要先"试"半年是什么意思。

做了这么多年生意,我和tiny在这行也有点小名气。自认为朋友不少,我们两个人为人也算爽快。其实,如果开始直说"我们想自己做,付半年的钱,行不行?"那我们当然很欢迎。服务嘛,本来就是可以随时停止的,也有客户是这么做的,希望我们帮他们度过过渡期,大家合作愉快。或者,实在不愿意付钱,也直说"我们想自己做,就这半年没的用,能不能帮我个忙?"我们估计也同意。多个朋友,没坏处。

可惜,他选了最没意思的一个办法。

虽然很气人,也亏了不少钱,不过,事情最后这样了,也就这样了。我甚至没跟别人说过这事,就让他过去了吧,谁做生意还不碰几次钉子,虽然我到现在也不能理解一个大公司干嘛要占2个苦哈哈的创业者的便宜。

--------------------时间的分隔线---------------------------

为什么我今天突然想起来写这个事呢?

早晨,我接了一个朋友的电话,问我,是不是tiny比较懂iphone的开发,说他有个朋友,有一些开发问题想请教。我说没问题,tiny解答不了的,我还有几个朋友都做这个,我也可以介绍。

然后我这个朋友说,那个人说有tiny的电话,但是不太好意思打。我就多嘴问了句是谁。结果竟然是前文所述的那位"好朋友"

于是我说,这个还是算了吧,我也不想和他打交道了。我的朋友告诉我,对方说,之前合作过,但是我们能力有限,支持不了他们的应用,最后没有用。这个可给我气坏了,您占了便宜,不说好话也就算了,也没必要这么诋毁我们吧?

我说,我们系统是否能支持,我这里有当年所有服务的log,一看就知道了。没出现过不能支持的事,既然他这么说,那就让他滚蛋吧。


半个小时之后,我们公司电话响了,找tiny。这位"好朋友"终于亲自现身了,先道了个歉,说当时觉得我们的服务太贵了,所以没用。tiny说,我们的服务从来都是按照报价来的,当时你们的应该是XX元,哪里贵了?对方立刻表示是误会,误会。 (注意,半个小时之内,已经由支持不了,变成价格贵了)然后就开始请教他的iphone开发问题,还要请tiny当面给他们的程序员讲讲。

我简直被雷倒了。


后来tiny跟我说,他这样该得到教训了吧?最后还是要来找咱们。

而我估计,他不仅没得到教训,甚至连后悔都没有。因为,他占了便宜,最后还仍然可以厚着脸皮来找我们帮忙。他从来也没亏什么。

这样的事,真是一次足够了。我这辈子也不想再跟他有任何关系,我也不希望我的朋友们再遭遇一次了,故为记。

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

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

about me:
me.jpg

抓虾
订阅到有道阅读

Subscribe in NewsGator Online
Add to Google
Subscribe in Bloglines
CC License. Some rights reserved.
署名·非商业用途·保持一致
本站之所有未作特别说明的内容均使用 创作共用协议.
POWERED_BY_MT_3.2