May1

[转]sohu的核心架构,年代虽远,但依然受用

Author: 邹丹  Click: 6727   Comments: 0 Category: 架构  Tag: sohu,架构
bserv:
用于高负载,高读写速度的单点和集合数据。内核为BerkeleyDB,外壳为UDP线程池。接口为读写单点数据或者集合数据。单点数据就是Key->Value数据。集合数据就是有索引的数据,List->Keys->Values比如一个班级所有成员,一个主贴所有回帖等等。 DBDS性能很高,每秒读取>800个每秒,写>300个每秒(志强xeon:2G*272GscsiRam:2G配合java接口,目前应用在ChinaRen所有项目中(ChinaRen校内,校友录,社区等等)。是整个ChinaRen的核心数据服务,大概配备了50台服务器。特点:高速,高请求量。用于各种数据的低成本存储,解决数据库无法实现超高速读写的问题。门户级别的高速数据服务。

OnlineServer:
ChinaRen/SOHU
小纸条系统核心核心为3个小server系统:online2(在线系统业务逻辑)userv(用户资料系统)cserv(LRU缓存) 这三个子系统都是UDP+线程池结构,单进程+多线程。配备java接口,apache_modjsonxml接口。 online2包括了大部分业务逻辑,包括,上线,好友系统,纸条系统。 userv包括设置用户各种属性,信息。 cserv是个大的lru缓存,用于减小磁盘IO。可以放各种信息块,包括用户信息,好友,留言等。目前配备4台服务器(DL380xeon:3G*2SCSI:146G raidRam:2G),用户分布到4台服务器上,相互交互。服务器可以由1台到2台,到4台,到8台。底层存储为文件存储(无数据库),用reiserfs配套系统: mod_online,两个版本,apachelighttpd版本,用于页面上显示蜡烛人。请求量巨大,目前用lighttpd版本的mod_online放在sohusquid前端机器上,运行在8080,大概8台,每台请求量大概500-800个每秒。蜡烛人在所有ChinaRen页面有ID的地方显示用户是否在线。目前这套在线系统,作为SOHUIM的内核原型。准备开发WEBIM系统,用户所有SOHU矩阵用户的联络。


apache_mod

cserv:
高速LRU缓存系统。内核是UDP+线程池+LRU结构(hash+PQueue)。用于存放各种数据块,Key->Value结构。通过LRU方式提供给应用,可减小文件IO,磁盘IO等慢速操作。目前用于ChinaRen在线系统的用户资料缓存。特点:高速读写,低成本。

ddap:
UDP+
线程池,单进程,多线程的服务端程序原型,大部分程序由这个结构开始。性能为8000-10000个请求每秒。

eserv:
访问统计系统用于用户访问的次数和最后上线时间的存储和读写。用于ChinaRen校友录每个班级的访问记录。存储为文件存储,并有同时写入后备的bserv,用于备份和检索。目前性能,每台机器每秒50个记录,100个读每秒。能满足校友录巨大的用户登录记录的需要。特点:无数据库,纯文件存储,高速读写。低成本

logserver:
用于各种事件的日志记录核心为ddapUDP+线程池功能是分模块记录各种日志。ChinaRen所有用户服务,系统日志,都记录在logserver中。用于统计,查询。写入性能很好,每秒100个单台机器。特点:高速高效,低成本,海量。

SessionServ:
session
系统核心为ddapUDP+线程池用于在内存中存储临时数据。有get/put/del/inc等操作。广泛的用于固定时间窗口的小数据存储。比如过期,数据有效性检测,应用同步等等。由于是全内存操作,所以速度很快,存取速度应该>1000个每秒。目前广泛用与ChinaRen社区,校内,校友录等业务当中。特点:高速高效,低成本,应用广泛。

其他server:
MO_dispatcher
用于短信上行接口的的数据转发,使用TCP。能高速大流量根据业务号码分发到各个应用服务中。目前用于SOHU短信到ChinaRen各短信服务的转发。 sync用于静态前端同步,分客户端和服务端程序。客户端通过TCP链接和服务端获取需要同步的文件列表,并且通过TCP高速更新本地文件。此同步程序用于多客户端,单服务端。比如一台服务器生成静态文件,同步这些文件到若干客户前端去。特点:门户级静态内容服务器间同步,高效,高速,大流量。目前用于ChinaRen社区的静态帖子。

总结一下:
门户的核心服务,要求是高效率,高密度存取,海量数据,最好还是低成本。不要用数据库,不要用java,不要用mswin。用C,用内存,用文件,用linux就对了。

系列:
基于apache2的服务有很多,用于高请求量,快速显示的地方。 1.mod_gen_verifyimg2
用于显示验证码,使用GD2freetype。直接在apache端返回gif流,显示随机的字体,角度,颜色等等。用于ChinaRen各个需要验证码的页面,请求量很大。 2.mod_ip2loc
用于apache端的IP->物理地址转换,高速,高效。读取数据文件到内部数据树,高速检索,获得客户端ip的物理地址。用于需要IP自动定位的产品,还有就是数据统计等。比如ChinaRen校内,每个客户端请求都能获得物理地址,用于应用的逻辑处理。 3.mod_pvserver2
ChinaRen
社区帖子点击的记录和显示。根据URL,得到帖子ID,通过UDP数据包,统计到bserv系统。并且把结果通过Cookie返回到客户端。html直接用javascript显示点击数在帖子上。解决了点击数量高效记录,高效读取和非动态页面程序显示的问题。 4.mod_online
用于ChinaRen页面上的蜡烛人显示。和onlineserver通讯,得到用户在线状态和其他状态信息。请求量很大,每台前端大概500-800个请求。 5.其他mod 还有一些认证的,访问统计的,特种url过虑跳转的,页面key生成的,还有若干。特点:高速,密集超高请求量。前端分担应用服务器压力,高效。
Apr3

网站加速--Cache篇2【转自架构师杨建】

Author: 杨建  Click: 6815   Comments: 0 Category: 架构  Tag: cache,网站加速,系统架构
--提升性能的同时为你节约10倍以上成本
From: http://blog.sina.com.cn/iyangjian

一,Cache, 王道也
二,Cache 基本原理介绍
三,我划分的3个刷新级别
四,我对HTTP协议做的一点创新(?maxage=6000000)
五,Yslow优化网站性能的14条军规点评
六,上线了 !=  Finished
七,提速度同时节约成本方法汇总
-----------------------------------------------------------------------------------------

一,Cache,王道也

我觉得系统架构不应该仅仅是搭建一个强硬的能承受巨大并发压力的后台,前端页面也是需要架构的而且同等重要,不理解前台的的后台工程师是不合格的。中国人讲究钢柔相济,后台强硬只能说你内功深厚,前端用的巧,那叫四两拨千斤。

一般后台工程师很少关心前端如何使用自己的资源,而前端工程师,不知道自己的一个简单的用法会对后端造成多大影响。我会给出一些数据,来震撼下你的眼球。

二,Cache 基本原理介绍 (参考Caching Tutorial)

为什么使用Cache?
1,减少延迟,让你的网站更快,提高用户体验。
2,避免网络拥塞,减少请求量,减少输出带宽。
补充一个cache的原则:不更新的资源就不应该让它再次产生HTTP请求,如果强制产生了请求,那么就看看能否返回304。

Cache的种类?
浏览器Cache,代理Cache,网关Cache。
后端还有 disk cache ,server cache,php cache,不过不属于我们今天讨论范围。

Cache如何工作的?
1,如果响应头告诉cache别缓存它,cache不对它做缓存;
2,如果请求需要验证的或者是需要安全性的,它将不被缓存;
3,如果响应头里没有ETag或Last-Modifed header这类元素,而且也没有任何显式的信息告诉如何对数据保鲜,则它被认为不可缓存。
4,在下面情况下,一个缓存项被认为是新鲜的(即,不需到原server上检查就可直接发送给client):
    它设置了一个过期时间或age-controlling响应头,而且现在仍未过期。
    如果浏览器cache里有某个数据项,并且被被设置为每个会话(session)过程中只检查一次;
    如果一个代理cache里能找个某个数据项,并且它是在相对较长时间之前更新过的。
    以上情况会认为数据是新鲜的,就直接走cache,不再查询源server。
5,如果有一项过期了,它将会让原server去更新它,或者告诉cache这个拷贝是否还是可用的。

怎么控制你的Cache?
Meta tags :在html页面中指定,这个方法只被少数浏览器支持,Proxy一般不会读你html的具体内容然后再做cache决策的。

Pragma: no-cache : 一般被大家误用在http响应头中,这不会产生任何效果。而实际它仅仅应该用在请求头中。不过google的Server: GFE/1.3 响应中却这样用,难道人家也误用了呢。

Date: 当前主机GMT时间。

Last-Modified : 文件更新GMT时间,我在响应头中带上这个元素的时候,通常浏览器在cache时间内再发请求都会稍带上If-Modified-Since,让我们判断需要重新传输文件内容,还是仅仅返回个304告诉浏览器资源还没更新,需要缓存策略的服务器肯定都得支持的。有了这个请求,head请求在基本没太多用处了,除非在telnet上调试还能用上。

If-Modified-Since :  用在请求头里,见Last-Modified 。

Etag: 标识资源是否发生变化,etag的生成算法各是各样,通常是用文件的inode+size+LastModified进行Hash后得到的,可以根据应用选择适合自己的。Last-Modified 只能精确到秒的更新,如果一秒内做了多次更新,etag就能派上用场。貌似大家很少有这样精确的需求,浪费了http header的字节数,建议不要使用。
更正:Etag 其实在某种情况下可以很好的减少数据传输。在stonehuang的提醒下我才恍然大悟,转眼好几个月了也一直忘记更新。Etag应用场景。比如,数据为php的动态输出。每次请求把上一次Etag带来,跟本次计算的Etag进行比较,相等就可以避免一次数据传输。(最后修改时间 2009.12.07)

Expires :  指定缓存到期GMT的绝对时间,这个是http 1.0里就有的。这个元素有些缺点,一,服务器和浏览器端时间不一致时会有问题。二,一旦失效后如果忘记重新设置新的过期时间会导致cache失效。三,服务器端需要根据当前Date时间 + 应该cache的相对时间去计算这个值,需要cpu开销。 我不推荐使用。

Cache-Control:
这个是http 1.1中为了弥补 Expires 缺陷新加入的,现在不支持http 1.1的浏览器已经很少了。
max-age: 指定缓存过期的相对时间秒数,max-ag=0或者是负值,浏览器会在对应的缓存中把Expires设置为1970-01-01 08:00:00 ,虽然语义不够透明,但却是我最推荐使用的。
s-maxage: 类似于max-age,只用在共享缓存上,比如proxy.
public: 通常情况下需要http身份验证的情况,响应是不可cahce的,加上public可以使它被cache。
no-cache: 强制浏览器在使用cache拷贝之前先提交一个http请求到源服务器进行确认。这对身份验证来说是非常有用的,能比较好的遵守 (可以结合public进行考虑)。它对维持一个资源总是最新的也很有用,与此同时还不完全丧失cache带来的好处,因为它在本地是有拷贝的,但是在用之前都进行了确认,这样http请求并未减少,但可能会减少一个响应体。
no-store:  告诉浏览器在任何情况下都不要进行cache,不在本地保留拷贝。
must-revalidate: 强制浏览器严格遵守你设置的cache规则。
proxy-revalidate: 强制proxy严格遵守你设置的cache规则。
用法举例:  Cache-Control: max-age=3600, must-revalidate

其他一些使用cache需要注意的东西,不要使用post,不要使用ssl,因为他们不可被cache,另外保持url一致。只在必要的地方,通常是动态页面使用cookie,因为coolie很难cache。至于apache如何支持cache和php怎么用header函数设置cache,暂不做介绍,网上资料比较多。

如何设置合理的cache时间 ?
http://image2.sinajs.cn/newchart/min/n/sz000609.gif?1230015976759
拿我分时图举例,我们需要的更新频率是1分钟。但为了每次都拿到最新的资源,我们在后面加了个随机数,这个数在同一秒内的多次刷新都会变化。我们的js虽然能够很好的控制,一分钟只请求一次,但是如果用户点了刷新按纽呢?这样的调用是完全cache无关的,连返回304的机会都没有。

试想,如果很多人通过同一个代理出去的,那么所有的请求都会穿透代理,弄不好被网管封掉了。如果我们做只做一秒的cache,对直接访问源服务器的用户没太多影响,但对于代理服务器来说,他的请求可能会从10000 req/min 减少为 60 req/min ,这是160倍。

对于我们行情图片这样的情况,刷新频率为1分钟,比较好的做法是把后面的随机数(num)修改为 num=t-t%60 其中t是当前时间戳,这样你一分钟内刷这个url是不变的,下一分钟会增加1,会再次产生一个新请求。而我的max-age设置为默认59秒,即使设置120秒其实也没什么影响。可能你会说万一赶上临界点可能拿不到最新的数据,其实对用户来说,用那个多变的随即数和我这个分钟级的随即数,看到的效果是相同的下面我给你分析一下:如果用户打开了我们的分时间页面,当前随即数对他来说是新的,所以他会拿到一个当前最新的图片,然后他点了刷新按纽,用户会产生http请求,即使url没变,服务器有最新图片也一定会返回,否则返回304,一分钟后js刷新图片,分钟数加了1,会得到全新资源。这和那个随时变化的随即数效果有区别吗?都拿到了最新的数据,但是却另外收益了cache带来的好处,对后端减少很多压力。

三,我划分的3个刷新级别

名词解释 全新请求: url产生了变化,浏览器会把他当一个新的资源(发起新的请求中不带If-Modified-Since)。

更正:在firefox后来的版本中对此做了改进,倾向于更多的使用cache,曾经访问过的都会尽量捎带If-Modified-Since头。这些表现和IE一致。修改部分用红色标出。(最后修改时间 2009.12.07)

注: sports.sinajs.cn 在IE下的表现存在一个小bug,由于不是使用的strncpy,导致IE下难以返回304,
需要修改一行代码,把比较字符串长度设置为29即可解决。不过目前本人已不在职,难以修改。
情况一 FF 捎带的头: If-Modified-Since    Mon, 07 Dec 2009 10:54:43 GMT
情况二 IE 捎带的头: If-Modified-Since    Mon, 07 Dec 2009 10:54:43 GMT; length=6

1,在地址栏中输入http://sports.sinajs.cn/today.js?maxage=11地址按回车。重复n次,直到cache时间11秒过去后,才发起请求,这个请求会带If-Modified-Since

2,按F5刷新.  在你发起一个全新的请求以后,然后多次按F5都会产生一个带If-Modified-Since的请求。

3, ctrl+F5 ,总会发起一个全新的请求。

下面是按F5刷新的例子演示: http://sports.sinajs.cn/today.js?maxage=11
( 如果这个值大于浏览器最大cache时间maxage,将以浏览器最大cache为准)

----------------------------------------------------------发起一个全新请求
GET /today.js?maxage=11 HTTP/1.1
Host: sports.sinajs.cn
Connection: keep-alive

HTTP/1.x 200 OK
Server: Cloudia
Last-Modified: Mon, 24 Nov 2008 11:03:02 GMT
Cache-Control: max-age=11    (浏览器会cache这个页面内容,然后将cache过期时间设置为当前时间+11秒)
Content-Length: 312
Connection: Keep-Alive
---------------------------------------------------------- 按F5刷新
GET /today.js?maxage=11 HTTP/1.1
Host: sports.sinajs.cn
Connection: keep-alive
If-Modified-Since: Mon, 24 Nov 2008 11:03:02 GMT   (按F5刷新,If-Modified-Since将上次服务器传过来的Last-Modified时间带过来)
Cache-Control: max-age=0

HTTP/1.x 304 Not Modified   
Server: Cloudia
Connection: Keep-Alive
Cache-Control: max-age=11   (这个max-age有些多余,浏览器发现Not Modified,将使用本地cache数据,但不会重新设置本地过期时间)
----------------------------------------------------------
继续按F5刷新n次.......

这11秒内未产生http请求.直到11秒过去了...............
----------------------------------------------------------按F5刷新
GET /today.js?maxage=11 HTTP/1.1
Host: sports.sinajs.cn
Connection: keep-alive
If-Modified-Since: Mon, 24 Nov 2008 11:03:02 GMT (多次按F5都会产生一个带If-Modified-Since的请求)
Cache-Control: max-age=0

HTTP/1.x 304 Not Modified
Server: Cloudia
Connection: Keep-Alive
Cache-Control: max-age=11
----------------------------------------------------------按F5刷新
GET /today.js?maxage=11 HTTP/1.1
Host: sports.sinajs.cn
Connection: keep-alive
If-Modified-Since: Mon, 24 Nov 2008 11:03:02 GMT  (同上 ...)
Cache-Control: max-age=0

HTTP/1.x 304 Not Modified
Server: Cloudia
Connection: Keep-Alive
Cache-Control: max-age=11
----------------------------------------------------------


四,我对HTTP协议做的一点创新(?maxage=6000000)

上面看到了url后面有  ?maxage=xx  这样的用法,这不是一个普通的参数,作用也不仅仅是看起来那么简单。他至少有以下几个好处:

1,可以控制HTTP header的的 max-age 值。
2, 让用户为每个资源灵活定制精确的cache时间长度。
3, 可以代表资源版本号。

首先谈论对后端的影响:
服务器实现那块,不用再load类似mod_expires,mod_headers 这样额外的module,也不用去加载那些规则去比较,它属于什么目录,或者什么文件类型,应该cache多少时间,这样的操作是需要开销的。

再说说对前端的影响:
比如同一个分时行情图片,我们的分时页中需要1分钟更新,而某些首页中3分钟更新好。不用js控制的话,那我cache应该设置多少呢?   有了maxage就能满足这种个性化定制需求。

另一种情况是,我们为了cache,把某个图片设置了一个永久cache,但是由于需求,我必须更新这个图片,那怎么让用户访问到这个更新了的图片呢?从yahoo的资料和目前所有能找到的资料中都描述了同一种方法,更改文件名字,然后引用新的资源。 我觉得这方法太土, 改名后,老的还不能删除,可能还有地方在用,同一资源可能要存两份,再修改,又得改个名,存3份,不要不把inode当资源。我就不那样做,只需要把maxage=6000000 修改成 maxage=6000001 ,问题就解决了。

maxage=6000000 所产生的威力 (内存块消耗减少了250倍  ,请求数减少了37倍) :
体育那边要上一个新功能,一开始动态获取那些数据,我觉得那样太浪费动态池资源,就让他们把xml文件到转移到我的js池上来,为了方便,他们把那个84k的flash文件也放在了一起,而且是每个用户必须访问的。说实在的,我不欢迎这种大块头,因为它不可压缩,按正常来说,它应该代表一个3M的文件。我的服务器只这样设计的,如果一次发送不完的就暂存在内存里,每个内存块10k,如果不带参数默认maxage=120 。 我发现,由于这个文件,10w connections的时候,我消耗了10000个内存块。我自己写的申请连续内存的算法也是消耗cpu地,一个84k的文件,发送一次后,剩余的64k就应该能装的下,于是我把最小内存块大小改为64K。 这样消耗10w conn的时候消耗1500个左右内存快,虽然内存消耗总量没怎么变小,但是它能更快的拿到64K的连续内存资源,cpu也节约下来了。接下来我让meijun把所应用的flash资源后面加上maxage=6000000 (大概=79天,浏览器端最长cache能达到着个就不错了), 10w connections的时候,只消耗了不到40个内存块,也就是说内存块消耗减少了250倍  ,请求数减少了37倍。  35w+ connections, 5.67w req/s的时候也就消耗100块左右,比线性增加要少很多。也就是这点发现让我有了做这个技术分享的冲动,其他都是顺便讲讲。


五,Yslow优化网站性能的14条军规点评

其中黑色部分,跟后端是紧密相连的,在我们的内容中都已经涉及到了,而且做了更深入的讨论。兰色部分,5,6,7是相关页面执行速度的,构建前端页面的人应该注意的。 11属于避免使用的方法。 红色部分我着重说一下:

gzip 我不推荐使用,因为有些早期IE支持的不好,它的表现为直接用IE访问没问题,用js嵌进去,就不能正常解压。这样的用户占比应该在2%左右。这个问题我跟踪了近一个月,差点放弃使用压缩。后来发现我以前用deflate压缩的文件却能正常访问。 改用deflate问题解决。apache 1.x使用mod_gzip ,到了 2.x 改用cmod_deflate,不知道是否跟这个原因有关。 另外对于小文件压缩来说,deflate 可比 gzip 省不少字节。

减少 DNS 查询: 这里也是有个取舍的,一般浏览器最多只为一个域名创建两个连接通道。 如果我一个页面嵌了 image.xx.com 的很多图片,你就会发现,图片从上往下一张张显示出来这个过程。这造成了浏览器端的排队。 我们可以通过增加域名提高并发度,例如 image0.xx.com ,image1.xx.com ,image2.xx.com,image3.xx.com 这样并发度就提上去了,但是会造成很多cache失效,那很简单,假如我们对文件名相加,对4取mod,就能保证,某个图片只能通过某个域名进行访问。不过,我也很反对一页面请求了数十个域名,很多域名下只有一到两个资源的做法,这样的时间开销是不划算的。

另外,我在这里再添一个第15条:错开资源请求时间,避免浏览器端排队。
随着ajax的广泛使用,动态刷新无处不在,体育直播里有个页面调用了我一个域名下的6个文件,3个js,3个xml。刷新频率大致是两个10秒的,两个30秒的,两个一次性载入的。观察发现正常响应时间都在7ms,但是每过一会就会出现一次在100ms以上的,我就很奇怪,服务器负载很轻呢。meijun帮我把刷新时间错开,11秒的,9秒的,31秒的,这样响应在100ms以上的概率减少了好几倍,这就是所谓的细节决定成败吧。

1. 尽可能的减少 HTTP 的请求数     [content]
2. 使用 CDN(Content Delivery Network)     [server]
3. 添加 Expires 头(或者 Cache-control )     [server]
4. Gzip 组件     [server]
5. 将 CSS 样式放在页面的上方     [css]
6. 将脚本移动到底部(包括内联的)     [javascript]
7. 避免使用 CSS 中的 expression_r_r_r_rs     [css]
8. 将 JavaScript 和 CSS 独立成外部文件     [javascript] [css]
9. 减少 DNS 查询     [content]
10. 压缩 JavaScript 和 CSS (包括内联的)     [javascript] [css]
11. 避免重定向     [server]
12. 移除重复的脚本     [javascript]
13. 配置实体标签(ETags)     [css]
14. 使 AJAX 缓存    

六,上线了 !=  Finished

奥运期间我按1500w~2000w connections在线,设计了一套备用系统,现在看来,如果用户真达到了这个数目我会很危险,因为有部分服务器引入了32bit的centos 5未经实际线上检验,而我当时简单的认为它应该和centos 4表现出一样的特性。所以现在未经过完全测试的lib库和新版本,我都很谨慎的使用。没在真实环境中检验过,不能轻易下结论。

很多项目组好象不停的忙,做新项目,上线后又继续下个新项目,然后时不时的转过头去修理以前的bug。如果一个项目上线后,用户量持续上升,就应该考虑优化了,一个人访问,和100w人访问,微小的修改对后端影响是不能比较的,不该请求的资源就让它cache在用户的硬盘上,用户访问块了,你也省资源。上线仅仅代表可以交差了而已,对于技术人员来说持续的对一个重要项目进行跟踪和优化是必要的。


七,提速度同时节约成本方法汇总

1,编写节约的HTTP服务器 (高负载下速度明显提升,节约5~10倍服务器)
对一些重要的服务器量身定做。或者选用比较高效的开源软件进行优化。

2,不同服务混合使用  (节约1~2倍服务器)
如果我们一台服务器只支持30w conn的话,那么剩余的75% cpu资源,95%的内存资源,和几乎所有的磁盘资源都可以部署动态池系统,我觉得DB对网卡中断的消耗还是有限的,我也不用新买网卡了。

3,对于纯数据部分启用新的域名(速度有提升,上行带宽节约1倍以上)
比如我们另外购买了sinajs.cn 来做数据服务,以避免cookie,节约带宽. Cookie不但会浪费服务器端处理能力,而且它要上行数据,而通常情况上行比下行慢。

4, 使用长连接 (速度明显提升,节约带宽2倍以上,减少网络拥塞3~无数倍)
对于一次性请求多个资源,或在比较短的间隔内会有后续请求的应用,使用长连接能明显提升用户体验,减少网络拥塞,减少后端服务器建立新连接的开销。

5,数据和呈现分离,静态数据和动态数据分离 (速度明显提升,同时节约3倍带宽)
div+css 数据和呈现分离以后,据说文件大小能降到以前的1/3。
把页面中引用的js文件分离出来,把动态部分和静态部分也分离开来。

6,使用deflate压缩算法 (速度明显提升,节约3.33倍带宽)
一般来说压缩过的文件大小不到以前的30% 。
将上面分离出来的数据进行压缩(累计节约带宽10倍)。

7, 让用户尽可能多的Cache你的资源 (速度明显提升,节约3~50倍服务器资源和带宽资源)
将上面分离出来的css和不经常变动的js数据部分cache住合适的时间。(理想情况,累计节约带宽30~500倍) 。

以上改进可以让速度大幅度提升的同时,服务器资源节约 5~20 倍 ,减少网络拥塞3~无数倍, 上行带宽节约1倍以上,下行带宽节约30~500倍,甚至更多。
Apr3

网站加速--系统架构篇【转自架构师杨建】

Author: 杨建  Click: 7708   Comments: 2 Category: 架构  Tag: 系统架构,网站加速
--提升性能的同时为你节约10倍以上成本
From: http://blog.sina.com.cn/iyangjian

一,系统部署(高并发,可扩展)
二,负载均衡LVS(高可用,低成本)
三,IDC分布,DNS解析(快速)
-----------------------------------------------------------------------------------------

一,系统部署(高并发,可扩展)

本来想画在手稿上然后扫描上去的,貌似方法太土,在朋友的帮助下费了n个小时用Visio画了个,感觉很好看 ^-^ 。这一篇将主要围绕这个图来讲述。


首先从数据源说起,所谓狡兔三窟,我们数据源也是按三路设计,以保证IDC内部和不同IDC之间实现灾备。源头转发机A,B,C拥有往集群中任何一台服务器同步数据的权限,所以他们三个有一个活着,数据就可以同步更新,而且可以自动切换。从源头转发机到其他各IDC的数据都是双路的,然后每个IDC的前两台服务器具备转发功能,往IDC内部其他服务器分发数据,同一IDC内部的主备转发机可以自动切换。这样就实现了数据同步更新的高可用性。

介绍下这个集群里的角色,备机A来自行情系统,兼任源头转发的异地备份。系统内的另外两个备机属于轻负荷服务器,80端口空出来,必要时候只要一启动,就会立即自动加入到LVS后面服役。除了A以外所有具备转发功能的机器同时也是集群内的普通成员,需要提访问供服务的。各IDC的LVS本身也是有主备的,可以实现自动切换。

整个系统增减服务器非常方便,用户根本感觉不到,备机的启用更快,也就3~5秒,具备很好的扩展性。

我们的数据从源头上就是使用我编写的myzip压缩好了的,后缀名用"*.mz" ,比如 a.js.mz ,一直到用户的浏览器端才解压。数据传输量小,速度快。源头转发机上同时运行一个checkchange的程序,确保内容实际更新过的文件才往其他IDC转发,这样能有效的减少传输文件数量,以达到更快的更新速度。

另外,跨IDC系统部署,很重要的一点是,内网连通,路由选择,这影响数据传输速度的关键。北京的各机房间一般都有比较好的专线连通,只需要把路由打通就ok了。跨IDC的,一般都使用vpn来做内网传输,有条件的使用专线,这个比较昂贵,省着点用。另外跨网通,电信,和移动机房的一般都从双线机房路由,或者说,从到不同信息服务商连通性都比较好的机房路由。总之跨IDC数据传输,要做到各IDC之间的传输速度心中有数。

最后,请稍微注意下系统的安全性,包括数据传输的安全性,和网络安全性,避免遭受攻击。


二,负载均衡LVS(高可用,低成本)

LVS 有三种模式,NAT,TUN,DR,其中DR是最高效的,下面我将主要介绍DR的应用。更多LVS资料参见 LVS项目中文文档 目前我们公司的LVS应用规模在国内应该至少可以排前三,更多技术细节请咨询我们的LVS大牛xiaodong2.

下面是DR单臂模式的系统结构图:


下面引用一下官网的介绍: 在VS/DR 中,调度器根据各个服务器的负载情况,动态地选择一台服务器,不修改也不封装IP报文,而是将数据帧的MAC地址改为选出服务器的MAC地址,再将修改后的数据帧在与服务器组的局域网上发送。因为数据帧的MAC地址是选出的服务器,所以服务器肯定可以收到这个数据帧,从中可以获得该IP报文。当服务器发现报文的目标地址VIP是在本地的网络设备上,服务器处理这个报文,然后根据路由表将响应报文直接返回给客户。

我在财经时要使用LVS的初衷只是为了解决负载的均衡性,因为DNA轮询各前端服务器上连接数有不小的差距,那时候我们老大阿图对于这个项目给予了很大支持,还亲自组织过几次会议。话说恰巧yingyuan做了个新技术讲座,我从中发现LVS/DR后想让它帮忙修改负载均衡算法,后来部署上以后发现,不用修改,均衡的很,再后来xiaodong2接手后对性能和稳定性做了很大的提升,我们使用两年来没出过问题。另外lvs还额外带来了两个好处,高可用性,和可伸缩性。是可以随时把lvs后面的一台服务器下掉,扛走,用户是不知道的。服务器坏了也不用着急修,也不用修改DNS(另外DNS的层层cache影响不是一时半会就能消除的)。新增加一台服务器也是同理,最绝的就是备机的启用可以用秒来衡量(这些F5都能实现,代价不菲)。财经应用对公司内lvs的项目推动有不可磨灭的贡献 ,xiaodong2也这么说地 :) 。


三,IDC分布,DNS解析(快速)


这里思路跟CDN是一致的,尽量减少主干线路上的拥塞,让用户就近访问,以达到最快的数据传输速率。
我们要做的就是了解自己应用的用户分布情况,然后再结合现有资源以及各地网络出口特征,信息服务商的特征来部署我们的服务。

1,各省市网络用户分布依次排名(数据来自cnnic2007年的统计):
广东 13.4%
山东 8.2%
江苏 7.5
浙江
四川
河北
河南
福建
上海
辽宁
北京
湖南
山西
黑龙江

2,运营商的网络分布特点:
网通:以北京为超核心的放射性结构。山东应该是网通最大的用户,但它的网络存在瓶颈,会有丢包,造成外面访问它慢,它访问别人也慢。对此我们没有必要浪费珍贵的主干带宽,在济南布个点,同步一份数据过去就,让他们在自己省内访问,访问速度会立刻提升n倍。

电信:以几大省市为核心的环状结构,省市内部也是大环套小环。其中以广东用户最多,必须要部点的地方。记得很久以前我拿到一份数据说,上海人访问本IDC的数据,不如访问广东的速度快,不晓得现在是否还存在这种情况。电信有7个主要核心,分布在广州,上海,江苏,西安,成都,武汉,北京。

教育网:以国内主要的八个结点为核心。这八个结点分布在北京,西安,成都,广州,武汉,南京,上海,和沈阳。

3,DNS解析时候需要权衡的:
现在了解了这些信息,那我们开始讨论如何部署我们的服务。要考虑两个问题:一,要部署在哪几个IDC。二,每个IDC部署的服务器数量。三,DNS如何按区域划片。

要部署在哪几个IDC ?
其实这里还涉及到规模化应用的好处,一个小应用就部署了N多个IDC显然不划算。如果我的应用上了规模,我可以在每个省都部署上,那样用户体验将非常好,而且规模化以后会有专业人员对应用进行优化。所以公司里有动态池,和静态池这样的公用平台是好事(也许将来还会有我的js池)。如果我们的服务还没有上升到公司级别的规模,那就得考虑下取舍。

网通:东北三省,可以在沈阳和哈尔滨选择一个部署,有条件可以都部署。沈阳到北京的速率比哈尔滨到北京的速率快一倍,而哈尔滨到沈阳的速率,还不如到北京的快。北京,如果只让我在网通部署一个点的话,毫无疑问我会选择北京,其他所有结点到它的速率都比较快,但是北京的带宽比较昂贵。天津,这个点重要性仅次于北京,可以辐射河北,河南,江苏,离北京也比较近,价钱便宜。太原,可以辐射到西北一带。山东,前面已经说过,最好要部署的。

电信:那七个核心结点上部署了,速度就有保证。具体覆盖范围。广州覆盖周边几省,上海覆盖本地,江浙一带。武汉覆盖华中一带,西安可以覆盖西北5省,成都覆盖西南5省。

每个IDC部署的服务器数量?
这要根据具体应用来决定。比如财经用户网通,电信比例:3:4 而体育是 1:2 。教育网用户一般占1/30左右。这里还不能单纯考虑用户分布,还要考虑IDC内部灾备和IDC间灾备,是要有个取舍的。拿咱们的某个具体项目来说,教育网,够不上一台服务器,但是不得不部,因为它访问外界实在太慢了,我就住在学校里,也为了方便自己。我把北京作为主要结点部署了3台,天津,其实一台就够了,山东一台有点多。但是考虑到北京IDC一旦倒了,实力相当的IDC可以灾备,同时考虑到,天津,和山东只有一台,idc内部,都无法实现灾自动切换。所以,我选择天津两台,山东不部署,以性能换安全。

DNS如何按区域划片?
原则,就近分片,以达到最快传输速率。其次,考虑到各IDC间快速切换比较容易,DNS解析文件要写的简洁一些。另外,DNS解析有有个缺陷,每个单独域名里写在最前面的那个ip,它被轮询到的概率要比同组的服务器高10%,而且随着同组服务器的增多,这个差距会变大。所以最解析时候,每个IDC我都把硬件性能最好的服务器ip放在最前面。

另外:
做系统架构不提数据库,有点过不去。这块问题可以请教我们的DBA大牛zongwen同学。数据库是我将来一年的学习重点,争取一年后在DB方面能达到我们DBA六层功力。
Apr3

网站加速--服务器编写篇2【转自架构师杨建】

Author: 杨建  Click: 6517   Comments: 0 Category: 架构  Tag: 网站加速,服务器,网络架构
--提升性能的同时为你节约10倍以上成本
From: http://blog.sina.com.cn/iyangjian


七,NBA js直播的发展历程

这一节就谈下这个项目发展过程中所遇到的瓶颈,以及如何解决的。
应该是06年吧,当时NBA 比赛比较火,woocall负责高速模式图文直播放,普通模式和动态比分数据等都放在一群破服务器上,大概有十几20台,这些破服务器有些扛不住了。

因为第二天有一场比较大的比赛,我想埋连接在线上测一下效果,于是连夜把财经实时行情server改写成了NBA JS直播server. 当时有两台 Intel(R) Xeon(TM) CPU 3.00GHz 双cpu的服务器,在F5后面。先启用一台服务器,比赛开始前静悄悄的,不一会,迅速串到了20w connections,再往上增长,就慢的几乎不可访问, ethtool eth0  , Speed: 100Mb/s, 网卡出口带宽跑满了(那时候支持千兆的交换机还不多)。迅速把另一台服务器启用,后来又卡了,好象是F5处理能力不足。后来升级服务器出口带宽为1G,当然这需要交换机支持千兆口,更换网线,服务器也从F5后面转移出来,通过DNS直接轮询。

后来又测试了几次,等到新申请的Intel(R) Xeon(R) CPU 5120  @ 1.86GHz,双核双cpu服务器一到,就开始大规模部署,比赛更火了,不巧的是行情也火了起来,我的财经实时图片系统和行情系统也是带宽杀手,同时我也成了服务器杀手,到处蹭服务器。IDC带宽出口开始告急,我每天开始关注哪个机房还有富余带宽,有的机房被我们跑的太满,开始有人劝我们迁移到别的机房。后来 yangguang4劝我支持gzip输出,我觉得动态压缩太耗费cpu,不知道预先压缩是否可行,写个程序试了一把,没问题,于是NBA JS直播的的带宽一下子被砍掉了70%,而且没浪费一点我们的cpu,赚大了。

之后的两年里NBA JS服务一直很稳定,我几乎都没怎么看过,2007年的一场体育赛事中,单机达到25w+ connections,2.86w req/s ,cpu空闲30% ,见下图 (2CPU*2Core 1.86GHZ服务器) 。直到奥运期间,有场赛事,woocall瞬间仍给我近200w connections,网通的服务器被秒杀了1/3。这其实就是善意的DDOS攻击,这些用户如果正常进入是没有问题了,瞬间扔过来,超出了操作系统极限,系统挂掉了,我的服务也over了。在调整内核参数里有讲,怎么修改内核参数提高服务器抗秒杀能力,但是不能杜绝。

下图为2007年一场比赛时,单机25w+ connections,2.86w req/s,的状态(2CPU*2Core 1.86GHZ):


奥运结束后,我对服务器程序和架构做了调整,砍掉了2/3的服务器。但我没留意的是,同样connections,实际http请求增加了一倍,因为新上了一个flash方位图,里面增加了3个js,增加就增加吧,既然砍了就不准备再恢复了。但是服务在2CPU*4Core centos5 32bit上的表现却让我很失望,跑不过2CPU*2Core centos4 32bit 。我开始怀疑程序升级的时候是不是有什么地方没考虑到,开始调程序,折腾几天没有结果,症状是单机支撑12.5万时候没有任何异常,内存使用1%左右,总cpu使用了5%左右,load 0.5,但是再增加0.1w用户server肯定崩溃,每次都是相同的表现,我知道在什么地方卡住了。后来看了下dmesg,才恍然大悟,我是被32bit centos 5的内核暗杀的(Out of memory: Killed process xxx)。

32位机上LowFree一般是会变化的(cat /proc/meminfo  | grep LowFree),最大不能超过880M(这个值不能改变除非用hugemem内核),在centos4 上有内核参数vm.lower_zone_protection(单位是M)来调节LowFree,默认vm.lower_zone_protection=0 ,LowFree=16M,但是在centos5上这个参数貌似被取消了,改变不了。从使用经验来看,也就是你能申请16M~880M这么大的连续内存,但是16M以上不保证你能申请的到。centos4用到64M以上都没什么问题,centos5 用40M+ 就被OOM Killer给毙了。

本周开始使用64bit centos5.2进行测试,迁移很顺利,没有修改一行代码,他们把整个16G物理内存都作为LowFree,这下可以随便挥霍了(尽管如此我还是会节约的),前几天的比赛,这个64bit机跑了18w connections,很安静,未见异常,等有大比赛再检验下,没问题的话就开始大规模使用64bit系统。

目前看来,如果成功迁移64bit系统似乎可以高枕无忧了,但是我还是有两个忧虑:

1,再突然甩200w connections给我,我不敢保证能扛的住,因为现在服务器数量消减太多,需要yangguang4那边做策略调整,在比赛结束后,平滑的把用户丢给我,这应该有个持续过程,而不是一秒内的瞬间。

2,我猜这个系统,单机支撑到30w conections的时候会遇到瓶颈,因为网卡的中断集中在cpu0上,没有均衡开。我们有硬件解决方案已经实现(每个服务器会多2000RMB开销)我只部署了一台,但是软的还没实现,寄希望于xiaodong2 。

补充:
昨天的比赛中,一台64bit机,单机支撑30w+ connections,cpu0空闲率只剩6%,和我的预料是一致的。当时的CPU总量被我用掉近 1/4,内存被我用掉 0.5% 。

下图为30w+ connections, 4.6w req/s 的时候我的程序使用的资源情况(2cpu*4Core):


下图为cpu使用分布情况,cpu0空闲率只剩 6% (2cpu*4Core):


另外附上一个23w connections, 3.5w req/s 的时候我的程序使用的资源情况(2cpu*4Core),当时cpu只被用掉1/8,内存被用掉 0.4% ,cpu没有发挥线性增加的作用,我肯定不说能我可以支撑23w*8,但是保守地说,只要把网卡中断分散一下,单机50w+ connections很easy。
 


八,新浪财经实时行情系统的历史遗留问题 (7 byte = 10.68w RMB/year)

这点我还是提下吧,估计我不说,大家也想不到。
先感谢wangyun同学的大胆使用才有了今天的财经实时行情系统(当初是从一台PIII 900服务器上发展起来的,前几天刚被我下线)不过 "hq_str_"  这7个字节的前缀,也是他造成的,当初他说改抓取页面有些麻烦,就让我写死在server里,虽然意识到将来也许会有隐患,但我还是照做了。见下面返回数据:
http://hq.sinajs.cn/list=s_sz000609,s_sz000723,s_sh000001
var hq_str_s_sz000609="绵世股份,9.29,-0.05,-0.54,170945,16418"; 
var hq_str_s_sz000723="美锦能源,0.00,0.00,0.00,0,0";
var hq_str_s_sh000001="上证指数,2031.681,-47.436,-2.28,1216967,8777380";
我算了一笔帐,行情好的时候每秒会产生30~40w个请求,一般一个请求会请求3~50只股票,保守点就按每个请求5只股票来计算,每秒会产生200w只股票查询信息。由于这7个字节产生的带宽为: 200w  7byte  * 8bit / 1024 /1024 = 106.8 M  ,而往往我们的带宽要按峰值来准备,按1G带宽100w RMB/year 计算,每年耗费10.68w RMB。把这笔钱拿给我做奖金,我会很happy的 ^-^ . 现在因为很多页面都使用了行情数据,想修改,代价很高。

所以设计系统的时候一定要考虑的稍微远一些,哪怕当时只是一点点微不足道的地方,要考虑将来访问规模变大了会是什么后果。还有就是要敢于坚持自己的原则。

分类

标签

归档

最新评论

Abyss在00:04:28评论了
Linux中ramdisk,tmpfs,ramfs的介绍与性能测试
shallwe99在10:21:17评论了
【原创】如何在微信小程序开发中正确的使用vant ui组件
默一在09:04:53评论了
Berkeley DB 由浅入深【转自架构师杨建】
Memory在14:09:22评论了
【原创】最佳PHP框架选择(phalcon,yaf,laravel,thinkphp,yii)
leo在17:57:04评论了
shell中使用while循环ssh的注意事项

我看过的书

链接

其他

访问本站种子 本站平均热度:8823 c° 本站链接数:1 个 本站标签数:464 个 本站被评论次数:94 次