大型网站技术架构的演进与设计

原创
2015/11/04 17:08
阅读数 2.4K

#0 系列目录#

一个成熟的大型网站(如淘宝、京东等)的系统架构并不是开始设计就具备完整的高性能、高可用、安全等特性,它总是随着用户量的增加,业务功能的扩展逐渐演 变完善的,在这个过程中,开发模式、技术架构、设计思想也发生了很大的变化,就连技术人员也从几个人发展到一个部门甚至一条产品线。所以成熟的系统架构是 随业务扩展而完善出来的,并不是一蹴而就;不同业务特征的系统,会有各自的侧重点,例如淘宝,要解决海量的商品信息的搜索、下单、支付,例如腾讯,要解决 数亿的用户实时消息传输,百度它要处理海量的搜索请求,他们都有各自的业务特性,系统架构也有所不同。尽管如此我们也可以从这些不同的网站背景下,找出其 中共用的技术,这些技术和手段可以广泛运行在大型网站系统的架构中,下面就通过介绍大型网站系统的演化过程,来认识这些技术和手段。

核心内容:流量从小到大过程中,架构是如何演进的?遇到了哪些问题?以及如何解决这些问题?

核心观点:好的架构不是设计出来的,而是进化而来的。

如何演进:站点流量在不同阶段,会遇到不同的问题,找到对应阶段站点架构所面临的主要问题,在不断解决这些问题的过程中,整个系统的架构就不断的演进了。

如何演进,简言之:找到主要矛盾,并解决主要矛盾

#1 建站之初# 建站之初,站点流量非常小,可能低于十万级别。这意味着,平均每秒钟也就几次访问。请求量比较低,数据量比较小,代码量也比较小,几个工程师,很短的时间搭起这样的系统,甚至没有考虑“架构”的问题。

##1.1 架构:ALL-IN-ONE## 和许多创业公司初期一样,最初站点架构特点是**“ALL-IN-ONE”**:

输入图片说明

这是一个单机系统,所有的站点、数据库、文件都部署在一台服务器上。工程师每天的核心工作是CURD,浏览器端传过来一些数据,解析GET/POST/COOKIE中传过来的数据,拼装成一些CURD的sql语句访问数据库,数据库返回数据,拼装成页面,返回浏览器。相信很多创业团队的工程师,初期做的也是类似的工作。如图:

输入图片说明

##1.2 技术## 输入图片说明

初创阶段,工程师面临的主要问题:写CURD的sql语句很容易出错。我们在这个阶段引进DAO和ORM,让工程师们不再直接面对CURD的sql语句,而是面对他们比较擅长的面向对象开发,极大的提高了编码效率,降低了出错率。

#2 流量增加,数据库成为瓶颈# 随着流量越来越大,老板不只要求“有一个可以看见的站点”,他希望网站能够正常访问,当然速度快点就更好了。

而此时系统面临问题是:流量的高峰期容易宕机,大量的请求会压到数据库上,数据库成为新的瓶颈,人多并行访问时站点非常卡。这时,我们的机器数量也从一台变成了多台,我们的系统成了所谓的(伪)“分布式架构”

##2.1 架构:分布式## 输入图片说明

使用了一些常见优化手段:

  1. 动静分离,动态的页面通过Web-Server访问,静态的文件例如图片就放到单独的文件服务器上;
  2. 读写分离,将落到数据库上的读写请求分派到不同的数据库服务器上;

互联网绝大部分的业务场景,都是读多写少。绝大部分用户的需求是访问信息,搜索信息,只有少数的用户发贴。此时读取性能容易成为瓶颈,那么如何扩展整个站点架构的读性能呢?常用的方法是主从同步,增加从库。我们原来只有一个读数据库,现在有多个读数据库,就提高了读性能。

随着业务的扩展,一台服务器已经不能满足性能需求,故将应用程序、数据库、文件各自部署在独立的服务器上,并且根据服务器的用途配置不同的硬件,达到最佳的性能效果。

输入图片说明

在这个阶段,系统的主要矛盾为“站点耦合+读写延时”,如何解决这两个问题的呢?

第一个问题是站点耦合。典型业务场景是:类别聚合的主页,发布信息的发布页,信息聚合的列表页,帖子内容的详细页,原来这些系统都耦合在一个站点中,出现问题的时候,整个系统都会受到影响。

第二个问题是读写延时。数据库做了主从同步和读写分离之后,读写库之间数据的同步有一个延时,数据库数据量越大,从库越多时,延时越明显。对应到业务,有用户发帖子,马上去搜索可能搜索不到(着急的用户会再次发布相同的帖子)。

##2.2 架构:垂直拆分## 输入图片说明

要解决耦合的问题,最先想到的是针对核心业务做切分,工程师根据业务切分对系统也进行切分:我们将业务垂直拆分成了首页、发布页、列表页和详情页。

另外,我们在数据库层面也进行了垂直拆分,将单库数据量降下来,让读写延时得到缓解。随着用户量的增加,数据库成为最大的瓶颈,改善数据库性能常用的手段是进行读写分离以及分表,读写分离顾名思义就是将数据库分为读库和写库,通过主备功能实现数据同步。分库分表则分为水平切分和垂直切分,水平切换则是对一个数据库特大的表进行拆分,例如用户表。垂直切分则是根据业务不同来切换,如用户业 务、商品业务相关的表放在不同的数据库中。

输入图片说明

  1. 垂直拆分数据库时,会遇到的问题:

  2. 跨业务的事务;

  3. 应用的配置项多了;

  4. 关于事务的问题,有两种办法:

  5. 使用分布式事务

  6. 去掉事务或不追求强事务;

  7. 数据水平拆分会遇到的问题:

  8. SQL的路由问题,需要知道某个User在哪个数据库上。

  9. 主键的策略会有不同。

  10. 查询时的性能问题,如分页问题。

  11. 关于数据水平拆分问题,解决办法:

  12. 使用搜索引擎:解决数据查询问题;

  13. 部分场景可使用NoSQL提高性能;

  14. 开发数据统一访问模块:解决上层应用开发的数据源问题

##2.3 技术## 输入图片说明

同时,还使用了这些技术来优化系统和提高研发效率:

  1. 对动态资源和静态资源进行拆分。对静态资源我们使用了CDN服务,用户就近访问,静态资源的访问速度得到很明显的提升;

  2. 除此之外,我们还使用了MVC模式,擅长前端的工程师去做展示层,擅长业务逻辑的工程师就做控制层,擅长数据的工程师就做数据层,专人专用,研发效率和质量又进一步提高。

在硬件优化性能的同时,同时也通过软件进行性能优化,在大部分的网站系统中,都会利用缓存技术改善系统的性能,使用缓存主要源于热点数据的存在,大部分网站访问都遵循28原则(即80%的访问请求,最终落在20%的数据上),所以我们可以对热点数据进行缓存,减少这些数据的访问路径,提高用户体验。

输入图片说明

缓存实现常见的方式是本地缓存、分布式缓存。当然还有CDN、反向代理等,这个后面再讲。本地缓存,顾名思义是将数据缓存在应用服务器本地,可以存在内存 中,也可以存在文件,OSCache就是常用的本地缓存组件。本地缓存的特点是速度快,但因为本地空间有限所以缓存数据量也有限。分布式缓存的特点是,可 以缓存海量的数据,并且扩展非常容易,在门户类网站中常常被使用,速度按理没有本地缓存快,常用的分布式缓存是Memcached、Redis。

#3 使用集群改善应用服务器性能# 随着用户量的增加,对站点可用性要求也越来越高,机器数也从最开始的几台上升到几百台。那么如何提供保证整个系统的可用性呢?首先,我们在业务层做了进一步的垂直拆分,同时引入了Cache

##3.1 架构:高可用## 输入图片说明

在架构上,我们抽象了一个相对独立的服务层,所有数据的访问都通过这个服务层统一来管理,上游业务线就像调用本地函数一样,通过RPC的框架来调用这个服务获取数据,服务层对上游屏蔽底层数据库与缓存的复杂性。

应用服务器作为网站的入口,会承担大量的请求,我们往往通过应用服务器集群来分担请求数。应用服务器前面部署负载均衡服务器调度用户请求,根据分发策略将请求分发到多个应用服务器节点。

输入图片说明

常用的负载均衡技术硬件的有F5,价格比较贵,软件的有LVS、Nginx、HAProxyLVS是四层负载均衡,根据目标地址和端口选择内部服务器,Nginx是七层负载均衡HAProxy支持四层、七层负载均衡,可以根据报文内容选择内部服务器,因此LVS分发路径优于Nginx和HAProxy,性能要高些,而Nginx和HAProxy则更具配置性,如可以用来做动静分离(根据请求报文特征,选择静态资源服务器还是应用服务器)。

##3.2 技术:反向代理## 输入图片说明

除此之外,为了保证站点的高可用,我们使用了反向代理

什么是代理?代理就是代表用户访问xxoo站点。

什么是反向代理?反向代理代表的是网站,用户不用关注访问是哪台服务器,由反向代理来代表网站。网站通过反向代理,DNS轮询, LVS等技术,来保证接入层的高可用性。

另外,为了保证服务层和数据层的高可用,我们采用了冗余的方法,单点服务不可用,我们就冗余服务,单点数据不可用,我们就冗余数据

这个阶段,为了进一步解耦系统,我们引入了配置中心、柔性服务和消息总线

##3.3 架构:进一步解耦## 输入图片说明

引入配置中心,业务要访问任何一个服务,不需要在本地的配置文件中配置服务的ip list,而只需要访问配置中心。这种方式的扩展性非常好,如果有机器要下线,配置中心会反向通知上游订阅方,而不需要更新本地配置文件。

柔性服务是指当流量增加的时候,自动的扩展服务和站点。

消息总线也是一种解耦上下游“调用”关系常见的技术手段。

机器越来越多,此时很多系统层面的问题,靠“人肉”已经很难搞定,于是自动化变得越来越重要:自动化回归、自动化测试、自动化运维、自动化监控等等等等。

##3.4 Session管理## 但是,使用服务器集群时,需要考虑一个问题:Session的管理问题。Session的管理有以下几种方式:

  1. Session Sticky:打个比方就是如果我们每次吃饭都要保证我们用的是自己的碗筷,而只要我们在一家饭店里存着我们的碗筷,只要我们每次去这家饭店吃饭就好了。

输入图片说明

这种方式的问题:

  1. 一台服务器重启,上面的session都没了;

  2. 负载均衡器成了有状态的机器,要实现容灾会有麻烦

  3. Session复制:就像我们在所有的饭店里都存一份自己的碗筷。不适合做大规模集群,适合机器不多的情况

输入图片说明

这种方案的问题:

  1. 应用服务器间带宽问题

  2. 大量用户在线时,占用内存过多

  3. 基于Cookie:类似于每次吃饭都把自己的碗筷带上

输入图片说明

这种方案的问题:

  1. Cookie的长度限制

  2. 安全性;

  3. 数据中心外部带宽的消耗

  4. 性能影响,服务器处理每次的请求的内容又多了

  5. Session服务器:同样可以是集群的。这种方式适用于session数量及web服务器数量大的情况

输入图片说明

这种方案需要考虑的是:

  1. 保证session服务器的可用性;

  2. 我们在写应用时需要做调整,我目前不知道应用服务器能否将这部分逻辑透明化;

#4 使用CDN和反向代理提高网站性能# 假如我们的服务器都部署在成都的机房,对于四川的用户来说访问是较快的,而对于北京的用户访问是较慢的,这是由于四川和北京分别属于电信和联通的不同发达 地区,北京用户访问需要通过互联路由器经过较长的路径才能访问到成都的服务器,返回路径也一样,所以数据传输时间比较长。对于这种情况,常常使用CDN解 决,CDN将数据内容缓存到运营商的机房,用户访问时先从最近的运营商获取数据,这样大大减少了网络访问的路径。比较专业的CDN运营商有蓝汛、网宿

反向代理,则是部署在网站的机房,当用户请求达到时首先访问反向代理服务器,反向代理服务器将缓存的数据返回给用户,如果没有没有缓存数据才会继续走应用服务器获取,也减少了获取数据的成本。反向代理有Squid,Nginx。

输入图片说明

#5 使用分布式文件系统# 用户一天天增加,业务量越来越大,产生的文件越来越多,单台的文件服务器已经不能满足需求。需要分布式的文件系统支撑。常用的分布式文件系统有NFS

输入图片说明

#6 使用NoSql和搜索引擎# 对于海量数据的查询,我们使用nosql数据库加上搜索引擎可以达到更好的性能。并不是所有的数据都要放在关系型数据中。常用的NOSQL有mongodb和redis,搜索引擎有lucene。

输入图片说明

#7 将应用服务器进行业务拆分# 随着业务进一步扩展,应用程序变得非常臃肿,这时我们需要将应用程序进行业务拆分,如百度分为新闻、网页、图片等业务。每个业务应用负责相对独立的业务运作。业务之间通过消息进行通信或者同享数据库来实现

输入图片说明

#8 搭建分布式服务# 这时我们发现各个业务应用都会使用到一些基本的业务服务,例如用户服务、订单服务、支付服务、安全服务,这些服务是支撑各业务应用的基本要素。我们将这些服务抽取出来利用分部式服务框架搭建分布式服务。淘宝的Dubbo是一个不错的选择

输入图片说明

展开阅读全文
打赏
0
54 收藏
分享
加载中
good
2015/11/04 23:40
回复
举报
感谢分享!
2015/11/04 18:26
回复
举报
更多评论
打赏
2 评论
54 收藏
0
分享
OSCHINA
登录后可查看更多优质内容
返回顶部
顶部