September 04, 2006

初创网站与开源软件

前面有一篇文章中提到过开源软件,不过主要是在系统运维的角度去讲的,主要分析一些系统级的开源软件(例如bind,memcached),这里我们讨论的是用于搭建初创网站应用的开源软件(例如phpbb,phparticle),运行在Linux,MySQL,Apache,PHP,Java等下面。

创业期的网站往往采用比较简单的系统架构,或者是直接使用比较成熟的开源软件。使用开源软件的好处是搭建速度快,基本不需要开发,买个空间域名,下个软件一搭建,用个半天就搞定了,一个崭新的网站就开张了,在前期可以极大程度的节约时间成本和开发成本。

当然使用开源软件搭建应用也存在一些局限性,这是我们要重点研究的,而研究的目的就是如何在开源软件选型时以及接下来的维护过程中尽量避免。

一方面是开源软件一般只有在比较成熟的领域才有,如果是一些创新型的项目很难找到合适的开源软件,这个时候没什么好的解决办法,如果非要用开源的话一般会找一个最相似的改一下。实际上目前开源的项目也比较多了,在sf.net上可以找到各种各样的开源项目。选型的时候尽量应该选取一个程序架构比较简单的,不一定越简单越好,但一定要简单,一目了然,别用什么太高级的特性,互联网应用项目不需要太复杂的框架。原因有两个,一个是框架复杂无非是为了实现更好的可扩展性和更清晰的层次,而我们正在做的互联网应用范围一般会比开源软件设计时所考虑的范围小的多,所以有的应用会显得设计过度,另外追求完美的层次划分导致的太复杂的继承派生关系也会影响到整个系统维护的工作量。建议应用只需要包含三个层就可以了,数据(实体)层,业务逻辑层,表现层。太复杂的设计容易降低开发效率,提高维护成本,在出现性能问题或者突发事件的时候也不容易找到原因。

另外一个问题是开源软件的后期维护和继续开发可能会存在问题,这一点不是绝对的,取决于开源软件的架构是否清晰合理,扩展性好,如果是较小的改动可能一般不会存在什么问题,例如添加一项用户属性或者文章属性,但有些需求可能就不是很容易实现了。例如网站发展到一定阶段后可能会考虑扩展产品线,原来只提供一个论坛加上cms,现在要再加上商城,那用户系统就会有问题,如何解决这个问题已经不仅仅是改一下论坛或者cms就可以解决了,这个时候我们需要上升到更高的层次来考虑问题,是否需要建立针对整个网站的用户认证系统,实现单点登录,用户可以在产品间无缝切换而且保持登录状态。由于网站初始的用户数据可能大部分都存放在论坛里,这个时候我们需要把用户数据独立出来就会碰到麻烦,如何既能把用户数据独立出来又不影响论坛原有系统的继续运行会是件很头痛的事情。经过一段时间的运行,除非是特别好的设计以及比较好的维护,一般都会在论坛里存在各种各样乱七八糟的对用户信息的调用,而且是直接针对数据库的,这样如果要将用户数据移走的话要修改代码的工作量将不容忽视,而另外一个解决办法是复制一份用户数据出来,以新的用户数据库为主,论坛里的用户数据通过同步或异步的机制实现同步。最好的解决办法就是在选型时选一个数据层封装的比较好的,sql代码不要到处飞的软件,然后在维护的时候保持系统原有的优良风格,把所有涉及到数据库的操作都放到数据层或者实体层里,这样无论对数据进行什么扩展,代码修改起来都比较方便,基本不会对上层的代码产生影响。

网站访问速度问题对初创网站来说一般考虑的比较少,买个空间或者托管服务器,搭建好应用后基本上就开始运转了,只有到真正面临极大的速度访问瓶颈后才会真正对这个问题产生重视。实际上在从网站的开始阶段开始,速度问题就会一直存在,并且会随着网站的发展也不断演进。一个网站最基本的要求,就是有比较快的访问速度,没有速度,再好的内容或服务也出不来。所以,访问速度在网站初创的时候就需要考虑,无论是采用开源软件还是自己开发都需要注意,数据层尽量能够正确,高效的使用SQL。SQL包含的语法比较复杂,实现同样一个效果如果考虑到应用层的的不同实现方法,可能有好几种方法,但里面只有一种是最高效的,而通常情况下,高效的SQL一般是那个最简单的SQL。在初期这个问题可能不是特别明显,当访问量大起来以后,这个可能成为最主要的性能瓶颈,各种杂乱无章的SQL会让人看的疯掉。当然前期没注意的话后期也有解决办法,只不过可能不会解决的特别彻底,但还是要吧非常有效的提升性能。看MySQL的SlowQuery Log是一个最为简便的方法,把执行时间超过1秒的查询记录下来,然后分析,把该加的索引加上,该简单的SQL简化。另外也可以通过Showprocesslist查看当前数据库服务器的死锁进程,从而锁定导致问题的SQL语句。另外在数据库配置文件上可以做一些优化,也可以很好的提升性能,这些文章在网站也比较多,这里就不展开。

这些工作都做了以后,下面数据库如果再出现性能问题就需要考虑多台服务器了,一台服务器已经解决不了问题了,我以前的文章中也提到过,这里也不再展开。

其它解决速度问题的办法就不仅仅是在应用里面就可以实现的了,需要从更高的高度去设计系统,考虑到服务器,网络的架构,以及各种系统级应用软件的配合,这里也不再展开。

良好设计并实现的应用+中间件+良好的分布式设计的数据库+良好的系统配置+良好的服务器/网络结构,就可以支撑起一个较大规模的网站了,加上前面的几篇文章,一个小网站发展到大网站的过程基本上就齐了。这个过程会是一个充满艰辛和乐趣的过程,也是一个可以逐渐过渡的过程,主动出击,提前考虑,减少救火可以让这个过程轻松一些。

Posted by yudunde at 05:22 PM | Comments (12)

July 06, 2006

ICE-高效的中间件平台,牛刀小试

ICE(Internet Communications Engine)是ZeroC提供的一款高性能的中间件,基于ICE可以实现电信级的解决方案。前面我们提到过在设计网站架构的时候可以使用ICE实现对网站应用的基础对象操作,将基础对象操作和数据库操作封装在这一层,在业务逻辑层以及表现层(java,php,.net,python)进行更丰富的表现与操作,从而实现比较好的架构。基于ICE的数据层可以在未来方便的进行扩展。ICE支持分布式的部署管理,消息中间件,以及网格计算等等。

大道理讲完,言归正传,最近育儿网新增了不少新服务,服务间经常会需要相互调用数据,例如用户中心要取博客系统里的文章啊,论坛里发文后要在积分系统里增加用户积分啊。由于设计时这些服务仅仅基于统一的用户中心,服务间基本是独立的,所以要实现这些调用只能在每个服务上新增为其它服务提供服务的服务-_-!。这个时候有几个可选方案,我们开始选择了xml-rpc,基于http和xml的选程调用,用了一段时间,发现维护成本和访问性能都存在问题。

由于这些中间服务部署的时候是和各自所属的服务部署在一起的,对这些服务做整体的改动就非常困难,要维护起来就比较麻烦。另外由于是什么http和xml作为通信协议,由php实现业务逻辑,性能问题也很明显,而且这些http请求都会在http日志留下足迹,导致我们的日志分析很不精确。这个问题不是太大,但很郁闷,所以我们考虑使用ICE来解决这个问题,至于SOAP什么的就不考虑了,同样效率低下。

实现的过程还是比较顺利,花了三天的时间用c++实现了大部分常用的接口,服务端采用deamon的方式运行,错误日志记在syslog里(/var/log/messages),客户端PHP,编译进去了IcePHP,调用的方法很简单。现在还存在一些问题,运行的时候会异常退出,还需要一段时间来解决,暂时加了只狗看着,一旦进程里没了就重新启动。

既然要跨平台通讯,就涉及对象描述,ICE使用Slice来对结构,类,方法等进行定义。完了以后服务器端,客户端都按这个来调用和实现。ICE内置的Linux 下后台Deamon实现方案非常简单,只需要从Ice::Service里派生出一个类来,实现run方法,在这个方法里创建adapter对象,并在adapter对象里添加Servants,然后激活这个adapter就可以了,网络层的通信都由ICE接管了。由于是基于tcp/ip的直接通信,比更高层的http通信效率要高很多。

在客户端实现时,我们也碰到了一些小麻烦。一个是内置的$ICE对象用的时候有时需要用global声明,否则可能会出错,另外由于默认情况下Slice中struct对应到php的类型是一个类的实例,而不是一个数组,所以在赋值给页面的时候,smarttemplate以及其它模板系统中可能都会存在问题,可以通过修改模板系统的数据赋值显示代码解决。

我们做了一些性能的测试,同样运行1千次请求,使用xml-rpc实现需要28秒左右,使用ICE实现,只需要3秒多,性能的差距还是很大的,同时在这个过程中没发现有内存泄露的情况,效果还比较理想。

最后感慨一下,ICE是适合人类使用的中间件!

Posted by yudunde at 03:39 PM | Comments (13)

June 27, 2006

mixi.jp:使用开源软件搭建的可扩展SNS网站

于敦德 2006-6-27

Mixi目前是日本排名第三的网站,全球排名42,主要提供SNS服务:日记,群组,站内消息,评论,相册等等,是日本最大的SNS网站。Mixi从2003年12月份开始开发,由现在它的CTO - Batara Kesuma一个人焊,焊了四个月,在2004年2月份开始上线运行。两个月后就注册了1w用户,日访问量60wPV。在随后的一年里,用户增长到了21w,第二年,增长到了200w。到今年四月份已经增长到370w注册用户,并且还在以每天1.5w人的注册量增长。这些用户中70%是活跃用户(活跃用户:三天内至少登录一次的用户),平均每个用户每周在线时间为将近3个半小时。

下面我们来看它的技术架构。Mixi采用开源软件作为架构的基础:Linux 2.6,Apache 2.0,MySQL,Perl 5.8,memcached,Squid等等。到目前为止已经有100多台MySQL数据库服务器,并且在以每月10多台的速度增长。Mixi的数据库连接方式采用的是每次查询都进行连接,而不是持久连接。数据库大多数是以InnoDB方式运行。Mixi解决扩展问题主要依赖于对数据库的切分。

首先进行垂直切分,按照表的内容将不同的表划分到不同的数据库中。然后是水平切分,根据用户的ID将不同用户的内容再划分的不同的数据库中,这是比较通常的做法,也很管用。划分的关键还是在于应用中的实现,需要将操作封装在在数据层,而尽量不影响业务层。当然完全不改变逻辑层也不可能,这时候最能检验以前的设计是否到位,如果以前设计的不错,那创建连接的时候传个表名,用户ID进去差不多就解决问题了,而以前如果sql代码到处飞,或者数据层封装的不太好的话那就累了。

这样做了以后并不能从根本上解决问题,尤其是对于像mixi这种SNS网站,页面上往往需要引用大量的用户信息,好友信息,图片,文章信息,跨表,跨库操作相当多。这个时候就需要发挥memcached的作用了,用大内存把这些不变的数据全都缓存起来,而当修改时就通知cache过期,这样应用层基本上就可以解决大部分问题了,只会有很小一部分请求穿透应用层,用到数据库。Mixi的经验是平均每个页面的加载时间在0.02秒左右(当然根据页面大小情况不尽相似),可以说明这种做法是行之有效的。Mixi一共在32台机器上有缓存服务器,每个Cache Server 2G内存,这些Cache Server与App Server装在一起。因为Cache Server对CPU消耗不大,而有了Cache Server的支援,App Server对内存要求也不是太高,所以可以和平共处,更有效的利用资源。

图片的处理就显得相对简单的多了。对于mixi而言,图像主要有两部分:一部分是经常要使用到的,像用户头像,群组的头像等等,大概有100多GB,它们被Squid和CDN所缓存,命中率相对比较高;另一部分是用户上传的大量照片,它们的个体访问量相对而言比较小,命中率也比较低,使用Cache不划算,所以对于这些照片的策略是直接在用户上传的时候分发到到图片存储服务器上,在用户访问的时候直接进行访问,当然图片的位置需要在数据库中进行记录,不然找不到放在哪台服务器上就郁闷了。

文章参考:Batara Kesuma在MySQL Users Con 2006上的发言

Posted by yudunde at 08:23 PM | Comments (5) | TrackBack

FeedBurner:基于MySQL和JAVA的可扩展Web应用

于敦德 2006-6-27

FeedBurner(以下简称FB,呵呵)我想应该是大家耳熟能详的一个名字,在国内我们有一个同样的服务商,叫做FeedSky。在2004年7月份,FB的流量是300kbps,托管是5600个源,到2005年4月份,流量已经增长到5Mbps,托管了47700个源;到2005年9月份流量增长到20M,托管了109200个源,而到2006年4月份,流量已经到了115Mbps,270000个源,每天点击量一亿次。

FB的服务使用Java实现,使用了Mysql数据库。我们下面来看一下FB在发展的过程中碰到的问题,以及解决的方案。

在2004年8月份,FB的硬件设备包括3台Web服务器,3台应用服务器和两台数据库服务器,使用DNS轮循分布服务负载,将前端请求分布到三台Web服务器上。说实话,如果不考虑稳定性,给5600个源提供服务应该用不了这么多服务器。现在的问题是即使用了这么多服务器他们还是无法避免单点问题,单点问题将至少影响到1/3的用户。FB采用了监控的办法来解决,当监控到有问题出现时及时重启来避免更多用户受到影响。FB采用了Cacti(http://www.cacti.net)和Nagios(http://www.nagios.org)来做监控。

FB碰到的第二个问题是访问统计和管理。可以想象,每当我们在RSS阅读器里点击FB发布的内容,都需要做实时的统计,这个工作量是多么的巨大。大量写操作将导致系统的效率急剧下降,如果是Myisam表的话还会导致表的死锁。FB一方面采用异步写入机制,通过创建执行池来缓冲写操作;只对本日的数据进行实时统计,而以前的数据以统计结果形式存储,进而避免每次查看访问统计时的重复计算。所以每一天第一次访问统计信息时速度可能会慢,这个时候应该是FB在分析整理前一天的数据,而接下来的访问由于只针对当日数据进行分析,数据量小很多,当然也会快很多。FB的Presentation是这样写,但我发现好像我的FB里并没有今天实时的统计,也许是我观察的不够仔细-_-!

现在第三个问题出现了,由于大多数的操作都集中在主数据库上,数据库服务器的读写出现了冲突,前面提到过Myiasm类型的数据库在写入的时候会锁表,这样就导致了读写的冲突。在开始的时候由于读写操作比较少这个问题可能并不明显,但现在已经到了不能忽视的程度。解决方案是平衡读写的负载,以及扩展HibernateDaoSupport,区分只读与读写操作,以实现针对读写操作的不同处理。

现在是第四个问题:数据库全面负载过高。由于使用数据库做为缓存,同时数据库被所有的应用服务器共享,速度越来越慢,而这时数据库大小也到了Myisam的上限-4GB,FB的同学们自己都觉得自己有点懒。解决方案是使用内存做缓存,而非数据库,他们同样使用了我们前面推荐的memcached,同时他们还使用了Ehcache(http://ehcache.sourceforge.net/),一款基于Java的分布式缓存工具。

第五个问题:流行rss源带来大量重复请求,导致系统待处理请求的堆积。同时我们注意到在RSS源小图标有时候会显示有多少用户订阅了这一RSS源,这同样需要服务器去处理,而目前所有的订阅数都在同一时间进行计算,导致对系统资源的大量占用。解决方案,把计算时间错开,同时在晚间处理堆积下来的请求,但这仍然不够。

问题六:状态统计写入数据库又一次出问题了。越来越多的辅助数据(包括广告统计,文章点击统计,订阅统计)需要写入数据库,导致太多的写操作。解决方案:每天晚上处理完堆积下来的请求后对子表进行截断操作:

– FLUSH TABLES; TRUNCATE TABLE ad_stats0;

这样的操作对Master数据库是成功的,但对Slave会失败,正确的截断子表方法是:

– ALTER TABLE ad_stats TYPE=MERGE UNION=(ad_stats1,ad_stats2);

– TRUNCATE TABLE ad_stats0;

– ALTER TABLE ad_stats TYPE=MERGE UNION=(ad_stats0,ad_stats1,ad_stats2);

解决方案的另外一部分就是我们最常用的水平分割数据库。把最常用的表分出去,单独做集群,例如广告啊,订阅计算啊,

第七个问题,问题还真多,主数据库服务器的单点问题。虽然采用了Master-Slave模式,但主数据库Master和Slave都只有一台,当Master出问题的时候需要太长的时间进行Myisam的修复,而Slave又无法很快的切换成为Master。FB试了好多办法,最终的解决方案好像也不是非常完美。从他们的实验过程来看,并没有试验Master-Master的结构,我想Live Journal的Master-Master方案对他们来说应该有用,当然要实现Master-Master需要改应用,还有有些麻烦的。

第八个问题,停电!芝加哥地区的供电状况看来不是很好,不过不管好不好,做好备份是最重要的,大家各显神通吧。

这个Presentation好像比较偏重数据库,当然了,谁让这是在Mysql Con上的发言,不过总给人一种不过瘾的感觉。另外一个感觉,FB的NO们一直在救火,没有做系统的分析和设计。

最后FB的运维总监Joe Kottke给了四点建议:

1、 监控网站数据库负载。

2、 “explain”所有的SQL语句。

3、 缓存所有能缓存的东西。

4、 归档好代码。

最后,FB用到的软件都不是最新的,够用就好,包括:Tomcat5.0,Mysql 4.1,Hibernate 2.1,Spring,DBCP。

文章参考了Joe Kottke在MySQL Users Conference 2006上的发言

Posted by yudunde at 01:58 PM | Comments (7) | TrackBack

June 22, 2006

使用Red5和FFMpeg搭建在线Flash流媒体分享平台

最近视频的东西比较火,前些天我也稍微了解了一下使用开源软件建在线Flash流媒体播放平台的解决方案,还是有一些收获。

Red5是一款基于java的开源的Flash流媒体Server软件,可以作为取代Macromedia提供的商业版本FMS。Red5使用RSTP作为流媒体传输协议,内置了一些示例,这些示例实现了在线录制,flash流媒体播放,在线聊天,视频会议等一些基本的功能。由于系统本身是开源的,在碰到问题的时候也比较容易解决,大不了直接改代码,在成本方面也可以省下一笔不小的开销,为未来的功能扩展也提供了充分的空间。

如果仅仅是实现在线录制,在线播放,那么Red5也就差不多够了,但可能我们有时候还需要用户上传自己拍摄的视频文件,而要把这些视频文件转成可播放的flv文件就需要视频编码软件了。FFMpeg提供了录制,播放,视频流处理的完整解决方案。它自身也带了一个基于HTTP的流媒体广播程序以及其它几个实用的程序,但我们的重点还是它的视频转换程序,似乎Google Video也是用的它的程序作为视频转换工具。

我用FFMpeg转了几个视频,效果还可以,在声音上碰到了一些问题,在不添加参数的情况下,有一部分视频的声音会有问题,有的视频无论怎么添加参数,都出不来声音,报错提示的是不支持所带的声音采样格式,只支持几种固定的格式,我看了一下代码,确实是这样子,但理论上应该是能够解决的。FFMpeg自带的libavcodec是一套很牛的编码库,为了保证质量和性能,里面的很多codec都是从头开发的。

这两个加起来,实现一些简单的在线视频功能就差不多了。

PS:今天刚看到古永锵也开始做小视频分享网站:优酷

Posted by yudunde at 10:34 AM | Comments (2) | TrackBack

June 18, 2006

使用开源软件,设计高性能可扩展网站

2006-6-17 于敦德

上次我们以LiveJournal为例详细分析了一个小网站在一步一步的发展成为大规模的网站中性能优化的方案,以解决在发展中由于负载增长而引起的性能问题,同时在设计网站架构的时候就从根本上避免或者解决这些问题。

今天我们来看一下在网站的设计上一些通常使用的解决大规模访问,高负载的方法。我们将主要涉及到以下几方面:

1、 前端负载
2、 业务逻辑层
3、 数据层

LJ性能优化文章中我们提到对服务器分组是解决负载问题,实现无限扩展的解决方案。通常中我们会采用类似LDAP的方案来解决,这在邮件的服务器以及个人网站,博客的应用中都有使用,在Windows下面有类似的Active Directory解决方案。有的应用(例如博客或者个人网页)会要求在二级域名解析的时候就将用户定位到所属的服务器群组,这个时候请求还没到应用上面,我们需要在DNS里解决这个问题。这个时候可以用到一款软件bind dlz,这是bind的一个插件,用于取代bind的文本解析配置文件。它支持包括LDAP,BDB在内的多种数据存储方式,可以比较好的解决这个问题。

另外一种涉及到DNS的问题就是目前普遍存在的南北互联互通的问题,通过bind9内置的视图功能可以根据不同的IP来源解析出不同的结果,从而将南方的用户解析到南方的服务器,北方的用户解析到北方的服务器。这个过程中会碰到两个问题,一是取得南北IP的分布列表,二是保证南北服务器之间的通讯顺畅。第一个问题有个笨办法解决,从日志里取出所有的访问者IP,写一个脚本,从南北的服务器分别ping回去,然后分析结果,可以得到一个大致准确的列表,当然最好的办法还是直到从运营商那里拿到这份列表(update:参见这篇文章)。后一个问题解决办法比较多,最好的办法就是租用双线机房,同一台机器,双IP,南北同时接入,差一些的办法就是南北各自找机房,通过大量的测试找出中间通讯顺畅的两个机房,后一种通常来说成本较低,但效果较差,维护不便。

另外DNS负载均衡也是广泛使用的一种负载均衡方法,通过并列的多条A记录将访问随即的分布到多台前端服务器上,这种通常使用在静态页面居多的应用上,几大门户内容部分的前端很多都是用的这种方法。

用户被定位到正确的服务器群组后,应用程序就接手用户的请求,并开始沿着定义好的业务逻辑进行处理。这些请求主要包括两类静态文件(图片,js脚本,css等),动态请求。

静态请求一般使用squid进行缓存处理,可以根据应用的规模采用不同的缓存配置方案,可以是一级缓存,也可以是多级缓存,一般情况下cache的命中率可以达到70%左右,能够比较有效的提升服务器处理能力。Apache的deflate模块可以压缩传输数据,提高速度,2.0版本以后的cache模块也内置实现磁盘和内存的缓存,而不必要一定做反向代理。

动态请求目前一般有两种处理方式,一种是静态化,在页面发生变化时重新静态页面,现在大量的CMS,BBS都采用这种方案,加上cache,可以提供较快的访问速度。这种通常是写操作较少的应用比较适合的解决方案。

另一种解决办法是动态缓存,所有的访问都仍然通过应用处理,只是应用处理的时候会更多的使用内存,而不是数据库。通常访问数据库的操作是极慢的,而访问内存的操作很快,至少是一个数量级的差距,使用memcached可以实现这一解决方案,做的好的memcache甚至可以达到90%以上的缓存命中率。10年前我用的还是2M的内存,那时的一本杂事上曾经风趣的描述一对父子的对话:

儿子:爸爸,我想要1G的内存。

爸爸:儿子,不行,即使是你过生日也不行。

时至今日,大内存的成本已经完全可以承受。Google使用了大量的PC机建立集群用于数据处理,而我一直觉得,使用大内存PC可以很低成本的解决前端甚至中间的负载问题。由于PC硬盘寿命比较短,速度比较慢,CPU也稍慢,用于做web前端既便宜,又能充分发挥大内存的优势,而且坏了的话只需要替换即可,不存在数据的迁移问题。

下面就是应用的设计。应用在设计的时候应当尽量的设计成支持可扩展的数据库设计,数据库可以动态的添加,同时支持内存缓存,这样的成本是最低的。另外一种应用设计的方法是采用中间件,例如ICE。这种方案的优点是前端应用可以设计的相对简单,数据层对于前端应用透明,由ICE提供,数据库分布式的设计在后端实现,使用ICE封装后给前端应用使用,这路设计对每一部分设计的要求较低,将业务更好的分层,但由于引入了中间件,分了更多层,实现起来成本也相对较高。

在数据库的设计上一方面可以使用集群,一方面进行分组。同时在细节上将数据库优化的原则尽量应用,数据库结构和数据层应用在设计上尽量避免临时表的创建、死锁的产生。数据库优化的原则在网上比较常见,多google一下就能解决问题。在数据库的选择上可以根据自己的习惯选择,Oracle,MySQL等,并非Oracle就够解决所有的问题,也并非MySQL就代表小应用,合适的就是最好的。

前面讲的都是基于软件的性能设计方案,实际上硬件的良好搭配使用也可以有效的降低时间成本,以及开发维护成本,只是在这里我们不再展开。

网站架构的设计是一个整体的工程,在设计的时候需要考虑到性能,可括展性,硬件成本,时间成本等等,如何根据业务的定位,资金,时间,人员的条件设计合适的方案是件比较困难的事情,但多想多实践,终究会建立一套适合自己的网站设计理念,用于指导网站的设计工作,为网站的发展奠定良好的基础。

Posted by yudunde at 12:22 AM | Comments (8) | TrackBack

March 16, 2006

从LiveJournal后台发展看大规模网站性能优化方法

于敦德 2006-3-16

一、LiveJournal发展历程

LiveJournal是99年始于校园中的项目,几个人出于爱好做了这样一个应用,以实现以下功能: LiveJournal采用了大量的开源软件,甚至它本身也是一个开源软件。

在上线后,LiveJournal实现了非常快速的增长:

二、LiveJournal架构现状概况

livejournal_backend.png

三、从LiveJournal发展中学习

LiveJournal从1台服务器发展到100台服务器,这其中经历了无数的伤痛,但同时也摸索出了解决这些问题的方法,通过对LiveJournal的学习,可以让我们避免LJ曾经犯过的错误,并且从一开始就对系统进行良好的设计,以避免后期的痛苦。

下面我们一步一步看LJ发展的脚步。

1、一台服务器

一台别人捐助的服务器,LJ最初就跑在上面,就像Google开始时候用的破服务器一样,值得我们尊敬。这个阶段,LJ的人以惊人的速度熟悉的Unix的操作管理,服务器性能出现过问题,不过还好,可以通过一些小修小改应付过去。在这个阶段里LJ把CGI升级到了FastCGI。

最终问题出现了,网站越来越慢,已经无法通过优过化来解决的地步,需要更多的服务器,这时LJ开始提供付费服务,可能是想通过这些钱来购买新的服务器,以解决当时的困境。
毫无疑问,当时LJ存在巨大的单点问题,所有的东西都在那台服务器的铁皮盒子里装着。

LJ-backend-7.png

2、两台服务器

用付费服务赚来的钱LJ买了两台服务器:一台叫做Kenny的Dell 6U机器用于提供Web服务,一台叫做Cartman的Dell 6U服务器用于提供数据库服务。

LJ-backend-8.png

LJ有了更大的磁盘,更多的计算资源。但同时网络结构还是非常简单,每台机器两块网卡,Cartman通过内网为Kenny提供MySQL数据库服务。

暂时解决了负载的问题,新的问题又出现了:

3、四台服务器

又买了两台,Kyle和Stan,这次都是1U的,都用于提供Web服务。目前LJ一共有3台Web服务器和一台数据库服务器。这时需要在3台Web服务器上进行负载均横。

LJ-backend-9.png

LJ把Kenny用于外部的网关,使用mod_backhand进行负载均横。

然后问题又出现了:

4、五台服务器

又买了一台数据库服务器。在两台数据库服务器上使用了数据库同步(Mysql支持的Master-Slave模式),写操作全部针对主数据库(通过Binlog,主服务器上的写操作可以迅速同步到从服务器上),读操作在两个数据库上同时进行(也算是负载均横的一种吧)。

LJ-backend-10.png

实现同步时要注意几个事项:

5、更多服务器

有钱了,当然要多买些服务器。部署后快了没多久,又开始慢了。这次有更多的Web服务器,更多的数据库服务器,存在 IO与CPU争用。于是采用了BIG-IP作为负载均衡解决方案。

LJ-backend-11.png

6、现在我们在哪里:

LJ-backend-1.png

现在服务器基本上够了,但性能还是有问题,原因出在架构上。

数据库的架构是最大的问题。由于增加的数据库都是以Slave模式添加到应用内,这样唯一的好处就是将读操作分布到了多台机器,但这样带来的后果就是写操作被大量分发,每台机器都要执行,服务器越多,浪费就越大,随着写操作的增加,用于服务读操作的资源越来越少。

LJ-backend-2.png

由一台分布到两台

LJ-backend-3.png

最终效果

现在我们发现,我们并不需要把这些数据在如此多的服务器上都保留一份。服务器上已经做了RAID,数据库也进行了备份,这么多的备份完全是对资源的浪费,属于冗余极端过度。那为什么不把数据分布存储呢?

问题发现了,开始考虑如何解决。现在要做的就是把不同用户的数据分布到不同的服务器上进行存储,以实现数据的分布式存储,让每台机器只为相对固定的用户服务,以实现平行的架构和良好的可扩展性。

为了实现用户分组,我们需要为每一个用户分配一个组标记,用于标记此用户的数据存放在哪一组数据库服务器中。每组数据库由一个master及几个slave组成,并且slave的数量在2-3台,以实现系统资源的最合理分配,既保证数据读操作分布,又避免数据过度冗余以及同步操作对系统资源的过度消耗。

LJ-backend-4.png

由一台(一组)中心服务器提供用户分组控制。所有用户的分组信息都存储在这台机器上,所有针对用户的操作需要先查询这台机器得到用户的组号,然后再到相应的数据库组中获取数据。

这样的用户架构与目前LJ的架构已经很相像了。

在具体的实现时需要注意几个问题:

7、现在我们在哪里

LJ-backend-5.png

问题:

对于Master-Slave模式的单点问题,LJ采取了Master-Master模式来解决。所谓Master-Master实际上是人工实现的,并不是由MySQL直接提供的,实际上也就是两台机器同时是Master,也同时是Slave,互相同步。

Master-Master实现时需要注意:

解决方案:

Master-Master模式还有一种用法,这种方法与前一种相比,仍然保持两台机器的同步,但只有一台机器提供服务(读和写),在每天晚上的时候进行轮换,或者出现问题的时候进行切换。

8、现在我们在哪里

LJ-backend-6.png

现在插播一条广告,MyISAM VS InnoDB。

使用InnoDB:

使用MyISAM:

9、缓存

去年我写过一篇文章介绍memcached,它就是由LJ的团队开发的一款缓存工具,以key-value的方式将数据存储到分布的内存中。LJ缓存的数据:

如何建立缓存策略?

想缓存所有的东西?那是不可能的,我们只需要缓存已经或者可能导致系统瓶颈的地方,最大程度的提交系统运行效率。通过对MySQL的日志的分析我们可以找到缓存的对象。

缓存的缺点?

10、Web访问负载均衡

在数据包级别使用BIG-IP,但BIG-IP并不知道我们内部的处理机制,无法判断由哪台服务器对这些请求进行处理。反向代理并不能很好的起到作用,不是已经够快了,就是达不到我们想要的效果。

所以,LJ又开发了Perlbal。特点:

11、MogileFS

LJ使用开源的MogileFS作为分布式文件存储系统。MogileFS使用非常简单,它的主要设计思想是:

到目前为止就这么多了,更多文档可以在http://www.danga.com/words/找到。Danga.comLiveJournal.com的同学们拿这个文档参加了两次MySQL Con,两次OS Con,以及众多的其它会议,无私的把他们的经验分享出来,值得我们学习。在web2.0时代快速开发得到大家越来越多的重视,但良好的设计仍是每一个应用的基础,希望web2.0们在成长为Top500网站的路上,不要因为架构阻碍了网站的发展。

参考资料:http://www.danga.com/words/2005_oscon/oscon-2005.pdf

感谢向静推荐了这篇文档给我。

Posted by yudunde at 02:39 PM | Comments (15)

February 20, 2006

imageMagick图片处理工具

ImageMagick是一套Linux下的开源图形处理工具,针对几乎所有的图片格式提供比较全面的图片处理功能。不像windows下的photoshop,先要双击运行,然后打开图片,然后才能对图片进行处理,ImageMagick可以直接在命令行下运行,加上几个参数,就可以得到想要的图片了,而大批量的处理图片也比photoshop简单的多,写个shell多循环几次就可以了。

假如我想给图片加个框,转一下,再加个阴影,输入以下的命令就可以了:

convert -size 400x180 hatching.jpg  -thumbnail '200x90>' \
-bordercolor white -border 6 \
-bordercolor grey60 -border 1 \
-background none -rotate 6 \
-background black \( +clone -shadow 60x4+4+4 \) +swap \
-background none -flatten \
-depth 8 -colors 256 -quality 95 poloroid.png

结果是这样:

除了提供命令行工作,ImageMagick同样为各种语言(包括Java,perl,php,c/c++,pascal,python,ruby,tcl/tk等)提供了丰富的开发接口。开发人员可以直接使用这些接口对图片进行高质量,效果丰富的处理。这些都是ImageMagick处理的效果:


更多的效果:Anthony Thyssen写的使用帮助一些缩略图的效果一些汽泡状缩略图效果。ImageMagick还有个比较酷的功能是可以做图片的比较:
[IM Output]==>[IM Output]==>[IM Output]

ImageMagick安装起来也相当方便,以php为例,需要下载两个包,一个是ImageMagick的包,一个是MagickWand的包,打包下载后运行:

tar zxvf imagemagick.tar.gz
cd ImageMagick-xx-xx
./configure --prefix=/usr/local/ImageMagick
make && make install
export PATH=$PATH:/usr/local/ImageMagick/bin

这样ImageMagick就装完了,可以直接在任何一个目录下面运行命令行的程序了。下面我们假设机器上已经装好了某个版本的php,运行:
tar zxvf MagickWand.tar.gz
mv MagickWand-xx-xx /path/to/php-install/ext/
cd /path/to/php-install/ext/MagickWand-xx-xx/
phpize
cd /path/to/php-install
rm configure
./buildconf --force
./configure [options] --with-magickwand=/path/to/ImageMagick
make && make install

这样php就可以使用ImageMagick提供的函数了。

相关文章:
利用imagemagick來作縮圖
ImageMagick学习
ImageMagick 與 GD Library 縮圖比較
php中缩略图的问题

Posted by yudunde at 11:02 PM | Comments (1)

January 19, 2006

bind dlz - 分布式系统的请求分发工具

bind dlz全称是bind dynamic loadable zones,是基于bind的提供的一个组件,作用看名字就知道了,支持动态域加载支持。

bind已经有很久的历史,目前是搭建DNS服务器的首选。对于一般网站来说,一个标准的bind已经完全可以完成所有dns解决的工作,但在海量域名数量的情况下,bind也确实存在着一些问题:

1、域名解析信息全部存储在文本文件中,这非常容易导致由于编辑出错导致的域名解析出错。
2、bind运行时将全部的解析信息放在内存里,如果数量巨大将可能出现内存不足的情况,同时解析信息重新加载时所耗费的时间也非常值得考虑,由于加载时间较长,所以基本可以不考虑动态的进行域名的调整。

dlz就是为了解决这个问题而针对bind开发的组件,可以将域名解析信息放在数据库中,从而避免域名信息变动时重新加载的时间,在变动后马上生效。

dlz支持多种数据存储形式,包括文件系统,Berkeley-DB,Postgre-SQL,MySQL,ODBC,LDAP等等。性能的比较在这里

bind dlz这种提供动态的域名调整,并且仍然可以提供高性能的dns解析服务的特点可以应用于提供二级或三级域名服务的分布式系统的前端,对不同的域名解析到所在服务器组上,从而实现可扩展的系统架构。

Posted by yudunde at 09:12 PM | Comments (1)

January 13, 2006

使用memcached进行内存缓存

旧文重发
2005.8.9

通常的网页缓存方式有动态缓存和静态缓存等几种,在ASP.NET中已经可以实现对页面局部进行缓存,而使用memcached的缓存比ASP.NET的局部缓存更加灵活,可以缓存任意的对象,不管是否在页面上输出。而memcached最大的优点是可以分布式的部署,这对于大规模应用来说也是必不可少的要求。
LiveJournal.com使用了memcached在前端进行缓存,取得了良好的效果,而像wikipedia,sourceforge等也采用了或即将采用memcached作为缓存工具。memcached可以大规模网站应用发挥巨大的作用。

Memcached是什么?
Memcached是高性能的,分布式的内存对象缓存系统,用于在动态应用中减少数据库负载,提升访问速度。
Memcached由Danga Interactive开发,用于提升LiveJournal.com访问速度的。LJ每秒动态页面访问量几千次,用户700万。Memcached将数据库负载大幅度降低,更好的分配资源,更快速访问。

如何使用memcached-Server端?
在服务端运行:
# ./memcached -d -m 2048 -l 10.0.0.40 -p 11211
这将会启动一个占用2G内存的进程,并打开11211端口用于接收请求。由于32位系统只能处理4G内存的寻址,所以在大于4G内存使用PAE的32位服务器上可以运行2-3个进程,并在不同端口进行监听。

如何使用memcached-Client端?
在应用端包含一个用于描述Client的Class后,就可以直接使用,非常简单。
PHP Example:
$options["servers"] = array("192.168.1.41:11211", "192.168.1.42:11212");
$options["debug"] = false;
$memc = new MemCachedClient($options);
$myarr = array("one","two", 3);
$memc->set("key_one", $myarr);
$val = $memc->get("key_one");
print $val[0]."\n"; // prints 'one‘
print $val[1]."\n"; // prints 'two‘
print $val[2]."\n"; // prints 3

为什么不使用数据库做这些?

暂且不考虑使用什么样的数据库(MS-SQL, Oracle, Postgres, MysQL-InnoDB, etc..), 实现事务(ACID,Atomicity, Consistency, Isolation, and Durability )需要大量开销,特别当使用到硬盘的时候,这就意味着查询可能会阻塞。当使用不包含事务的数据库(例如Mysql-MyISAM),上面的开销不存在,但读线程又可能会被写线程阻塞。
Memcached从不阻塞,速度非常快。

为什么不使用共享内存?
最初的缓存做法是在线程内对对象进行缓存,但这样进程间就无法共享缓存,命中率非常低,导致缓存效率极低。后来出现了共享内存的缓存,多个进程或者线程共享同一块缓存,但毕竟还是只能局限在一台机器上,多台机器做相同的缓存同样是一种资源的浪费,而且命中率也比较低。
Memcached Server和Clients共同工作,实现跨服务器分布式的全局的缓存。并且可以与Web Server共同工作,Web Server对CPU要求高,对内存要求低,Memcached Server对CPU要求低,对内存要求高,所以可以搭配使用。

Mysql 4.x的缓存怎么样?
Mysql查询缓存不是很理想,因为以下几点:
当指定的表发生更新后,查询缓存会被清空。在一个大负载的系统上这样的事情发生的非常频繁,导致查询缓存效率非常低,有的情况下甚至还不如不开,因为它对cache的管理还是会有开销。
在32位机器上,Mysql对内存的操作还是被限制在4G以内,但memcached可以分布开,内存规模理论上不受限制。
Mysql上的是查询缓存,而不是对象缓存,如果在查询后还需要大量其它操作,查询缓存就帮不上忙了。
如果要缓存的数据不大,并且查询的不是非常频繁,这样的情况下可以用Mysql 查询缓存,不然的话memcached更好。

数据库同步怎么样?
这里的数据库同步是指的类似Mysql Master-Slave模式的靠日志同步实现数据库同步的机制。
你可以分布读操作,但无法分布写操作,但写操作的同步需要消耗大量的资源,而且这个开销是随着slave服务器的增长而不断增长的。
下一步是要对数据库进行水平切分,从而让不同的数据分布到不同的数据库服务器组上,从而实现分布的读写,这需要在应用中实现根据不同的数据连接不同的数据库。
当这一模式工作后(我们也推荐这样做),更多的数据库导致更多的让人头疼的硬件错误。
Memcached可以有效的降低对数据库的访问,让数据库用主要的精力来做不频繁的写操作,而这是数据库自己控制的,很少会自己阻塞 自己。

Memcached快吗?

非常快,它使用libevent,可以应付任意数量打开的连接(使用epoll,而非poll),使用非阻塞网络IO,分布式散列对象到不同的服务器,查询复杂度是O(1)。

参考资料:
Distributed Caching with Memcached | Linux Journal
http://www.danga.com/
http://www.linuxjournal.com/article/7451

Posted by yudunde at 02:58 AM | Comments (3)

November 09, 2005

CMS系统的演进

CMS即Content Management System,一般用于网站的内容组织发布。不严格的意义上来看,博客系统也可以算是一个小型的CMS系统。最近做了一个小的CMS系统,感悟不少。

CMS最基本的功能当然是文章发布系统,后台提供一个文章管理的功能,前面将文章显示出来,按照栏目进行组织。当然,栏目,用户,权限管理等基本功能也是必不可少。

开始文章的显示是动态的,每次有人看都执行一下,然后把页面显示出来。后来发布动态的发布虽然实现简单,但即有着一些天然的缺陷。例如抵御大规模的访问,虽然可以通过缓存来进行解决,但毕竟无法从根本上解决这个问题。还有就是文章浏览与管理集中,依赖于同一数据源,一台数据源出现问题两个服务都无法正常提供。在这个条件下很多CMS就提供了静态化的发布方式,文章以静态文件发布出去后与CMS系统没有直接的关系,无论是访问速度,还是可靠性都得到大幅的提高。

Content的指的是内容,并不单纯是文章,而互联网的逐步发展使用户已经不满足于简单的文本阅读,于是CMS又添加了图文混排的功能,开始是单图,然后进一步是组图。
图片加上了以后,很多网站发布每天发布这么多文章实际上有很多文章是转载来的,如果能够自动的将别人网站的文章抓过来,编辑打勾就可以直接发布效率就高的多了,于是各种抓站系统又成了CMS的标准配置。这里面值得称道的是donews的CMS系统,看到一个喜欢的网页,直接右键保存,系统可以自动分析html页面,并将关键数据取出,点一下确定就可以发布,实在是非常方便。而且可以自动取出关键字,并在文章之间根据关键词形成关联。

在这个过程中模板系统也逐渐产生了。以前的模板多是由技术人员手工开发。例如做一个首页,页面上各个区块的逻辑确定后都手动写代码,写死后很难改变。这样子倒没有错误,只是模板制作效率非常低下,新做或修改模板非常麻烦。在这个条件下就促使开发人员将模板做进一步处理。模板一般会被切分成碎片,碎片有几种类型,文本,图片,广告,列表。前面三种都是简单的对html进行分块处理,列表是动态的功能,负责在发布的时候动态的组织内容。这样子就很方便了,可以很快的做出一个模板,加上模板复制的功能就更加如虎添翼。

目前新浪的CMS是C++做的,重点强调数据结构的丰富,功能非常强大,评论系统做的也不错。评论系统做的最好的是网易,它的延伸阅读做的也不错。Sohu的图库做的不错。

以后CMS发展的方向我认为很大的一方面是内容的相关组织。目前各大网站最常用的提高访问量的招数就是在文章页底添加相关链接,一般是比较火暴的文章和图片,提升访问量非常显著。但这种方式比较死板,每篇文章下面的内容都一样,用户点了一次就不会点第二次。理想的效果是在文章下面添加相关的文章,而且不仅仅局限于CMS内部,应该将站外的相关内容都添加进去,只要用户觉得方便,别怕他不回来。

另外一个方向就是内容来源多样化。例如新浪的CMS,目前似乎只能对手发文章进行比较好的管理,对于iask的内容,对于论坛的内容都是手动的编辑,目前似乎还能满足需要,但从长期来看,是肯定要变的。如果没有RSS的出现,内容来源的多样化还是一句空话,RSS出现后,站内的内容,站外的内容都可以通过RSS来传递。

互联网的入口开始是门户,后来成了搜索引擎,就是因为搜索引擎可以提供来源多样化的内容,一个网站再大也只是一个网站,做再多的频道也就是一个网站,无法满足用户对于多样化,个性化的需要。未来的入口是什么,现在还说不定,但肯定是要对用户的胃口,不能我给你什么,你就看什么,而是看谁能提供给我最想看的东西,让我最少的动脑动手。

我认为未来内容分化为两部分的趋势会逐渐显现出来。一部分提供底层的内容,博客服务提供商以及社区服务提供商会是主力,门户也是重要组成部分;另外一部分仅仅对内容进行组织,充当入口,只提供内容的链接,并不实质的存放内容。

Google的ig是一个例子,微软的Live站略中RSS Live也是重中之重,Yahoo最近也提供了个性化的门户,这些都是引子,慢慢的戏会越来越好看。

发文庆祝CMS完成,小小的展望一下未来,继续做事。

Posted by yudunde at 09:53 PM | Comments (16) | TrackBack

January 30, 2005

Skype的主要功能-Skype通讯协议分析(3)

Skype的功能主要可以分为:初始化,登录,用户搜索,呼叫建立与终止,媒体传输和状态消息。

1、初始化
第一次安装后,Skype会发送一段HTTP 1.1的请求给中央服务器,包括关键字"installed"以及所装Skype的版本号。以后的每次登录Skype都会向中央服务器发送一小段包含关键字"getlatestversion"的HTTP 1.1请求,检查是否有新版本的Skype。

2、登录
登录可能是Skype最重要的功能。在这个过程中,Skype终端到登录服务器上验证用户名密码,广播他在上线给好友及其它的点,检查NAT和防火墙的类型,发现拥有公网IP地址的在线Skype节点,这些新发现的节被用于在所在Super Node无法使用后继续保持本机与Skype网络的连接。

登录的过程我们前面已经讲过,先用UDP连,然后是TCP,然后TCP到80,然后TCP到443,行的话就连上了,不行的话就显示无法登录。连接的对象是保存在本机中Host Cache中的。

登录服务器的IP是80.160.91.11,nslookup记录显示它的域名是:ns14.inet.tele.dk和ns15.inet.tele.dk,dk是丹麦的国家定级域名。

安装完第一次登录时,HC被初始化,里面包含7对IP与端口,而且基本总是这7个IP和端口,即使包含超过7对,这7对也在其中。当用户安装后第一次登录时,Skype通过其中的一对IP和端口建立TCP连接。

这7个IP-端口对,以及这些IP对应的主机名是:
IP address:port Reverse lookup result
66.235.180.9:33033 sls-cb10p6.dca2.superb.net
66.235.181.9:33033 ip9.181.susc.suscom.net
80.161.91.25:33033 0x50a15b19.boanxx15.adsl-dhcp.tele.dk
80.160.91.12:33033 0x50a15b0c.albnxx9.adsl-dhcp.tele.dk
64.246.49.60:33033 rs-64-246-49-60.ev1.net
64.246.49.61:33033 rs-64-246-49-61.ev1.net
64.246.48.23:33033 ns2.ev1.net
可以看到上述的主机分别属于4个ISP,其中Superb , Suscom, ev1.net是美国的ISP。

Posted by yudunde at 08:41 PM

Skype的主要组成部分-Skype通讯协议分析(2)

1、端口
在Skype的连接属性对话框中可以设置监听的端口号,在安装的时候Skype会随机的选择一个端口作为监听的端口,这一点与HTTP协议等不同,Skype没有默认的服务端口。同时,它还会打开对80和443端口的监听。80是常见的HTTP服务默认端口,而443则是HTTPS服务的默认端口。
2、主机列表(HC,Host Cache)
这里的主机指的是可以提供踏板及广播服务的Super Node(SN)。通常它被存储在注册表里的:HKEY_CURRENT_USER / SOFTWARE / SKYPE / PHONE / LIB / CONNECTION / HOSTCACHE 中.一般情况下,运行两天后,HC中会有约200个机器地址及对应的端口号。

3、编解码器
要能语音通信,编解码器当然少不了。Global IP Sound在他的网站上专用明它为Skype提供点对点语音通讯软件:Global IP Sound provides voice processing software to Skype's peer-to-peer voice-communications software.Skype应该是使用了他们的编解码器实现的语音通讯。

4、好友列表
当你换了一台计算机的时候可能会发现Skype上的好友列表没了,不要奇怪,Skype的好友列表没有保存在服务器上,而是保存在本地的注册表中,当然,是加过密的。

5、加密
Skype使用AES加密标准。

6、NAT与防火墙
Skype应该是使用了STUN和TURN协议来检测所处的NAT及防火墙环境。Skype定期的刷新这些信息,这些信息也是存储在注册表中的。与另外一个点对点文件共享系统Kazza不同,普通客户端无法阻止自己成为Super Node(SN),就是说它随时可能被征用成为别人登录服务和广播服务的提供者,就是类似于BT中的种子提供者的角色。

参考资料:

1、《An Analysis of the Skype Peer-to-Peer Internet Telephony Protocol》.

Posted by yudunde at 01:51 PM | Comments (3)

Skype通讯协议分析(1)

晚上在看Salman A. Baset和Henning Schulzrinne写的《An Analysis of the Skype Peer-to-Peer Internet Telephony Protocol》。因为Skype的通讯协议是不公开的,而且通讯内容是加过密的,这两位完全在实验的基础上对Skype的通讯机制进行分析,分析结果很有参考价值。

通过分析得出的结论主要有三个:
1、Skype的通话质量较MSN和Yahoo的即时通信工具要好;
2、可以无缝的在NATs和防火墙后使用;
3、安装使用起来确实非常简单。

skype_network.gif

Skype与以往MSN等IM工具最大的不同在于基除了用户登录,其余工作基本不依赖中央服务器,Skype在穿透防火墙通讯时完全使用了Peer to Peer,而没用到中央服务器。上图中的小黑点是客户端,大黑点是超级节点(用于为其它客户端提供登录踏板及广播服务),灰色的点是Skype的登录服务器。
用户下载安装完Skype后,Skype客户端会发送一段HTTP 1.1的请求到中央服务器,告诉它我装完了一个什么样的版本:

GET /ui/0/97/en/installed HTTP/1.1
User-Agent: Skype™ Beta 0.97
Host: ui.skype.com
Cache-Control: no-cache

服务器会返回一个200 OK的信息:

HTTP/1.1 200 OK
Date: Tue, 20 Apr 2004 04:51:39 GMT
Server: Apache/2.0.47 (Debian GNU/Linux) PHP/4.3.5
mod_ssl/2.0.47 OpenSSL/0.9.7b
X-Powered-By: PHP/4.3.5
Cache-control: no-cache, must revalidate
Pragma: no-cache
Expires: 0
Content-Length: 0
Content-Type: text/html; charset=utf-8
Content-Language: en

客户端会进行登录初始化工作,这一步工作包含很多内容,针对三种不同类型的网络情况有三种不同的登录方式:
1、直接有公众网的IP
2、在内部网,可以通过TCP访问外部网络
3、在内部网,但只能通过有限的几个端口(例如80和443)访问外部网络

Skype在登录的时候会先使用UDP请求HC中的IP,如果不行,就用TCP请求HC中的IP及端口,如果还不行,就用TCP请求HC中的IP及80端口,如果又不行,就再请求HC中的IP及443端口。如果这时候还不行,那就登录不了了。整个过程中传输的数据量大概在8k-10k,持续的时间在3至35秒。

明天继续。

参考资料:
1、《An Analysis of the Skype Peer-to-Peer Internet Telephony Protocol》.

Posted by yudunde at 01:50 AM

January 29, 2005

awk学习笔记

awk是一种用于处理数据和生成报告的编程语言。
一般的使用格式是:

awk '{pattern + action}' {filenames}

pattern指在每一行中进行匹配的条件,action指针对符合条件的行进行的操作,filenames是输入的文件名。
假设data文件中有以下数据:
1 donald 305 20050129
2 chin 102 20040129
3 mark 304 20040229

下面对这个文件进行操作:
awk '{print $1, $2, $3, $4}' data

输出:
1 donald 305 20050129
2 chin 102 20040129
3 mark 304 20040229



awk '{print $1"\t", $2"\t", $3"\t", $4}' data

输出:
1 donald 305 20050129
2 chin 102 20040129
3 mark 304 20040229

这里显示的格式并不是输出的真实格式,真实格式是中间会有一个tab符号,格式对的很整齐,只不过在html里看不出来。


awk '/donald/ {print $4}' data
匹配当data文件中包含字符串"donald"的行,输出第4列的值:
20050129

awk '/donald|chin/ {print $1, $2}' data
这里的"|"应该是或的作用,而不是管道,输出:
1 donald
2 chin

awk '/a[rl]/ {print $1, $2}' data
兼容perl的正则表达式,匹配包含"ar"或"al"的列,输出:
1 donald
3 mark

awk '/a[rl]/ {print $1, $2, $3+1}' data
给第三列加上1再输出:
1 donald 306
3 mark 305

awk '/a[rl]/ {print $1, $2} {print $3+1}' data
匹配只对第一对花括号产生作用,输出:
1 donald
306
103
3 mark
305

awk 'FS="n" {print $1}' data
使用"n"而不是空格做为分隔符,输出:
1
2 chi
3 mark 304 20040229

awk 'FS="n" {OFS="-"} {print $1, $2}' data
把分隔符输出:
1-donald
2 chi- 102 20040129
3 mark 304 20040229-

awk 'FS="n" {OFS="-"} {print NR, $1, $2}' data
使用NR变量,num of row,即行号,输出:
1-1-donald
2-2 chi- 102 20040129
3-3 mark 304 20040229-

awk '{x=x+$3} {print NR, $3, x}' data
使用变量进行累加计算,输出:
1 305 305
2 102 407
3 304 711

awk '{x=x+$3} END {print NR, $3, x}' data
使用BEGIN和END在处理之前或之后显示结果,输出:
3 304 711

awk '{x=x+$3} {print NR, $3, x | "sort -nr"}' data
在awk内使用管道进行排序,输出:
3 304 711
2 102 407
1 305 305

cat command
{x=x+$3}
{print NR, $3, x | "sort -nr"}
awk -f command data
将指定写在文件中,输出:
3 304 711
2 102 407
1 305 305

如果简单的输出不能处理您的程序中所需要的复杂信息,则可以尝试由 printf 命令获得的更加复杂的输出,其语法是

printf( format, value, value ...)

该语法类似于 C 语言中的 printf 命令,而格式的规格是相同的。通过插入一项定义如何打印数值的规格,可以定义该格式。格式规格包含一个跟有字母的 %。类似于打印命令,printf 不必包含在圆括号中,但是可以认为使用圆括号是一种良好的习惯。

下表列出 printf 命令提供的各种规格。

规格 说明
%c 打印单个 ASCII 字符
%d 打印十进制数
%e 打印数字的科学计数表示
%f 打印浮点表示
%g 打印 %e 或 %f;两种方式都更简短
%o 打印无符号的八进制数
s 打印 ASCII 字符串
%x 打印无符号的十六进制数
%% 打印百分号;不执行转换

可以在 % 与字符之间提供某些附加的格式化参数。这些参数进一步改进数值的打印方式:

参数 说明
- 将字段中的表达式向左对齐
,width 根据需要将字段补齐到指定宽度(前导零使用零将字段补齐)
.prec 小数点右面数字的最大字符串宽度或最大数量

参考资料:
AWK:Linux 管理员的智能工具包

Posted by yudunde at 08:40 PM | Comments (4) | TrackBack