« April 2009 | Main | June 2009 »

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在保护客户利益上做的比别人多一些,选择我们的服务是可以放心的。

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