刘晓震:新浪博客应用架构分享
大家下午好,第一次面对这么多人进行这次分享,这么高级的麦克风只在原来KTV时用过。首先做一下自我介绍,我是来自新浪博客刘晓震,平时主要负责应用架构方面工作。我们里面有一个名词叫做LAMP,[url=javascript:;]PHP[/url]都是跟LAMP结合在一块来说的。今天各位嘉宾演讲都是跟PHP相关的东西,我今天主要介绍博客整个系统架构,是如何在LAMP平台怎么进行的,下面我们就进行今天的演讲。
今天我演讲主题是新浪博客架构分享。这个PPT不是最新的,但是不影响今天演讲,到时候会把最新的PPT更新到网站上,欢迎大家去看。这个已经做完自我介绍了,首先我们是新浪博客部门,今天议程主要是以下几个部分。第一整个新浪博客介绍,我们都知道架构是离不开跟应用特点结合的,脱离于应用特点架构一般都是比较虚无的。
第二在整个博客网站运营维护过程中我们遇到一些问题,这些问题我们经过分析去总结了一些经验,形成了现在相对来说比较稳定的架构系统。第三部分详细介绍一下这部分架构我们是如何去做的。最后一部分是做我们近年来一些总结,以及我们即将要做的一些事情。首先是新浪博客的一个介绍,新浪博客无论从用户量,访问量来说都是在国内博客类网站都是首屈一指的,原来是,现在也是。
我们大概博客上线5年半时间,用户量大概在6千万级左右,每天PV都是10亿级别的,动态访问数都在5亿左右。如此大一个访问量,我们博客如何去做这样一个合理架构,去保证我服务长期稳定性,和一个高性能。博客的一个访问特点比较典型的系统。
第一个是访问量很大,我们有很多名人资源,有很多草根名博,这些都会提供到网站上供大家浏览,每天都有超过10亿PV。我们整个网站都是由动态应用程序组成,如何去承载这么高访问量。第二是一个博客比较典型的特点,读多写少。有很多名人博客,像韩寒,徐小明,他们发表一篇博文之后,每一篇最新文章都在访问百万级别左右。第三还是热点用户,和热点事件,当我们出现热点事件的时候博客访问量都会剧增。我们如何在激增过程当中保持一个服务稳定性和系统承载能力,都是我们需要考虑的。第四个是富媒体,博客的页面比较复杂,可以承载很多多媒体元素。利用视频,Flash,图片,音频等,页面结构比较复杂,用户也可以自己去输入CSS代码,就造成页面不可控,整个页面在渲染过程当中会出现问题,导致叶面挂掉。页面元素非常多,大家都知道HTTP下载会有阻塞,如何在这么多并发请求中最快的把页面展示给用户。
这是我们面临三个挑战,第一就是系统支撑能力和稳定性,主要针对一级访问去做的。第二就是用户体验,主要针对富媒体去做的。这个用户体验,可能还会有另外一个部分,就是中国网络一个特点,就是南北差异比较大。北方用户和南方用户访问同一个地理资源,可能网速是完全不一样,我们如何去保证这些用户具有相对来说比较稳定的访问速度,甚至说会加快网络访问速度,这些都是我们需要去做的。
最后就是一个监控和容灾切换。博客是新浪公司一个比较重要的产品,关注度也比较高,我们对于可用性需求,包括一个系统性能都会有比较高的标准。我们就需要保证他的可用性,或者我对一个系统性能进行长期监控,随着数据量增长,处理数据的性能不断下降,我如何优化这部分性能的下降给抹平,或者重新梳理并优化我们的架构。
博客架构并没有什么复杂的,基本上就是LAMP平台,这些软件相信大家都耳熟能详,用的也比较多。可能在座各位有很多高手用的比我们好,但是我们博客就是这样的,用这几个简单软件构成这么大的服务。这个是博客系统一个层次,我今天讲的这个博客系统只是讲的一个纯博客系统,其实博客这个服务包含有很多一些业务,是以一个博客系统为主的,涵盖多个子业务模块,这样一个比较大的交流平台。
今天所讲的博客系统有五部分,第一部分是我的负载均衡,我们知道一个高流量,高负载网站,负载均衡是必不可少。他必须要有一个高并发承载能力,去把所有用户请求过来,在把流量分发到各个处理单位去。我们第二层就是静态缓存,这个主要是针对整个博客产品这样一个访问特点,读多写少去做一个动态内容静态化,Squid规模做的比较多。
第三层是静态应用,主要是[url=javascript:;]Apache[/url]和PHP,依靠PHP这样一个可快速开发,一个性能还比较好的编程语言,Apache是相对来说服务型比较稳定去构成我们动态响应。相信很多人都在用Memcache,主要进行一些查询。最后一层是数据库,我们主要使用的是[url=javascript:;]Mysql[/url],因为比较火确实比较好用,我们一些比较小的,比较碎的数据,为了永久存储都存在MDB里面。
首先讲一下7层代理,我们是采用Nginx作为我们负载均衡设备,承载所有用户的一个请求。Nginx性能非常高,我们还做另外两个功能。第一提供一个基本服务,我们是网站运营,我们编辑运营那部分会把很多静态文件生成文件页,像我们博客大首页静态文件,会进行推送,就会直接提供静态文件服务。第二是一个代理负载均衡,就不多说了。第三是业务逻辑实现,博客有一个相应特点,我相同地址登录不登录访问是同一个网址,我们需要登录博主直接访问,或者访问我们静态单位,这样Nginx就会进行浏览展示是否是动态或者纯动态的,主要解决动态缓存刷新不及时,其实是在规避这个方向。
最后一个是Squid管理,我将在下面这个一起说。首先说Squid与Nginx是对等部署,就是提供一个反向代理。因为我们做了8个IDC部署,Squid+Nginx相对于我们自己搭建一个服务,在全国有8个点部署,这些基本上占博客流量70%,80%以上,让更贴近用户去访问。Squid主要依靠反向代理,如果没有命中会去后端取缓存到本地。我们去做了一个Squid静态缓冲池子,每个IDC会有N台Squid提供服务,当我们在负载均衡的时候,我们的URO过来之后可能会比较随机进行命中,这样会造成我们缓存比较多。
我们在刷新的时候其实是一个负担,我有8组Squid,但是我有100台Squid服务器。我们Nginx就做了这样一个事,把URI进行一次性刷新,会根据后台Squid数量进行一次刷新,当Squid有一台挂掉之后,我们Nginx就会把它从内存里面摘掉,会随机往后面三台推。虽然这样可能会造成有一段时间数据更新程度有问题,但保证了我们整个网站这种可用性。当我任何一台机器挂掉之后,我还是可用的,还要到后端去取,还是有缓存服务。
下面讲一下我们Squid上面一些缓存原则。Squid主要是缓存博客核心页面,博客核心页面主要是我们列表页,博主页正文页。在一开始在做静态化项目的时候,可能会把跟博主所有动态输出页面全部都缓存下来,这样就造成缓存成本非常高,刷新成本也非常高,导致命中率上不去。后端Squid,存储文件也非常多,性能下降很多。我们后来就会进行规避,分析用户这样一个行为,把一些访问量权重比较大的,第一页,比较热的这些页去做一些缓存,对于N页之后全部都没有做,这样做有一个比较可控,我缓存刷新的时候博主发表一个博文你可能都要进行刷新一下,当博主刷新之后我们就可以减少更新的复杂度。
另外Squid会把后端PHP减少压力,我们在做权衡的时候也是这样去做的。一些比较重的页面,像博主页可能会带来十几次数据查询,我们认为非常重,有很多次网络开销,更新频率不会太大。我们综合考虑更新复杂度会越过访问量和命中率,然后决定访问,我们都是通过PHP主动输出。对于我们不输出Squid是不会缓存的。
讲一下缓存命中率的优化,第一个是减少冗余存储。刚才说了主要是通过no-cache,还有良好URL设计。我们忽略到一些对我们来说影响缓存命中的一些东西,例如说会带来no-cache访问,或者带着一个策略去访问,会影响我们缓存时间,或者是缓存策略。第二有很多网站抓取的时候都会带很多随机数进行访问,这样的话,随机数会严重影响我们命中率,不断去后端去取。
第三个我们肯定要对这些不正常访问,或者非法访问做一些规则。既要保证可以访问到正常页面,不影响他的使用,又要保证我们静态缓存,静态化这一层去做到更好服务。主要有两点,第一是在Squid上去做规则,去预估他们的访问会以什么样的方式,哪些是非法的。还有一部分就是说,我们的URL设计一定要规范和合理,我们在做规则的时候,一个良好的规范URL设计是很容易去做的,如果你的URL千奇百怪,有各种各样类型,那你很难去做规则。
大家可以看一下这个图,这个图命中率是我们原来IBS,每天访问量大概在600万左右。我们做完这些规则,命中率已经达到95%。下面一层是动态应用,我们动态应用主要是Apache+PHP,有部分应用是Nginx+Spawn-fcgi+PHP组成的Web服务单位。按业务功能划分服务模块,把这些[url=javascript:;]服务器[/url]分成很多组,代码是统一去上线和部署的,我的环境因为一致性很高,所以我可以快速增加和删除机器,去实现他的一个可扩展性和伸缩性。
第二当我们的网站遇到高流量,可以把不必要的接口给禁掉,直接去提供主页面服务。动态层基本上就没有什么可讲的,主要是跟大家用的基本都一样。用Memcacheq进行版本管理,我们大家都知道,我的一个请求过来之后,他可能会有很多个操作,这些操作其实用户是不需要等待的。我们如何去把这些任务异步处理,我们用的是Memcacheq也是新浪自己开发的。我们在这个系统上做二次规范定义,第一是我自定义消息格式。Memcacheq是完全支持MC协议的,我这个消息从哪来是由谁发起的,他要干什么我们都会知道并且记录下来,对于我们这样异步系统容错会有比较好的帮助。
第二是一个异常处理,主要做一些机制去保证这个Memcacheq,消息不丢,始终有一台Memcacheq可以写进去。第三是伸缩性,当一台Memcacheq不够之后,我们可以很平滑把这个应用分到第二台,而我后台处理不需要知道这台。下面讲一下我们数据缓存,主要是用MC,相信大家都用的比较多,我们都用比较拉的方式,缓存命中的时候就去MC里去取,命不中的时候就去后端。我们一类是共享内存数据,类似与验证码这样东西。还有耗时查询结果,把一些比较耗时查询放在上面。
最后两个可能就是一个热点和索引数据,因为命中率非常高。最后是索引和聚合数据,我后端可能会请求几次API,这些如果更新的话长度不大的话,我们会主动把它放在MC,把多个结合汇集到一块使用。MC主要是缓存一些比较小的数据碎片,Squid因为是硬件存储所以容量上支撑比较好。缓存考核指标,还是一个容量,命中率这些东西。大家可以看这两张图,第一个图是一个MC缓存管理,第二张图是命中率提高,命中率一个表现。我们主要通过这两个去看我们的缓存服务是否该扩容,是否值得他浪费太多成本,或者他的命中率不够我们的要求,我们可能会再次去拆分数据,或者再次去聚合数据,从而提升整体命中率。
数据层,我们主要是两个,一个是Mysql,我们也有自己的团队去进行维护。主要存一些关系型数据,包括所有业务,基本上最后都会落在Mysql里面。然后MemcacheDB作为key-value存储,我们数据结构简单,高并发读写都放在里面,还有可扩展能力强。上面说到我们Mysql做了8层,我后面动态单元和数据存储层目前做了北京和广州两个IDC部署。MemCache同步主要靠一个同步机制,目前广州可以承载大概,博客整个动态服务都可以切进去,这样可以有效提升一个IDC容灾切换的能力。
首先说一下数据层,这个PPT跟我后来准备的不一样,有一些点没有准备。我重点去讲三部分,第一是冷热分离,我们做过统计包括我们现在也这样做了,博客的冷热用户还是区别比较明显,因为博客有很多名人用户。我们统计大概有5千个用户,博客用户量在6千万以上,大概我们有5千个用户,他们总的PV占到博客整个访问量10%,也就是说万分之一用户占到10%访问,我们就把这万分之十这些用户拆分出来,形成单独一个库,这些用户库他带来一个好处是什么?首先是说他一个数据容量减少了,因为我们博客以博文举例,数据库容量总的加起来已经超过4个T,我们把5千用户拆出来之后,我们在查询,索引效果之上优点会非常高。
第二如此小的一个数据量,我们可以每天都去更新和备份,不会浪费我们太多成本。如果我们不拆分这部分用户,我们想让博客提供稳定服务,这部分用户关注度这么高,我们可能会每一天,每两天基于4个T数据备份,对于成本浪费非常重,而且也起到一个非常好效果。从后端数据访问来说,原来我们文章平均查询时间大概在0.03左右,对一些热点库访问就在0.05秒之内,甚至更低,还有数据安全,我们可以以更高,更好的备份策略去保证他的安全。
第二部分是提高单表能力,这可能还是一个拆分功能。我们做了一个比较有意义的尝试,原来我们文章库既可以按照列表查询,又可以按照正文查询。当你容量很大的话,你单表超过几百万,甚至上千万,你列表查询比较慢,虽然有索引,但也需要把他所有索引取出来进行排序,你在从磁盘里面把数据取出来进行过滤,过滤完之后把你想要的数据给筛出来。这样的话给后端数据增长之后,后端压力不断变大,列表查询会变成非常大压力,影响我们数据库性能。我们采取不动数据库情况下,把列表和文章进行拆分,把列表库大字段给剥离出去了。
但是这样的话,也许有人会问你拆分完之后,你写一篇文章之后,会不会更新两个库。我们目前采取的技术,就是把那张表,字段强行转换为一个类型,在同步的时候是不受影响。因为你的列表库做了优化,正文库只取一条数据,这是直接命中的。最后一点对于Mysql使用,越简单越好,我们在设计的时候也不会设计多么复杂,倾向于简单使用,使用索引去查询,直接返回,没有特别复杂的应用,包括也不支持事物,因为本身我们网站特点事物性要求也不会太高。
另外,我们采取覆盖索引快速索引的一个机制去做。提到可用性和监控,我们主要从三个方面,第一个是我的前端性能监控,第二是前端性能监控,第三方面就是应用级别监控,还有后端监控。第一监控是这样,因为博客页面比较复杂,是富媒体,用户输入什么,输出什么,可能我们不会太清楚。这样的话用户虽然在输出,但是很多人会读他的。至于用户的前端发生过什么,如果没有一套非常好的监控机制,我们很难保证,我虽然服务器上返回是200,但是页面在执行一段之后,GS挂了,页面直接出白页再也不往下执行了,这些都会影响我们服务可行性,和服务质量。
我们主要通过两部分,第一是基调系统。全国各个地方布很多点,去监控一些特定地址,这些地址整个下载时间,这个地址可能会有不同元素,像图片,视频这样一个多媒体元素来去看他两个指标,一个是他首屏的时间。一个页面快慢直接是首屏时间,还有整个页面系统,包括资源下载完毕时间,让我们去做衡量。
第二点我们去做一些异常报错,当GS执行异常退出之后,我们会把这些事件,把一些信息传回服务器,我们就可以做定期收集,分析出来之后去做一定汇总,去让前端人员帮助我们不断地去进行优化,去看究竟是哪部分出的问题。应用级别优化,两部分,第一部分可能是在上线前我们会去做的,就是用Xdebug看他写的代码有没有很慢,或者重复连接的次数,去做这些工作。第二我们会监控,因为刚才各位嘉宾也都说了,PHP性能本身是没有什么性能上大的问题,主要还是取决于你对于资源的访问,数据库的查询,因为他是一个创业型,有一个槛就会影响整个性能。我们重点会监控数据库查询时间,和API查询时间。对于一些很严重的错误我们会通过Syslog定期收集数据库,定期出一个报表。Sql和Api会进行一个分析,我们去谁快谁慢找对应策略。服务器端靠它去监控服务器,这部分就不多讲了。
我们遇到很多问题,像现在来说博客系统相对来说是一个相对比较稳定的版本了,但是我们还是要前进和进步。首先是一个平台化的概念,我们服务单位比较多,其实扩展能力还可以,但是它的运维成本还比较复杂。我们就需要有一套比较快速的,可以高速扩展这样一个服务器管理平台能快速去进行服务器复制,去进行一个容量快速扩容的能力。
第二是我整个系统环境希望能够统一进行升级,包括代码也能统一去进行升级。第二是DAL访问层,有两部分,第一部分是解决我们连接上问题,可能会有连接阻塞,或者连接慢,给后端服务器造成压力,我们就需要有这样一个层去解决一个问题。第二我们希望把我们数据做的更底层一点,让开发人员可以忽略这层直接使用,在内部直接实现缓存刷新,可以达到比较高的水平。
第三个是Nginx SSI。博客有单独这样一个静态提供图片,包括小图片处理。大家都知道一个页面下载,我们会把用户自定义GS,CSS通过前端进行下载,这样势必会造成一个当他下载不下来,势必会造成一个裸奔,我页面整个模板出不来。第二他的下载会阻塞后面的执行,会造成页面阻塞,影响首屏。我们可能会用Nginx,我们已经做8个点部署,我们把一些比较小的合并数据,安全SSI标准,不一定是后端文件,也可能是后端API,让它直接在后端拉进来,直接在输出,这样用户会减少很多次请求。
最后一个是Redis,可能掀起最近一个缓存风暴,支持多种类型缓存数据,可以支持列表,集合,包括排序。我们希望能借助它去简化我们缓存整个系统复杂度,提升开发效率,包括后端部署效率。最后一点是Open,我们博客已经有5年历史,数据量也非常大,我们希望能够把我们数据开放出去。现在我们也在做一个Open项目,在这里有两个项目,第一把自己东西拿出去给别人用,第二我们需要有一个统一框架,统一平台去做类似于新浪微博这样产品东西,能够快速地融入进来。因为现在主流是微博,我们希望能够通过我们这样一个系统,能把跟微博做更好结合,从而扩充我们整个业务产品线。
我的演讲就到这里,谢谢大家。