关键字包括:nginx,lighttpd,quixote,Memcached,mogile FS,Mako,Gentoo Linux,Xapian,spread
ps:窃以为第一段关于语言的采访,相当[csdn]化
你要是愿意,就买一枝三块钱的玫瑰,送给我吧,这城市也是怪让人伤心的,我想死心塌地的爱上你”
这是一个叫钟童茜的歌手的歌,我在豆瓣网站发现有人评论,才知道了这首有些凄凉的歌曲。你几乎不可能从百度的最流行的mp3的列表中找到它,因为它不是那么有名,也许是这个原因,引发了我采访豆瓣的愿望。接受我采访的是,豆瓣网站的技术总监洪强宁先生和产品经理张贝宁女士。
本刊记者:好,现在开始,豆瓣是一个非常著名的Web2.0网站,你们的开发语言选择的是Python,我想问的是,为什么选择python?
洪强宁:我们选择Python的理由是它是动态语言,具有动态语言的优点,比如开发特别迅速。我们做的是一个Web2.0的网站,这种网站的特点就是always beta,用户的需求在随时发生变化,我们也不断发现新的价值。所以网站的结构和程序会不断变化,如果用Java做,你的开发量比较大,你就难以做出迅速地改变。Python的特点就是开发迅速,你可以在一两个小时,就做出一个功能。或者说已??上线了,用户反映需要某一功能,也可以比较快地做出来。
本刊记者:这就是TDD,敏捷开发的思路,和传统的方式有些不同。但是会有另一方面的问题,Python的程序员好找吗?在国内会Python的要比会Java程序员少的多。
洪强宁:对,确实是。在中国用Python的人确实不多,也给我们寻找开发任何人员带来困难。不过从另一方面说,也有好处,因为没有一个学校去教Python,会Python的人都是自己学的,也就是说他知道自己需要什么技术,而且能够通过自学掌握它,包括Python的资料中文比较少,需要学习者接触第一手资料,这都使得Python程序员的平均水平,要比使用其他热门语言的平均水平要高。另一方面Python也越来越流行,在国外比较流行的动态语言有Perl,和Python,现在Python已经超过了Perl。
本刊记者:不过,在Web开发这方面有许多选择,比如,Java,.NET,和PHP,在这个格局里Python还是比较弱势。
洪强宁:对,当然,它是新兴语言。在未来,我相信,至少在在Web2.0网站开发方面,它会有自己的一个位置。
本刊记者:还有问一个问题,Python与Perl比较怎么样?因为Python的面向对象的特性好一些,代码看起来更容易理解一些吧,我以前是用 Perl写程序的,觉得Perl的程序代码看起来比较乱。
洪强宁:对,Perl 是write once风格的,一个人写完了,过一段时间,可能自己都不能看懂,它确实很强大,但比较适合当作个人工具使用,不太适合团队的开发。Python的哲学是解决问题的最好方式只有一种,这样同样的功能,每个人写出来的程序样子应该差不太多,比较易于理解,更适合团队开发。
本刊记者:还有一个问题,,有一种说法,认为Python比较慢,在性能方面会不会有问题?
洪强宁:这个问题可以分两个方面说,首先,说Python慢,这是和编译语言比,比如与C,C++,Java比,在动态语言中,它并不慢,它比Ruby要快,它和Perl性能相当。如果选择动态语言的话,Python并不是很慢。另一方面,如果做网站开发,语言的不是速度的瓶颈,比如我把我们现在用Python写的程序全部用C写,程序当然会快一点,但是改变不是很大。Web网站一般会有很多对IO的操作,比如对数据库的访问,对硬盘的访问响应用户的请求,80%,90%你的时间都花在IO上,语言的速度,相对而言,不是那么重要。也可以这样说,网站的性能主要取决于架构设计的是否合理。因为网站需要响应大量的并发的请求,如果你的设计的不好,即使你用C写的,也可能无法应付。所以更多的考虑是在架构设计上,要使架构体系不会产生速度瓶颈。
本刊记者:那您能简要地介绍一下豆瓣的架构吗?
洪强宁:关于豆瓣的系统架构图,首先我们在Web server上做个划分,把网站内容分为动态内容和静态内容。在豆瓣上所有的html都是动态内容,图片都是静态内容。分成两个Web 服务可以做不同的调优。 对动态内容,我们用的是nginx和lighttpd的混合,nginx做负载的平衡,lighttpd通过 SCGi 与application server相连,application server是基于 quixote这个框架写的。
application server拿到用户的请求,分析用户的url,并且利用外部的资源,比如数据库,组合成一个html,返回。从数据库存取会比较慢,数据库有大量的IO,我们使用cache,我们使用的是Memcached,这是一个分布式的内存的cache,比如你可以用很多机器,每个机器有两个G的内存,我们自己开发了client端来使用它,另外如果用户有搜索请求,我们会用搜索引擎。Xapian是一个C++写的开源的搜索引擎,我们通过Web service去访问它。其他,我们还提供了另外的Web service接口响应用户的请求,比如要访问某个文件。spread是我们最近加了一部分,用户有的请求可以采用这样的异步服务。
数据库是这样的,两个MySQL做成一对,一个master ,一个 slave,根据应用划分,使得load不会太高。这个图上??的是两对,实际上有三对。还有一个slave,一方面作为备份,一方面用作数据挖掘,因为不能对线上的数据做直接操作。
对于静态部分,我们也是用nginx,你注意到豆瓣现在有日记的贴图功能系统,用户可能上传很多图片,我们采用的方案是用了mogile FS ,这是一个分布式的文件系统,同时可以做备份,保持高可用性,可以提高很大的IO。
关于application server,它都是用Python写的。我们是用的MVC方式,Controller我们用的是quixote ,它接受用户的请求,根据这个URL去找到Model的某个具体的函数来执行,它是一个dispatcher,当中会判断用户的权限等。然后再传给View,View根据模版进行渲染,形成网页。View的模版,我们以前是用的是PTL,PTL很高效,最近引用了mako,这是一个比较现代的开源的模版,用它写出的代码比较好维护,比PTL好维护一些.。同时,在使用mako的同时,我们的工程师做了很多加速的工作,现在mako的代码有很多是豆瓣的人写的。
你如果注意过Python的Web开发框架的话,你会发现Python的有三个比较著名的框架,Django,Pylons,TurboGears,Pylons默认的模版就是Mako。
下面的就是Model,业务模块,核心是类是User,因为Web2.0是以人为本,我们肯定会有一个User。只有人也做不了事情,还要有物。豆瓣的物,就是Subject,比如书,比如评论,比如小组等。
与数据库进行链接,我们一个很轻量级的与数据库进行链接,这也是一个开源项目,SQL Farm Manager。这个Web service,豆瓣中有很多用的都是Web service。
本刊记者:好,还想问您一个问题,Web2.0会不会也在架构设计中也有所体现呢 ?
洪强宁: Web2.0用户的反复的操作非常多,你需要一个非常流畅的体现。这需要一些技术来实现,比如Ajax;豆瓣花了很多钱很多精力,来提高性能,比如买好的机器,使用Gentoo linux,为什么使用Gentoo Linux,因为它方便调优。还有,大量的使用cache。在数据库调优方面,我们也花了很大的精力。
另一方面,Web 2,0是用户提供数据的,用户有很多写操作。这样很多1.0优化方法在2.0中行不通。豆瓣在数据库上用的是分库的方式。除此之外我们还尝试了一些其他的方法。
本刊记者:我现在想问张贝宁一个问题,您能否谈一下Web2.0社区网站和传统的社区网站的区别?比如天涯论坛,和豆瓣的区别。
张贝宁:先说一下Web 2.0 的概念,传统网站,用户到这些网站,只是看信息,这些信息是怎么来的呢,比如像Google,它是抓来的,或者像新浪这样的门户网站,是用户给你编好的。你到这样的网站,只是获取信息,你不能创造信息,也不能决定它放的位置。按照业界的理解,Web 2.0相对于Web 1.0,它是以用户为中心的,或者说是以用户创造内容为主,并且可以决定展现方式。你刚才说的传统的社区,在某种程度上,也可以说是2.0的,因为它也由用户提供内容。不过早期的BBS,网站以内容作分类,比如体育,军事,文学等。用户不能形成自己的分类。在豆瓣,用户可以对任何一个话题进行讨论,这完全是用户自主的。这还只是关系到豆瓣的小组的功能,如果拿天涯论坛和豆瓣做比较的话,豆瓣与天涯这样的BBS不同还在于,它首先有一个物的概念,比如书,音乐,和电影。
本刊记者:我也发现了这点。这样的组织方式,给人的感觉会非常不同。比如我们要查找对余华的小说《活着》的评论,在豆瓣就比较容易找到认真,有质量的评论。而在传统的BBS上,你只能用查找的方式,搜索“活着”这个词,找出的东西,也可能还不是谈论《活着》这本小说的,而只是其中的文本包含了“活着”这个词,而且有很多无意义的吵架帖。豆瓣的组织方式,让人感觉很严肃,雅气。不过,我也发现了一个或许有些不便的地方,比如,我要在讨论德里达的小组回帖,在一般的BBS可以匿名,或具有一个ID就行了,但在豆瓣,我要首先参加德里达这个小组。
张贝宁:对,是这样的。豆瓣更关心的是人群,就是对同一话题和事物有兴趣的人群,而不是帖子,这与传统的BBS确实有一些区别。
本刊记者:好,就到这里。谢谢你们两位能够接受我的采访,分享你们的经验与思想。
----------------------------------------------------------------
这次的 QCon 会议,《豆瓣网技术架构的发展历程》这个议题差不多是最受关注的。洪强宁在演讲开始告诫大家期望值不要太高,我还是相信不会有人觉得失望的。
先说几句题外话,整个演讲听下来,我们会发现豆瓣在发展的过程中也是有点弯路,这些是一个网站发展过程中的宝贵财富,能把自己有周折的地方大大方方的拿出来,是难能可贵的事情。尽管豆瓣批露了很多架构细节出来,也不会(也不可能)有哪个公司一拿到这些东西,就能照猫画虎再做一个豆瓣并且超过豆瓣。从某种程度上来说这体现了豆瓣同学们的气度,这是令国内大多数公司汗颜的。很多公司只愿索取,而不愿奉献哪怕一点点出来,用这样封闭的心态对待技术其实是小家子气,守财奴的思维。技术只有为更多人所用才是大道。
议论说完,再来叙述。写点对豆瓣架构的体会。戏法人人会变,各有巧妙不同。有些东西大家都在用(Nginx),但是有人的用得好,有人用了比不用还差。所以,需要逐渐总结,改进。学习别人的架构设计,不是要照搬,而是借鉴其思想。
技术的选择
一直以来,豆瓣在技术上都给人很前卫的感觉,看起来好像什么新用什么,其实是不是的,他们一直是"用已掌握的技术解决问题",现有的东西如果够用,那么就没必要一定迁移到新的上面去,而转换往往是为了解决当前问题。另外,换用新的东西,要有足够的驾驭能力,从演讲中得知,豆瓣曾有几次在临上线前发现基础库的Bug(比如 Libmemcached的一致性哈希相关的Bug),技术团队能在第一时间有进行修复并且提交给开源社区。否则的话,就变成了一种错误决策了。
磁盘转速
小话题。如果可能,直接买 15000 转的磁盘好了。10000 转的磁盘可能省钱,但这东西部署了之后几乎就不太可能升级。所以,如果是初创公司,我的建议就是买高速磁盘,因为业务如果发展快了的话,先前对机器的定位也可能发生变化。
杜绝远程 I/O
在普通的 TCP/IP 网络的环境下,不要进行远程数据写入操作。跨网络操作的延时看似没什么大不了的,但一旦达到临界点就回天乏术。这个事情基本是不撞南墙不回头,有的技术人员总要亲身体验一把才肯罢休。
持续保持 URL 友好风格
演讲中有多次提到一致性 URL ,其实体现了豆瓣对 URL Rewrite 的重视,结构调整,或者应用程序变化的时候,URL 最好做到"用户友好"的。这算是"软技术",但是应该加以最大的重视。
数据库复制延迟问题
对于 mysql 复制的环境,如果Slave 上有读取操作,那么有些情况下可能因为 Master 和 Slave 节点数据不一致对用户造成困惑。如果从一致性的角度上考虑,其实也不复杂:,只需要对"知道数据发生了变化的用户"提供一致性就行了(基本上就是发起变更的用户),不知道数据发生变化的用户对数据的不一致有一定的"容忍程度",当然说着简单,实现起来还是需要技巧和精巧的。
大量小文件同步问题:Merkle tree
关于大量小文件的同步问题,很多上了规模的网站都会遇到,如果设计得不好或者是比较偷懒,用传统的办法(比如 rsync 之类的老模式)很容易触发问题,也浪费资源。DoubanFS 是用Merkle tree(Hash Tree)的方式进行数据同步的。对这个问题的具体描述可以参见《大量小文件的实时同步方案》。Merkle Tree 是个很精巧的思路,ZFS 在用(refer),Amazon Dynamo 系统也在用。
不会一会儿又有人留言说:我们早就采用这个思路了...... 我这里预先来句回答:拜托,你早点共享啊?
豆瓣网架构-国内python语言网站的王者之路
豆瓣网对互联网用户来说是知名的Web 2.0社区,但对开发者而言,更重要的是一个应用Python打造的非常成功的Web 2.0站点。豆瓣网已经达到了300万注册用户,另外还有千万级的非注册用户。访问量每天则超过两千万。
豆瓣Python应用开发经验谈
豆瓣是一个Web 2.0网站,这类网站的特点就是“Always Beta”,不断有新的产品和功能升级来为用户提供更好的服务。作为使用Python进行开发的网站,豆瓣有效的程序开发配置和版本控制值得我们学习。
豆瓣的主要开发环境配置就是SVN+Trac+Bitten。豆瓣的版本管理系统使用的是Subversion(SVN),使用Trac来管理协同开发,同时使用Trac的Bitten插件进行持续集成。
在开发模式方面,由于是Always Beta,豆瓣采用的方式是:站点运行在主分支上,开发者在开发新功能时会建立一个子分支,新功能开发并测试完成后,会更新服务器的主分支版本,之后上线。
在开发框架方面,豆瓣主要使用Quixote(被称之为“堂吉诃德”,一个轻量级的Python Web框架,简单、高效,代码简洁);后台运行的Web服务主要使用Web.py(web.py也是一个Python的Web框架,简单且功能强大)。
豆瓣网可分割成两大块:一块是前端的Web,也就是用户在浏览器访问的时候会触发一系列的操作,从数据库拿出数据,渲染成HTML页面反馈给用户,这是前端;另外一块是后端,在豆瓣有一个很强的数据挖掘团队,每天把用户产生的数据进行分析,进行组合,然后产生出用户推荐,然后放在数据库里面,前端会实时的抓取这些数据显示给用户。
豆瓣(架构)设计现在在WEB这一端主要是用这么几种技术:前端是nginx和lighttpd,中间是Quixote的Web框架,后面是MySQL以及我们自己开发的DoubanDB。这些除了Quixote都是一些比较流行的、尖端的技术。Quixote稍微老一点,如果要重新设计的话,可能会在这方面做一些考虑。比如Python社区中的Django、Pylons等等都是可以考虑的,那么在豆瓣的内部的话,我们一般是用web.py,很轻量的一个Web框架来做,也是非常不错的选择,它可能需要自己做的事情多一点。但是,也不太可能完全重新设计了。
豆瓣网可分割成两大块:一块是前端的Web,也就是用户在浏览器访问的时候会触发一系列的操作,从数据库拿出数据,渲染成HTML页面反馈给用户,这是前端;另外一块是后端,在豆瓣有一个很强的数据挖掘团队,每天把用户产生的数据进行分析,进行组合,然后产生出用户推荐,然后放在数据库里面,前端会实时的抓取这些数据显示给用户。
豆瓣(架构)设计现在在WEB这一端主要是用这么几种技术:前端是nginx和lighttpd,中间是Quixote的Web框架,后面是MySQL以及我们自己开发的DoubanDB。这些除了Quixote都是一些比较流行的、尖端的技术。Quixote稍微老一点,如果要重新设计的话,可能会在这方面做一些考虑。比如Python社区中的Django、Pylons等等都是可以考虑的,那么在豆瓣的内部的话,我们一般是用web.py,很轻量的一个Web框架来做,也是非常不错的选择,它可能需要自己做的事情多一点。
豆瓣现在还没有达到数据库分片的程度。最常见的手段是,按照功能分区。我们会把数据表分成几个独立的库,现在是一共有4个库。每个表都是库的一个部分,每个库会有主副两个。通过这种方式来减轻数据库的压力,当然这个是现在的方案,再往后的话,表的行数会增长,到达一定的程度后,还要进行水平分割,这是肯定的。然后我们现在的技术方面,在操作数据库之前,首先获取数据库的游标,有一个方法,这个方法会干所有的事情,我们以后做的时候会从这个方法中进行判断该从哪取东西。这个架构已经在了,只是现在还没有做这一步而已。
数据库这边主要采用什么解决方案呢?
在数据库这边,我们主要用的是MySQL。MySQL有一个问题,大文本字段会影响它的性能。如果数据量过大的话,它会挤占索引的内存。那么现在一个行之有效的方法是,我们另外建立一套可伸缩的Key-Value数据库,叫做DoubanDB。我们把不需要索引的大文本字段,放到DoubanDB里面去。MySQL只保存需要索引的Relationship这方面的信息。这样给MySQL数据库降低了压力,也就可以保证它的性能。
比如说像保证数据的安全性,以及数据库的吞吐量,豆瓣是怎样的策略呢?
首先DoubanDB会把每个数据在三个节点进行备份,任何一个出现故障都不会影响索取数据。MySQL是通过双Master方案,同时还会带1到2个slave,所以说在MySQL中我们会有三到四个的备份。这点是可以放心的。
你刚才说到MySQL的双Master方案,这方面会不会存在什么问题?比如说同步的问题,等等?
在MySQL里面,双Master方案是一个比较经典的方案,我们现在用它很大一部分是为了解决我们同步延迟的问题。在做切换的时候,会出现同步延迟的问题,但其实MySQL的同步速度还是可以的,在切换的时候,我们会忍受几秒钟等待同步的时间。在做脚本的切换的时候,我们会稍微等一下。
豆瓣的数据表一般是怎么样的规模?
数据表,这个不好说了,因为不同的表都是不一样的。我们最大的表是“九点”的Entry表,“九点”的爬虫爬过来的所有的文章,现在应该有四千万左右的行数。然后其他的上百万的表也有很多。还有包括收藏表也有千万级的行数。
在这种海量数据的情况下,对数据表的就结构变更,一定是一个比较麻烦的问题。常见的情况,比如增加一个新的索引,会导致索引好几个小时。像豆瓣之前会存在这样的问题,是怎么解决的呢?
这个问题曾经让我们吃过苦头,在忽视它的状况下就去改表,然后就锁了很长时间。后来我们意识到这个问题,如果有表的改动的话,我们会先在一个测试的库上试验一下它的时间长短,是不是在可接受的范围,如果是可接受的范围,比如说几分钟,就做一个定时任务,在深夜里面去执行。如果耗时是不可忍受的,就必须通过其他技术手段,我们现在的手段一般是建一个新表,这个新表从旧表同步数据,然后再写数据的时候,也会同步,往两边写,一直到两边完全一样了,再把旧表删掉,大概是这样一个方式。
刚才您好像提过你们设计了自己的DoubanDB,还有一个是DoubanFS,这两者关系是怎么样的?
首先是先出来的DoubanFS,我们刚开始的时候用MogileFS来解决我们可扩展图片存储的问题,由于MogileFS有一个重型数据库,这成为了它的性能瓶颈。我们为了解决这个问题,开发了DoubanFS,基于哈希来寻找节点。之后,我们又发现了新的问题,数据库中的大文本字段也会影响性能。所以,我们在DoubanFS的基础上,换了一个底层,做了一些调整,参照Amazon的dynamo思想,搭建了DoubanDB,把文本字段放在DoubanDB里面。做完之后,又反过来用DoubanDB来实现FS,大致是这么一个过程。
DoubanFS跟DoubanDB的实现,他们在对于内容的安全性,或者内容的冗余性…
都是(备份)三份。这都是可以配置的,现在的配置是3份。
DoubanDB就是用什么机制实现的?
DoubanDB简单来说是这样子:你来一个Key,它是Key-Value数据库,你要写或读的时候,通过这个Key来寻找这个值。拿一个Key对它做哈希,通过Consistent哈希方法去查找它在哪个节点上,然后往这个节点上去写或读。在这个节点上,顺着哈希的wheel顺次的找到第二、三个节点,写的时候会保证这三个节点都写,读的时候是任意一个,如果其中一个读失败了,会自动切换到下一个。
您刚才提DoubanDB的话,是采用的技术是?
DoubanDB的底层存储用的是TokyoCabinet,是一个很轻量级、高效的Key-Value数据库。我们在它的基础之上,做了分布式,用这种方式来实现的。
实际上有一些其他的方案可以解决,比如说像Berkeley DB(简称BDB)、CouchDB等等,你们为什么要选择TokyoCabinet?
最简单的原因是由于它足够快,实际上BDB跟它比较类似,BDB更加强大一些。对我们而言,我们在这边就是需要一个可靠、高效的Key-Value存储,这两个其实是我们都可以替换的,只要统一下接口就可以。CouchDB的话就是另外一个东西了,它是一个文档型数据库,它不仅仅做了一个Key-Value的工作,它还在这上面做了很多其他的事情,比如它有View的概念,可以进行query。这些TokyoCabinet是没有的,而我们暂时也不需要这些功能。CouchDB是一个很有意思的数据库,我们可能会在其他方面(应用),我们也在研究它。
在豆瓣专门有一个算法团队,他们的主要工作就是数据挖掘。这边讲技术实现的话,可能就讲不完了。只能讲一些大概,数据挖掘是怎么和前端结合起来的,让用户看见的。每天用户在豆瓣上的操作都会产生很多数据,在豆瓣上面看到的东西,收藏的东西,都会存在数据库或是访问日志。每天这些信息都会传到算法团队的机器上,然后会从这个数据中建立一个稀疏矩阵,你看过什么,干过什么。他们维护了一个很高效的稀疏矩阵运算库,然后用它来做各种各样的尝试,去看是否能得到好的结果,一旦发现这个结果很好,就会把它写到数据库里面。然后用户在访问的时候,前端从数据库中取出推荐给你的数据,然后把这些数据做一些过滤(比如你读过的东西就不再给你展现了)、调整,最后展现给用户。基本上是这么一个逻辑。
关于Python
Python语言的历史可以参考《Guido Rossum:打造Google第三大开发语言》
关于Subversion
Subversion(简称SVN)是一款开源的版本控制管理系统,被认为是CVS的替代者。Subversion的版本库可以通过网络访问,从而使用户可以在不同的电脑上进行操作。从某种程度上来说,允许用户在各自的空间里修改和管理同一组数据可以促进团队协作。
关于Trac
Trac是一个开源软件平台,集成了Wiki和问题跟踪管理系统。Trac以简单的方式建立了一个软件项目管理的Web应用,以帮助开发人员更好地写出高质量的软件。Trac采用Python语言开发的,因此Trac的在运行的时候,需要有Python环境的支持。
关于Quixote
Quixote是一个Python的Web框架,它基于简单灵活的方案设计,可以进行快速地开发项目,而且使用很多Python第三方模块。通过恰当地配置,可以让Quixote发挥巨大能量,这使得它可以被用于大规模系统当中。