1、简介

大型网站架构的演进最开始都是由小及大慢慢演变过来的,任何一个好的架构都不是设计出来的,是经过业务发展迭代而来的,这个观点我是赞同的。对于网站架构技术非常有兴趣,一直持续关注学习架构技术,本次想通过大型网站技术发展历程,剖析大型网站技术架构模式,深入分析大型互联网架构设计。这篇文章我们只关注架构的演变历程。
大型网站架构演变_第1张图片
通过电商业务为例,该系统的功能有用户模块【用户注册和管理】、商品模块【商品展示和管理】、交易模块【创建交易和管理】。通过图例分析一个最初从单台LAMP怎么发展到庞大的分布架构体系。

线上系统高可用参考指标:
大型网站架构演变_第2张图片
大型网站架构演变_第3张图片

2、架构演变历程

2.1. 第一阶段【单机构建网站】

网站程序用到的开源框架如maven+spring+struct+hibernate、maven+spring+springmvc+mybatis;网站初期,我们经常会用单机跑所有的程序和软件。通常由app server 和DB server组成,如tomcat和mysq;最后通过JDBC进行数据库的连接和操作。
大型网站架构演变_第4张图片

一般5万pv到30万pv访问量,结合内核参数调优、web应用性能参数调优、数据库调优,基本上能够稳定的运行。

2.2. 第二阶段【应用服务器与数据库分离】

随着网站的发展当访问量逐渐增大,服务器的负载慢慢提高,系统的压力越来越大,响应速度越来越慢,这个时候比较明显的是数据库和应用互相影响,应用出问题了,数据库也很容易出现问题,而数据库出问题的时候,应用也容易出问题。在服务器还没有超载的时候,我们应该提前做好准备,提升网站的负载能力。假如我们代码层面已难以优化,在不提高单台机器的性能的情况下,将应用和数据库从物理上分离,增加机器是一个不错的方式,不仅可以有效地提高系统的负载能力,而且性价比高。此时我们可以把数据库、web服务器拆分开来,这样不仅提高了单台机器的负载能力,也提高了容灾能力。
大型网站架构演变_第5张图片

随着用户量的增大、对带宽需求的增大、对CPU处理能力的增大;不足以融合所有用户的需求了,或者网站业务量的需求了。此处很少也不应该直接就对它做负载均衡式的扩展,因为数据同步很麻。所以我们架构扩展,不会上来就直接做负载均衡式的每一组组件当做一个整体来进行扩展,而是功能上切割 。这个时候技术上没有什么新的要求,但你发现确实起到效果了,系统又恢复到以前的响应速度了,并且支撑住了更高的流量,并且不会因为数据库和应用形成互相的影响。

2.3. 第三阶段【应用服务器负载均衡】

随着访问量继续增加,单台应用服务器已经无法满足需求了。假设数据库服务器没有压力,我们可以把应用服务器从一台变成了两台甚至多台,把用户的请求分散到不同的服务器中,从而提高负载能力。此时我们应该选择一款合适的负载均衡产品,一般来讲keepalived配合上ipvsadm做负载均衡,可谓是神器。这一阶段是需要掌握更多基础知识的关键节点。以下可以使用DNS解析对用户请求做负载均衡:
大型网站架构演变_第6张图片

需要注意:负载均衡产品的选择、负载均衡算法的选择、用户session保持的问题、应用占用资源的角度选择合理的服务器配置。
解决方案:根据以下技术特性选择适合业务的产品、技术。例如nginx+keepalived一台服务器、apache+tomcat一台服务器、mysql一台服务器。

三种负载均衡器的优缺点说明如下:摘自(互联网)
LVS的优点:
1、抗负载能力强、工作在第4层仅作分发之用,没有流量的产生,这个特点也决定了它在负载均衡软件里的性能最强的;无流量,同时保证了均衡器IO的性能不会受到大流量的影响;
2、工作稳定,自身有完整的双机热备方案,如LVS+Keepalived和LVS+Heartbeat;
3、应用范围比较广,可以对所有应用做负载均衡;
4、配置性比较低,这是一个缺点也是一个优点,因为没有可太多配置的东西,所以并不需要太多接触,大大减少了人为出错的几率;
LVS的缺点:
1、软件本身不支持正则处理,不能做动静分离,这就凸显了Nginx/HAProxy+Keepalived的优势。
2、如果网站应用比较庞大,LVS/DR+Keepalived就比较复杂了,特别是后面有Windows Server应用的机器,实施及配置还有维护过程就比较麻烦,相对而言,Nginx/HAProxy+Keepalived就简单多了。

Nginx的优点:
1、工作在OSI第7层,可以针对http应用做一些分流的策略。比如针对域名、目录结构。它的正则比HAProxy更为强大和灵活;
2、Nginx对网络的依赖非常小,理论上能ping通就就能进行负载功能,这个也是它的优势所在;
3、Nginx安装和配置比较简单,测试起来比较方便;
4、可以承担高的负载压力且稳定,一般能支撑超过几万次的并发量;
5、Nginx可以通过端口检测到服务器内部的故障,比如根据服务器处理网页返回的状态码、超时等等,并且会把返回错误的请求重新提交到另一个节点;
6、Nginx不仅仅是一款优秀的负载均衡器/反向代理软件,它同时也是功能强大的Web应用服务器。LNMP现在也是非常流行的web环境,大有和LAMP环境分庭抗礼之势,Nginx在处理静态页面、特别是抗高并发方面相对apache有优势;
7、Nginx现在作为Web反向加速缓存越来越成熟了,速度比传统的Squid服务器更快,有需求的朋友可以考虑用其作为反向代理加速器;
Nginx的缺点:
1、Nginx不支持url来检测。
2、Nginx仅能支持http和Email,这个它的弱势。
3、Nginx的Session的保持,Cookie的引导能力相对欠缺。

HAProxy的优点:
1、HAProxy是支持虚拟主机的,可以工作在4、7层(支持多网段);
2、能够补充Nginx的一些缺点比如Session的保持,Cookie的引导等工作;
3、支持url检测后端的服务器;
4、它跟LVS一样,本身仅仅就只是一款负载均衡软件;单纯从效率上来讲HAProxy更会比Nginx有更出色的负载均衡速度,在并发处理上也是优于Nginx的;
5、HAProxy可以对Mysql读进行负载均衡,对后端的MySQL节点进行检测和负载均衡,不过在后端的MySQL slaves数量超过10台时性能不如LVS;
6、HAProxy的算法较多,达到8种;

负载均衡的基础技术
1、http重定向。HTTP重定向就是应用层的请求转发,用户的请求到了HTTP重定向负载均衡服务器,根据算法要求用户重定向,浏览器自动重新请求实际服务器的IP地址。
优点:简单
缺点:性能较差

2、DNS域名解析负载均衡。DNS域名解析负载均衡就是在用户请求DNS服务器,获取域名对应的IP地址时,DNS服务器直接解析到负载均衡后的服务器IP。
优点:使用DNS省心,不用我们去维护。
缺点:使用DNS,它不具备故障检测功能,DNS解析结果会被缓存因此效果一般,DNS负载均衡多数为商业产品,其功能、管理权限有限。

3、反向代理服务器。反向代理服务器转发的请求在HTTP协议层面,因此也叫应用层负载均衡。用户的请求到达反向代理服务器,由反向代理服务器根据算法转发到具体的服务器。反向代理软件常用的有apache、nginx。
优点:部署简单。
缺点:反向代理服务器是所有请求和响应的分发服务器,其性能可能会成为瓶颈。特别是上传大文件。

4、IP层负载均衡。用户的请求到达负载均衡器后,通过修改请求的目的IP地址,实现负载均衡。代表实现LVS的NAT模式
优点:性能更好。
缺点:负载均衡器的宽带成为瓶颈。

5、数据链路层负载均衡。用户的请求到达负载均衡器后,通过修改请求的mac地址,实现负载均衡。与IP负载均衡不同当请求访问完服务器之后,直接返回客户而无需再经过负载均衡器。代表实现LVS的DR模式

常见的调度算法:摘自(互联网)
1、rr 轮询调度算法。顾名思义,轮询分发请求。
优点:实现简单
缺点:不考虑每台服务器的处理能力

2、wrr 加权调度算法。我们给每个服务器设置权值weight,负载均衡调度器根据权值调度服务器,服务器被调用的次数跟权值成正比。
优点:考虑了服务器处理能力的不同

3、sh 原地址散列:提取用户IP,根据散列函数得出一个key,再根据静态映射表,查处对应的value,即目标服务器IP。过目标机器超负荷,则返回空。

4、dh 目标地址散列:同上,只是现在提取的是目标地址的IP来做哈希。
优点:以上两种算法的都能实现同一个用户访问同一个服务器。

5、lc 最少连接。优先把请求转发给连接数少的服务器。
优点:使得集群中各个服务器的负载更加均匀。

6、wlc 加权最少连接。在lc的基础上,为每台服务器加上权值。算法为:(活动连接数*256+非活动连接数)÷权重 ,计算出来的值小的服务器优先被选择。
优点:可以根据服务器的能力分配请求。

7、sed 最短期望延迟。其实sed跟wlc类似,区别是不考虑非活动连接数。算法为:(活动连接数+1)*256÷权重,同样计算出来的值小的服务器优先被选择。

8、nq 永不排队。改进的sed算法。我们想一下什么情况下才能“永不排队”,那就是服务器的连接数为0的时候,那么假如有服务器连接数为0,均衡器直接把请求转发给它,无需经过sed的计算。

9、LBLC 基于局部性的最少连接。均衡器根据请求的目的IP地址,找出该IP地址最近被使用的服务器,把请求转发之,若该服务器超载,最采用最少连接数算法。

10、LBLCR 带复制的基于局部性的最少连接。均衡器根据请求的目的IP地址,找出该IP地址最近使用的“服务器组”,注意,并不是具体某个服务器,然后采用最少连接数从该组中挑出具体的某台服务器出来,把请求转发之。若该服务器超载,那么根据最少连接数算法,在集群的非本服务器组的服务器中,找出一台服务器出来,加入本服务器组,然后把请求转发之。

session会话保持
session sticky
ip based
cookie based
session replication
session server

1、Session Sticky。始终将同一个请求者的连接定向至同一个RS(第一次请求时仍由调度方法选择);没有容错能力,有损均衡效果。常见的算法有ip_hash法,即上面提到的两种散列算法。
优点:实现简单。
缺点:无高可用,反均衡。

2、Session Replication。在RS之间同步session,因此每个RS持集群中所有的session;对于大规模集群环境不适用。
优点:减轻负载均衡服务器的压力,可以随意调度。
缺点:带宽、内存占用量大,并发能力有限。

3、Session Server:利用单独部署的服务器来统一管理session; 实现了session和应用服务器的解耦。
优点:相比session replication的方案,集群间对于宽带和内存的压力减少了很多。
缺点:需要维护存储session的数据库。

4、Cookie Base:cookie base就是把session存在cookie中,由浏览器来告诉应用服务器用户的session是什么,同样实现了session和应用服务器的解耦。
优点:实现简单,基本免维护。
缺点:cookie长度限制,安全性低,宽带消耗。

应用从资源占用的角度分类
1、CPU Bound 【CPU密集型】一般指aap server
2、IO Bound 【IO密集型】一般指db server

有了以上理论经验,根据业务我们可以重新对架构演进为以下方案:
大型网站架构演变_第7张图片

2.4. 第四阶段【数据库读写分离】

前面几个阶段我们都是假设后端数据库负载没问题,但随着访问量的增大,数据库的负载也在慢慢增高。那么可能有人马上想到跟应用服务器一样,把数据库一分为二再负载均衡即可。但对于数据库来说,并没有那么简单。假如我们简单的把数据库一分为二,然后对于数据库的请求,分别负载到A机器和B机器,那么显然会造成两台数据库数据不一致的问题。那么对于这种情况,我们可以先考虑使用读写分离的方式。
大型网站架构演变_第8张图片

需要注意:引用MySQL主从势必会面临的问题,数据复制的问题、应用选择数据源的问题
解决方案:使用MySQL自带的master+slave的方式实现主从复制。采用第三方数据库中间件,例如mycat。mycat是从cobar发展而来的。mycat目前是国内比较好的mysql开源数据库分库分表中间件。

2.5. 第五阶段【引入搜索引擎实现全文搜索】

MySQL对于全局搜索能力支持不强,MySQL只对MyISAM引擎做全文索引但不支持事务,而MySQL对InnoDB引擎不支持全文索引。当一个站点到了这一个阶段其用户搜索量是非常大的。做为一个电商站点的交易达成有70%是通过搜索进行的,由此像这种越来越多的搜索需求使得我们数据库根本就没办法应付这种需要,因为每一次搜索都是一次全面查询,常常对模糊查找力不从心,即使做了读写分离,这个问题还未能解决。我们要想应付这种需求,一般只能自己构建搜索引擎来缓解读库的压力。

搜索引擎并不能替代数据库,他解决了某些场景下的“读”的问题,是否引入搜索引擎,需要综合考虑整个系统的需求。引入搜索引擎后的系统结构:
大型网站架构演变_第9张图片

2.6. 第六阶段【引入缓存】

1、页面缓存
web服务器压力较大时,如果让站点的一部分内容能缓存,一部分内容不能缓存,我们可以使用ESI动态缓存技术。web缓存服务器还可以对jpg、jpeg、gif、png、html、css、js格式的页面做缓存。例如:varnish, squid

2、数据缓存
随着访问量的增加,逐渐出现了许多用户访问同一部分内容的情况,对于这些比较热门的内容,没必要每次都从数据库读取,我们可以使用缓存技术。例如:memcached, redis

优点:减轻数据库的压力、大幅度提高访问速度
缺点:需要维护缓存服务器、提高了编码的复杂性

缓存服务器集群的调度算法不同与上面提到的应用服务器和数据库,最好采用“一致性哈希算法”。
大型网站架构演变_第10张图片

2.7. 第七阶段【数据库拆分】

当访问量逐渐增大,单一应用增加机器带来的效果不明显,需要考结合业务考虑数据库拆分,来提升系统运行效率。我们的网站演进到现在,交易、商品、用户的数据都还在同一个数据库中。尽管采取了增加缓存,读写分离的方式,但随着数据库的压力继续增加,数据库的瓶颈越来越突出,此时主库写操作压力只能做数据库拆分,我们可以有垂直拆分和水平拆分两种选择。

1、垂直拆分:把数据库中不同的业务的数据拆分到不同的数据库服务器中。
大型网站架构演变_第11张图片

2、水平拆分:把一个单独的表中的数据拆分到多个不同的数据库服务器上。
大型网站架构演变_第12张图片

2.8. 第八阶段【应用拆分】

随着业务的发展,业务越来越多,应用越来越大。我们需要考虑如何避免让应用越来越臃肿。这就需要把应用拆开,从一个应用变为俩个甚至更多。还是以我们上面的例子,我们可以把用户、商品、交易拆分开。变成“用户、商品”和“用户,交易”两个子系统。
大型网站架构演变_第13张图片

需要注意:这样拆分后,可能会有一些相同的代码,如用户相关的代码,商品和交易都需要用户信息,所以在两个系统中都保留差不多的操作用户信息的代码。如何保证这些代码可以复用是一个需要解决的问题。
解决方案:通过走服务化的路线来解决,把公共的服务拆分出来,形成一种服务化的模式,简称SOA。

服务化之后的系统结构:
大型网站架构演变_第14张图片

需要注意:如何进行远程的服务调用
解决方案:我们可以通过引入消息中间件来解决

2.9. 第九阶段【引入消息中间件】

随着网站的继续发展,我们的系统中可能出现不同语言开发的子模块和部署在不同平台的子系统。此时我们需要一个平台来传递可靠的,与平台和语言无关的数据,并且能够把负载均衡透明化,能在调用过程中收集调用数据并分析之,推测出网站的访问增长率等等一系列需求,对于网站应该如何成长做出预测。开源消息中间件有阿里的dubbo,可以搭配Google开源的分布式程序协调服务zookeeper实现服务器的注册与发现。

引入消息中间件后的结构:
大型网站架构演变_第15张图片

3、高性能设计

三层架构(3-tier architecture) 通常意义上的三层架构就是将整个业务应用划分为:界面层(User Interface layer)、业务逻辑层(Business Logic Layer)、数据访问层(Data access layer)。区分层次的目的即为了“高内聚低耦合”的思想。在软件体系架构设计中,分层式结构是最常见,也是最重要的一种结构。微软推荐的分层式结构一般分为三层,从下至上分别为:数据访问层、业务逻辑层(又或称为领域层)、表示层。(百度百科)

大型网站架构演变_第16张图片

微服务的特点:
单一职责:微服务中每一个服务都对应唯一的业务能力,做到单一职责。
微:微服务的服务拆分粒度很小,例如一个用户管理就可以作为一个服务。每个服务虽小,但“五脏俱全”。
面向服务:面向服务是说每个服务都要对外暴露服务接口API。并不关心服务的技术实现,做到与平台和语言无关,也不限定用什么技术实现,只要提供Rest的接口即可。
自治:自治是说服务间互相独立,互不干扰
团队独立:每个服务都是一个独立的开发团队,人数不能过多。
技术独立:因为是面向服务,提供Rest接口,使用什么技术没有别人干涉
前后端分离:采用前后端分离开发,提供统一Rest接口,后端不用再为PC、移动段开发不同接口
数据库分离:每个服务都使用自己的数据源
部署独立,服务间虽然有调用,但要做到服务重启不影响其它服务。有利于持续集成和持续交付。每个服务都是独立的组件,可复用,可替换,降低耦合,易维护。
大型网站架构演变_第17张图片
大型网站架构演变_第18张图片

架构优化:
• 以用户为中心,提供快速的网页访问体验。主要参数有较短的响应时间,较大的并发处理能力,较高的吞吐量,稳定的性能参数;
• 可分为前端优化,应用层优化,代码层优化,存储层优化;
• 前端优化:网站业务逻辑之前的部分;
• 浏览器优化:减少Http请求数,使用浏览器缓存,启用压缩,Css Js位置,Js异步,减少Cookie传输;
• CDN加速,反向代理;
• 应用层优化:处理网站业务的服务器。使用缓存,异步,集群;
• 代码优化:合理的架构,多线程,资源复用(对象池,线程池等),良好的数据结构,JVM调优,单例,Cache等;
• 存储优化:缓存,固态硬盘,光纤传输,优化读写,磁盘冗余,分布式存储(HDFS),NOSQL等。