淘宝网采用什么技术架构来实现网站高负载的(转)

时间过得很快,来淘宝已经两个月了,在这两个月的时间里,自己也感受颇深。下面就结合淘宝目前的一些底层技术框架以及自己的一些感触来说说如何构建一个可
伸缩,高性能,高可用性的分布式互联网应用。

add by zhj: 有时间要好好看看,跟大牛学学。

相关专题:淘宝双11背后高并发技术讨论

原文:

一 应用无状态(淘宝session框架)

系列文章:

俗话说,一个系
统的伸缩性的好坏取决于应用的状态如何管理。为什么这么说呢?咱们试想一下,假如我们在session中保存了大量与客户端的状态信
息的话,那么当保存状态信息的server宕机的时候,我们怎么办?通常来说,我们都是通过集群来解决这个问题,而通常所说的集群,不仅有负载均衡,更重要的是要有失效恢复failover,比如tomcat采
用的集群节点广播复制,jboss采 用的配对复制等session状
态复制策略,但是集群中的状态恢复也有其缺点,那就是严重影响了系统的伸缩性,系统不能通过增加更多的机器来达到良好的水平伸缩,因为集群节点间session的
通信会随着节点的增多而开销增大,因此要想做到应用本身的伸缩性,我们需要保证应用的无状态性,这样集群中的各个节点来说都是相同的,从而是的系统更好的水平伸缩。

 

OK,上面说了无状态的重要性,那么具体如何实现无状态呢?此时一个session框架就会发挥作用了。幸运的是淘
宝已经具有了此类框架。淘宝的session框架采用的是client
cookie实现,主要将状态保存到了cookie里
面,这样就使得应用节点本身不需要保存任何状态信息,这样在系统用户变多的时候,就可以通过增加更多的应用节点来达到水平扩展的目的.但
是采用客户端cookie的
方式来保存状态也会遇到限制,比如每个cookie一般不能超过4K的大小,同时很多浏览器都限制一个站点最多保存20个cookie.淘
宝cookie框 架采用的是“多值cookie”, 就是一个组合键对应多个cookie的
值,这样不仅可以防止cookie数 量超过20, 同时还节省了cookie存
储有效信息的空间,因为默认每个cookie都会有大约50个字节的元信息来描述cookie。

时间过得很快,来淘宝已经两个月了,在这两个月的时间里,自己也感受颇深。下面就结合淘宝目前的一些底层技术框架以及自己的一些感触来说说如何构建一个可
伸缩,高性能,高可用性的分布式互联网应用。

除了淘宝目前的session框
架的实现方式以外,其实集中式session管理来完成,说具体点就是多个无状态的应用节点连接一个session
服 务器,session服 务器将session保 存到缓存中,session服
务器后端再配有底层持久性数据源,比如数据库,文件系统等等。

 

二 有效使用缓存(Tair)

相关专题:淘宝双11背后高并发技术讨论

做互联网应用的兄弟应该都清楚,缓存对于一个互联网应用是多么的重要,从浏览器缓存,反向代理缓存,页面缓存,局部页面缓存,对象缓存等等都是缓存应用的场景。

一 应用无状态(淘宝session框架)

一 般来说缓存根据与应用程序的远近程度不同可以分为:local cache 和 remote
cache。 一般系统中要么采用local cache,要么采用remote
cache,两者混合使用的话对于local cache和remote cache的数据一致性处理会变
大比较麻烦.

www.hj8828.com,俗话说,一个系
统的伸缩性的好坏取决于应用的状态如何管理。为什么这么说呢?咱们试想一下,假如我们在session中保存了大量与客户端的状态信
息的话,那么当保存状态信息的server宕机的时候,我们怎么办?通常来说,我们都是通过集群来解决这个问题,而通常所说的集群,不仅有负载均衡,更重
要的是要有失效恢复failover,比如tomcat采 用的集群节点广播复制,jboss采
用的配对复制等session状
态复制策略,但是集群中的状态恢复也有其缺点,那就是严重影响了系统的伸缩性,系统不能通过增加更多的机器来达到良好的水平伸缩,因为集群节点间
session的
通信会随着节点的增多而开销增大,因此要想做到应用本身的伸缩性,我们需要保证应用的无状态性,这样集群中的各个节点来说都是相同的,从而是的系统更好的
水平伸缩。

在大部分情况下,我 们所说到的缓存都是读缓存,缓存还有另外一个类型:写缓存.

于一些读写比不高,同时对数据安全性需求不高的数据,我们可以将其缓存起来从而减少对底层数据库的访问,比如
统计商品的访问次数,统 计API的 调用量等等,可
以采用先写内存缓存然后延迟持久化到数据库,这样可以大大减少对数据库的写压力。

OK,上面说了无状态的重要性,那么具体如何实现无状态呢?此时一个session框架就会发挥作用了。幸运的是淘
宝已经具有了此类框架。淘宝的session框架采用的是client
cookie实现,主要将状态保存到了cookie里
面,这样就使得应用节点本身不需要保存任何状态信息,这样在系统用户变多的时候,就可以通过增加更多的应用节点来达到水平扩展的目的.但
是采用客户端cookie的
方式来保存状态也会遇到限制,比如每个cookie一般不能超过4K的大小,同时很多浏览器都限制一个站点最多保存20个cookie.淘
宝cookie框 架采用的是“多值cookie”, 就是一个组合键对应多个cookie的
值,这样不仅可以防止cookie数 量超过20, 同时还节省了cookie存
储有效信息的空间,因为默认每个cookie都会有大约50个字节的元信息来描述cookie。

OK,我以店铺线的系统为例,在用户浏览店铺的时候,比如店铺介绍,店铺交流区页面,店铺服务条款页面,店铺试衣间页面,以及店铺内搜索界面这些界面更新不是非常频繁,因此适合放到缓存中,这样可以大大减低DB的负载。另外宝贝详情页面相对也更新比较
少,因此也适合放到缓存中来减低DB负载。

除了淘宝目前的session框
架的实现方式以外,其实集中式session管理来完成,说具体点就是多个无状态的应用节点连接一个session
服 务器,session服 务器将session保 存到缓存中,session服
务器后端再配有底层持久性数据源,比如数据库,文件系统等等。

三 应用拆分(HSF)

二 有效使用缓存(Tair)

首先,在说明应用拆分之前,我们先来回顾一下一个系统从小变大的过程中遇到的一些问题,通过这些问题我们会发现拆分对于构建一个大型系统是如何的重要。

做互联网应用的兄弟应该都清楚,缓存对于一个互联网应用是多么的重要,从浏览器缓存,反向代理缓存,页面缓存,局部页面缓存,对象缓存等等都是缓存应用的场景。

系统刚上线初期,用户数并不多,所有的逻辑也许都是放在一个系统中的,所有逻辑跑到一个进程或者一个应用当中,这个时候因为比较用户少,系统访问量低,因此将全部的逻辑都放在一个应用未尝不可。但是,兄弟们都清楚,好景不长,随着系统用户的不断增加,系统的访问压力越来越多,同时随着系统发展,为了满足用户
的需求,原有的系统需要增加新的功能进来,系统变得越来越复杂的时候,我们会发现系统变得越来越难维护,难扩展,同时系统伸缩性和可用性也会受到影响。那么这个时候我们如何解决这些问题呢?明智的办法就是拆分(这也算是一种解耦),我们需要将原来的系统根据一定的标准,比如业务相关性等分为不同的子系统,
不同的系统负责不同的功能,这样切分以后,我们可以对单独的子系统进行扩展和维护,从而提高系统的扩展性和可维护性,同时我们系统的水平伸缩性scale
out大
大的提升了,因为我们可以有针对性的对压力大的子系统进行水平扩展而不会影响到其它的子系统,而不会像拆分以前,每次系统压力变大的时候,我们都需要对整个大系统进行伸缩,而这样的成本是比较大的,另外经过切分,子系统与子系统之间的耦合减低了,当某个子系统暂时不可用的时候,整体系统还是可用的,从而整
体系统的可用性也大大增强了。

一 般来说缓存根据与应用程序的远近程度不同可以分为:local cache 和 remote
cache。 一般系统中要么采用local cache,要么采用remote
cache,两者混合使用的话对于local cache和remote cache的数据一致性处理会变
大比较麻烦.

因此一个大型的互联网应用,肯定是要经过拆分,因为只有拆分了,系统的扩展性,维护性,伸缩性,可用性才会变的更好。但是拆分也给系统带来了问题,就是子系统之间如何通信的问题,而具体的通信方式有哪些呢?一般有同步通信和异步通信,这里我们首先来说下同步通信,下面的主题“消息系
统”会说到异步通信。既然需要通信,这个时候一个高性能的远程调用框架就显得非常总要啦,因此咱们淘宝也有了自己的HSF框
架。

在大部分情况下,我 们所说到的缓存都是读缓存,缓存还有另外一个类型:写缓存.

于一些读写比不高,同时对数据安全性需求不高的数据,我们可以将其缓存起来从而减少对底层数据库的访问,比如
统计商品的访问次数,统 计API的 调用量等等,可
以采用先写内存缓存然后延迟持久化到数据库,这样可以大大减少对数据库的写压力。


面所说的都是拆分的好处,但是拆分以后必然的也会带来新的问题,除了刚才说的子系统通信问题外,最值得关注的问题就是系统之间的依赖关系,因为系统多了,系统的依赖关系就会变得复杂,此时就需要更好的去关注拆分标准,比如能否将一些有依赖的系统进行垂直化,使得这些系统的功能尽量的垂直,这也是目前淘宝正
在做的系统垂直化,同时一定要注意系统之间的循环依赖,如果出现循环依赖一定要小心,因为这可能导致系统连锁启动失败。

OK,我以店铺线的系统为例,在用户浏览店铺的时候,比如店铺介绍,店铺交流区页面,店铺服务条款页面,店铺试衣间页面,以及店铺内搜索界面这些界
面更新不是非常频繁,因此适合放到缓存中,这样可以大大减低DB的负载。另外宝贝详情页面相对也更新比较
少,因此也适合放到缓存中来减低DB负载。

OK,既然明白了拆分的重要性,我们看看随着淘宝的发展,淘宝本身是如何拆分系统的。

三 应用拆分(HSF)

首先我们来看以下这个图:作者图片已无法打开,请见谅)

首先,在说明应用拆分之前,我们先来回顾一下一个系统从小变大的过程中遇到的一些问题,通过这些问题我们会发现拆分对于构建一个大型系统是如何的重要。

从上面的图可以看出淘宝系统的一个演变过程,在这个演变的过程中,我们所说的拆分就出现V2.2和V3.0之
间。在V2.2版
本中,淘宝几乎所有的逻辑都放在(Denali)系统中,这样导致的问题就是系统扩展和修改非常麻烦,并且更加致命的是随着淘宝业务量的增加,如果按照V2.2的架构已经没有办法支撑以后淘宝的快速发展,因此大家决定对整个系统进行拆分,最
终V3.0版本的淘宝系统架构图如下:作者图片已无法打开,请见谅)

系统刚上线初期,用户数并不多,所有的逻辑也许都是放在一个系统中的,所有逻辑跑到一个进程或者一个应用当中,这个时候因为比较用户少,系统访问量
低,因此将全部的逻辑都放在一个应用未尝不可。但是,兄弟们都清楚,好景不长,随着系统用户的不断增加,系统的访问压力越来越多,同时随着系统发展,为了
满足用户
的需求,原有的系统需要增加新的功能进来,系统变得越来越复杂的时候,我们会发现系统变得越来越难维护,难扩展,同时系统伸缩性和可用性也会受到影响。那
么这个时候我们如何解决这些问题呢?明智的办法就是拆分(这也算是一种解耦),我们需要将原来的系统根据一定的标准,比如业务相关性等分为不同的子系统,
不同的系统负责不同的功能,这样切分以后,我们可以对单独的子系统进行扩展和维护,从而提高系统的扩展性和可维护性,同时我们系统的水平伸缩性scale
out大
大的提升了,因为我们可以有针对性的对压力大的子系统进行水平扩展而不会影响到其它的子系统,而不会像拆分以前,每次系统压力变大的时候,我们都需要对整
个大系统进行伸缩,而这样的成本是比较大的,另外经过切分,子系统与子系统之间的耦合减低了,当某个子系统暂时不可用的时候,整体系统还是可用的,从而整
体系统的可用性也大大增强了。

从上图可以看出V3.0版
本的系统对整个系统进行了水平和垂直两个方向的拆分,水平方向上,按照功能分为交易,评价,用户,商品等系统,同样垂直方向上,划分为业务系统,核心业务系统以及以及基础服务,这样以来,各个系统都可以独立维护和独立的进行水平伸缩,比如交易系统可以在不影响其它系统的情况下独立的进行水平伸缩以及功能扩展。

因此一个大型的互联网应用,肯定是要经过拆分,因为只有拆分了,系统的扩展性,维护性,伸缩性,可用性才会变的更好。但是拆分也给系统带来了问题,
就是子系统之间如何通信的问题,而具体的通信方式有哪些呢?一般有同步通信和异步通信,这里我们首先来说下同步通信,下面的主题“消息系
统”会说到异步通信。既然需要通信,这个时候一个高性能的远程调用框架就显得非常总要啦,因此咱们淘宝也有了自己的HSF框
架。

从上面可以看出,一个大型系统要想变得可维护,可扩展,可伸缩,我们必须的对它进行拆分,拆分必然也带来系统之间如何通信以及系统之间依赖管理等问题,关于通信方面,淘宝目前独立开发了自己的高性
能服务框架HSF,
此框架主要解决了淘宝目前所有子系统之间的同步和异步通信(目前HSF主要用于同步场合,FutureTask方
式的调用场景还比较少)。至于系统间的依赖管理,目前淘宝还做的不够好,这应该也是我们以后努力解决的问题。


面所说的都是拆分的好处,但是拆分以后必然的也会带来新的问题,除了刚才说的子系统通信问题外,最值得关注的问题就是系统之间的依赖关系,因为系统多了,
系统的依赖关系就会变得复杂,此时就需要更好的去关注拆分标准,比如能否将一些有依赖的系统进行垂直化,使得这些系统的功能尽量的垂直,这也是目前淘宝正
在做的系统垂直化,同时一定要注意系统之间的循环依赖,如果出现循环依赖一定要小心,因为这可能导致系统连锁启动失败。

四 数据库拆分(TDDL)

OK,既然明白了拆分的重要性,我们看看随着淘宝的发展,淘宝本身是如何拆分系统的。

在前面“应用拆分”主题中,我们提到了一个大型互联网应用需要进行良好的拆分,而那里我们仅仅说了”应用级别”的拆
分,其实我们的互联网应用除了应用级别的拆分以外,还有另外一个很重要的层面就是存储如何拆分的。因此这个主题主要涉及到如何对存储系统,通常就是所说的RDBMS进
行拆分。

首先我们来看以下这个图:(作者图片已无法打开,请见谅)

好了,确定了这个小节的主题之后,我们回顾一下,一个互联网应用从小变大的过程中遇到的一些问题,通过遇到的问题来引出我们拆分RDBMS的
重要性。

从上面的图可以看出淘宝系统的一个演变过程,在这个演变的过程中,我们所说的拆分就出现V2.2和V3.0之
间。在V2.2版
本中,淘宝几乎所有的逻辑都放在(Denali)系统中,这样导致的问题就是系统扩展和修改非常麻烦,并且更加致命的是随着淘宝业务量的增加,如果按照
V2.2的架构已经没有办法支撑以后淘宝的快速发展,因此大家决定对整个系统进行拆分,最
终V3.0版本的淘宝系统架构图如下:(作者图片已无法打开,请见谅)


统刚开始的时候,因为系统刚上线,用户不多,那个时候,所有的数据都放在了同一个数据库中,这个时候因为用户少压力小,一个数据库完全可以应付的了,但是随着运营那些哥们辛苦的呐喊和拼命的推广以后,突然有一天发现,oh,god,用户数量突然变多了起来,随之而
来的就是数据库这哥们受不了,它终于在某一天大家都和惬意的时候挂掉啦。此时,咱们搞技术的哥们,就去看看究竟是啥原因,我们查了查以后,发现原来是数据库读取压力太大了,此时咱们都清楚是到了读写分离的时候,这个时候我们会配置一个server为master节
点,然后配几个salve节
点,这样以来通过读写分离,使得读取数据的压力分摊到了不同的salve节点上面,系统终于又恢复了正常,开始正常运行了。但是好景还是不长,有一天我们发现master这哥们撑不住了,它负载老高了,汗
流浃背,随时都有翘掉的风险,这个时候就需要咱们垂直分区啦(也就是所谓的分库),比如将商品信息,用户信息,交易信息分别存储到不同的数据库中,同时还可以针对商品信息的库采用master,salve模式,OK,
通过分库以后,各个按照功能拆分的数据库写压力被分担到了不同的server上面,这样数据库的压力终于有恢复到正常状态。但是是不是这样,我们就可以高枕无忧了呢?NO,这个NO,
不是我说的,是前辈们通过经验总结出来的,随着用户量的不断增加,你会发现系统中的某些表会变的异常庞大,比如好友关系表,店铺的参数配置表等,这个时候无论是写入还是读取这些表的数据,对数据库来说都是一个很耗费精力的事情,因此此时就需要我们进行“水平分区”了(这就是俗话说的分表,或者说sharding).

从上图可以看出V3.0版
本的系统对整个系统进行了水平和垂直两个方向的拆分,水平方向上,按照功能分为交易,评价,用户,商品等系统,同样垂直方向上,划分为业务系统,核心业务
系统以及以及基础服务,这样以来,各个系统都可以独立维护和独立的进行水平伸缩,比如交易系统可以在不影响其它系统的情况下独立的进行水平伸缩以及功能扩
展。