概述
如何打造一个高性能、高可用、易扩展、可伸缩且安全的网站。
如何让网站随应用所需灵活变动。
随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。
架构
单一应用架构
当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。
当用户访问量提高时。
垂直应用架构
当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。
分布式服务架构
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。
流动计算架构
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。
常见问题
雪崩
由于一个节点的原因导致资源一直被占用,如果这部分资源没有被释放,就会导致其他节点的崩溃。
大型网站软件系统的特点
对大型网站而言,访问量于数据量缺一不可。
与传统企业应用系统相比,大型网站软件系统的特点有:
- 高并发、大流量。
- 需要面对高并发用户,大流量访问。
- 高可用。
- 系统7*24小时不间断服务,大型网站的宕机事件会成为新闻焦点。
- 海量数据。
- 需要存储、管理海量数据,需要使用大量服务器。
- 用户分布广泛,网络情况复杂。
- 需要为全球用户提供服务,各地网络情况千差万别。
- 国内,各个运营商网络互通难的问题。
- 中美光缆的数次故障,需要一些对国外用户依赖大的网站考虑建立海外数据中心。
- 安全环境恶劣。
- 互联网的开放型,容易受到攻击。
- 需求快速变更,发布频繁。
- 渐进式发展。
- 从一个小网站渐进的发展过来。
大型网站架构演化发展历程
任何简单的业务一旦需要处理数以P计的数据和面对数以亿计的用户,问题就会很棘手。大型网站架构主要解决这类问题。
初始阶段的网站架构
- 一台服务器即可。
- 应用程序、数据库、文件等所有资源都在一台服务器上。
应用服务与数据服务分离
问题
- 一台服务逐渐不能满足需求,越来越多的用户导致性能越来越差,越来越多的数据导致存储空间不足。
解决方案:将数据和应用分离:
三台服务器:
- 应用服务器:需要处理大量业务逻辑,需要更强CPU。
- 数据库服务器:快速磁盘检索和数据缓存,因此需要更快的磁盘和更大的内存。
- 文件服务器:存储大量用户上传的文件,需要更大的硬盘。
不同的服务器承担不同的服务角色,网站的并发处理能力、数据存储空间得到很大改善。
使用缓存改善网站性能
问题
- 数据库压力太大导致访问延迟,进而影响整个系统的性能。
解决方案:
- 网站的访问特点:二八定律,80%的业务访问集中在20%的数据上。
- 将频繁访问的数据缓存在内存当中。
缓存分类:
- 本地缓存,缓存在应用服务器上。
- 速度更快。
- 受内存限制,并与应用程序争夺内存。
- 远程缓存,缓存在专门的分布式缓存服务器上。
- 理论上不受内存限制。
使用应用服务器集群改善网站的并发处理能力
问题
- 单一应用服务器能够处理的请求连接有限,在网站访问高峰期,应用服务器成为瓶颈。
解决方案
增加服务器分担压力,并可以以此方式不断提升系统性能。实现系统的可伸缩性。
负载均衡调度服务器:将来自用户的访问请求分发到应用服务器集群的任何一台服务器中,若有更多的用户,则在集群中增加更多的服务器,使应用服务器的负载压力不再成为整个网站的瓶颈。
解决集群后的Session问题
当使用集群后,当第一次访问在服务器A上,则Session就建立在了服务器A上,如果不做处理,则无法保证接下来的请求每次都落在服务器A上,则存在Session问题,会话数据无法保存在单机上。
解决方案有:
- Session Sticky。较好。
- Session Replication。
- Session数据集中存储。较好。
- Cookie Based。
Session Sticky
如果保证同一个会话的请求都在同一个Web服务器上处理,那么对这个会话的个体来说,与之前单机的情况是一样的。则需要负载均衡器能够根据每次请求的会话的标识来进行请求转发。
该方案只是在负载均衡器上做处理,使得同样Session的请求每次都发送到同一个服务器端处理,非常利于针对Session进行服务器端本地的缓存。不过存在以下问题:
- 如果有一台Web服务器宕机或重启,那么这台机器上的会话数据会丢失,此时用户需要重新登录了。
- 会话标识是应用层的信息,那么负载均衡器要将同一个会话的请求都保存到同一个Web服务器上的话,就需要进行应用层的解析,开销大。
- 负载均衡器变为了一个有状态的节点,要将会话保存到具体Web服务器的映射,与无状态节点相比,内存消耗会更大,容灾方面会更麻烦。
举例讲即会话数据是我们的碗筷,要保证每次吃饭都用自己的碗筷,就讲碗筷存在某一家,每次都去这家吃。
Session Replication
如果我们在每个店都存放一套自己的餐具,就可以更自由地选择饭店了。
由Web服务器间保证不同服务器间的Session数据的一致。一般的应用容器都支持该方式。
存在的问题:
- 同步Session数据带来了网络带宽的开销,只要Session数据有变化,就需要将数据同步到所有其他机器上,机器数越多,同步带来的网络带宽开销就越大。
- 每台Web服务器都要保存所有的Session数据,如果整个集群的Session数很大的话,则占用大量的空间。
即不适合集群机器数很大的场景。
Session数据集中存储
将Session数据集中存储起来,不同的Web服务器从同样的地方来获取Session。
存在的问题:
- 读写Session引入了网络操作,相对于本机的数据读取来说,问题在于时延和不稳定性,但一般内网问题很小。
- 如果集中存储的Session的机器或集群存在问题就会影响应用。
在Web服务器数量较大时,优势很明显。
Cookie Based
该方案通过Cookie传递Session数据,我们将Session数据存放于Cookie中,在Web服务器上从Cookie生成Session数据,就好比每次将自己的碗筷都带在身上。
存在的问题:
- Cookie长度的限制。
- 安全性。
- 带宽消耗。
- 性能影响。
数据库读写分离
问题:
- 数据库负载压力过高
- 即使使用缓存后,绝大部分数据访问都可以不通过数据库就能完成
- 但依然有一部分读操作(缓存访问不命中、缓存过期)和全部的写操作需要访问数据库。会导致数据库负载压力过高
解决方案:
配置数据库的主从关系,将一台数据库服务器上的数据更新同步到另一台服务器上,实现读写分离。
应用服务器在写数据时,访问主数据库,主数据库通过主从复制将数据更新同步到从数据库,当应用服务器读数据时,就可以通过从数据库获得数据。为了便于应用程序访问读写分离后的数据库,通常使用专门的数据访问模块,使数据库读写分离对应用透明
使用反向代理和CDN加速网站响应
问题:
- 复杂的网络环境,导致不同地区的用户访问网站速度差别极大
解决方案:
- 加快网络响应速度,提高用户体验
- 反向代理与CDN加速
- 原理为:利用缓存
- CDN:部署在网络提供商的机房,使得用户在请求网站服务时,可以从距离自己最近的网络提供方机房获得数据
- 反向代理:部署在网站的中心机房,当用户请求到达中心时,首先访问反向代理服务器,如果反向代理服务器缓存着用户请求的数据,就直接返回给用户
- 通过缓存数据,也减轻了后端服务器的负载压力
使用分布式存储系统和分布式数据库系统
问题:
- 任何强大的单一服务器都无法满足持续增长的业务需求。
- 数据库服务器与文件服务器无法支持。
解决方案:
- 分布式数据库:
- 分布式数据库是网站数据库拆分的最后手段,只有在单表数据规模非常庞大时才使用。
- 常见手段为业务分库,不同业务的数据库部署在不同服务器上。
- 分布式存储系统:
- 包含分布式文件系统、分布式Key-Value系统和分布式数据库。
- 分布式文件系统解决小文件和大文件的存储问题,分布式Key-Value提供高性能的半结构化的支持,分布式数据库提供一个支持大数据、高并发的数据库系统。
- 通过集群提供高容量、高并发访问、数据冗余容灾的支持。
使用NoSQL和搜索引擎
问题:
- 网站业务复杂,对数据存储和检索的需求越来越复杂,需要采用一些非关系数据库(NoSQL)以及非数据库查询技术(搜索)。
解决方案:
- NoSQL与搜索引擎都是源自互联网的技术手段,对于可伸缩的分布式特性有更好地支持。
- 搜索引擎可作为一个读库,降低数据库压力。
- 应用服务器通过一个统一的数据访问模块访问各种数据,减轻应用程序管理诸多数据源的麻烦。
分库分表
问题:
数据库的压力依然在不断上升。
垂直拆分:
- 分而治之,将整个网站业务分为不同的产品线。将不同的表拆分到不同的数据库。
- 将一个网站拆分为许多不同的应用,每个应用独立部署维护。
- 应用间通过超链接建立关系。
- 也可以通过消息队列进行数据分发。
- 最多的是通过访问同一个数据存储系统来构成一个关联的完整系统。
水平拆分:
水平拆分即将同一个表的数据拆分到两个数据库中,其原因是某个业务的数据量或更新量到达的单个数据库的瓶颈。
即使一个订单表,也可以拆分为家电订单、图书订单。
也可以依据时间拆分,即2018年的订单,2019年的订单。
此时自增字段等不可用。
分布式服务
问题
- 随着业务拆分越来越小,存储系统越来越大,应用系统的复杂性指数级增大,部署维护越来越困难。
- 所有应用都要与数据库系统相连接,数万台服务器中,连接的数目是服务器^2,导致数据库资源不足,拒绝服务。
解决方案
- 将每一个应用系统需要执行的相同业务操作提取出来独立部署。
- 由这些可复用的业务连接数据库,提供公共的业务服务。
- 应用系统只需要管理用户界面,通过分布式服务调用共用业务服务完成具体业务操作。
云计算平台
当大型网站架构解决了海量数据管理和高并发事务的处理,就可以将这些解决方案应用到网站自身以外的业务上,即建设云计算平台,将计算作为一种基础资源出售。
总结
大型网站架构演化的价值观
网站的价值在于它能够为用户提供什么样的价值,在于能做什么,而不是怎么做的。
因此在小网站上追求架构是舍本逐末的,需要做的就是为用户提供更好的服务创造价值。
核心价值观:随网站所需灵活应对
核心价值是随着小型网站业务的逐步发展,慢慢演化为一个大型网站。
驱动大型网站技术发展的主要力量:网站的业务发展
创新的业务发展模式对网站架构逐步提出了更高的要求,才使得创新的网站架构得以发展成熟。
业务成就了技术,后努力提高技术回馈业务。
网站架构设计的误区
一味追求大公司的解决方案
为了技术而技术
网站技术是为业务而存在的,除此毫无意义。
在技术选型和架构设计中,脱离网站业务发展的实际,一味追求技术,会使得架构之路越走越难。
企图用技术解决所有问题
12306的故障问题:
- 真正的问题是在于业务架构。
- 在几亿中国人一票难求的情况下进行窗口售票模式在网上售票(0点后出售若干天后的车票)。
- 是一种促销秒杀。
- 进行分时段售票、排队机制等,控制并发访问的量。
技术是解决业务问题,业务手段也可以解决业务问题。
没有一劳永逸的解决方案,不当的解决方案只会带来更棘手的问题,真正的方案是在对问题的深刻理解上的创造和创建。
大型网站架构模式
高并发下的方案选择需要依靠业务场景,进行方案的组合。
跨越JVM的一致性问题
强一致性:指分布式事务。
最终一致性:通过补偿和记录的方式实现,在做所有不确定的事情前,先将事情记录下来,再去做不确定的事情。其结果有:
- 成功:清除记录。
- 失败:依靠定时任务补偿性不断重试,直到成功。并需要做好幂等。
- 不确定:视为失败。
分层
分层是最常见的一种架构模式,将系统在横向维度上切分成几个部分,每个部分负责一部分相对比较单一的职责,然后通过上层对下层的依赖和调用组成一个完整的系统。
应用层 | 负责具体业务和视图展示,如网站首页以及搜索输入和结果展示 |
---|---|
服务层 | 为应用层提供服务支持,如用户管理服务、购物车服务等 |
数据层 | 提供数据存储访问服务,如数据库、缓存、文件等 |
各层之间具有一定的独立性,只要维持调用接口不变,各层可以根据具体问题独立发展演化,而不需要其它层做出调整。
分布式
分层与分割的一个主要目的是为了切分后的模块便于分布式部署,即将不同模块部署在不同的服务器上,通过远程协同工作。但是分布式带来了其他的问题:
- 分布式意味着服务调用必须通过网络,这可能会对性能造成比较严重的影响。
- 服务器越多,服务宕机的概率也就越大,一台服务器宕机可能导致很多应用不可访问,使得网站的可用性降低。
- 数据在分布式环境下保持数据一致性也比较困难,分布式事务也难以保证,对网站业务正确性和业务流程有可能造成影响。
- 分布式导致网络依赖错综复杂,开发管理维护困难。
常见分布式应用方案:
- 分布式应用和服务。
- 分布式静态资源。
- 分布式数据和存储。
- 分布式计算。
- 分布式配置、分布式锁、分布式文件系统等。
集群
对于用户访问集中的模块,还需要将独立部署的服务器集群化,即多台服务器部署相同应用构成一个集群,通过负载均衡设备共同对外提供服务。
扩容
占用内存大小取决于工作内存内变量的多少与大小。
单个线程占用的内存不会很大,但是很多线程占用内存就会很多。
- 垂直扩容:提高系统部件能力(加内存)。
- 内存的价格,以及当前的内存性能瓶颈等问题。
- 水平扩容:增加更多系统成员来实现(加服务器),即服务器集群。
- 共享的资源与系统间的协调性是一种约束条件。例如网络IO、数据库资源。
扩容-数据库
读操作扩展:
- 垂直扩容。
- memcache。
- redis。
- CDN等缓存。
写操作扩展:
- 水平扩容。而垂直扩容存在硬盘与CPU的平衡,因此不如水平扩容有效。
- Cassandra。
- Hbase等。
缓存
缓存可以使用在APP端、网络转发端、服务端、存储端等位置,而不仅仅是在服务端。现代CPU越来越快的一个重要因素就是使用了更多的缓存。
- CDN。内容分发网络,部署在距离终端用户最近的网络服务商,用户的网络请求总是先到达他的网络服务商那里,在这里缓存网站的一些静态资源或变化较少的数据,就可以以最快的速度返回给用户。
- 直播、门户网站、新闻APP。
- 反向代理。反向代理属于网站前端架构的一部分,部署在网站的前端,当用户请求到达网站的数据中心时,最先访问到的就是反向代理服务器,这里缓存网站的静态资源。
- 本地缓存。在应用服务器本地缓存着热点数据,应用程序可以在本机内存中直接访问数据,而无需访问数据库。
- 分布式缓存。大型网站的数据量非常大,即使只缓存一小部分,需要的内存空间也不是单机能够承受的。
使用缓存有两个前提条件:
- 数据访问热点不均衡,某些数据会被更频繁的访问,这些数据应该放在缓存中。
- 数据在某个时间段内有效,不会很快过期,否则缓存的数据就会因已经失效而产生脏读。
消息队列
开始流程A->发送消息A1到队列->消息A1被处理->处理消息A1。
特性
- 业务无关,只做消息分发。
- FIFO,先投递先到达。
- 容灾,节点的动态增删和消息的持久化。
- 性能:吞吐量提示,系统内部通信效率提高。
为什么需要
生产和消费的速度或稳定性等因素不一致。
优势
- 业务解耦。
- 最终一致性。两个系统的状态保持一致,其存在一个时间限制。
- A成功,B失败
- A成功,B成功,但是没有获得B成功的消息,网络异常。
- A成功,B失败,A想回滚,但是此时A宕机了。
- 广播。如果没有消息队列,每有一个新的接口都需要联调一次,而消息队列只需要将其投放到消息队列即可。
- 错峰、流控。将短板效应进行一定的弥补,针对延迟不敏感的服务可以实现错峰。
举例
- Kafka。
- RabbitMQ。
应用拆分
从整体将一个应用拆分为多个应用。
原则
- 业务优先。业务存在边界,根据业务的不同进行拆分。
- 循序渐进。拆分与测试缺一不可,保证系统测试与功能要完整。
- 兼顾重构与分层。不能为了分布式而分布式,分层进行提高代码的可重用性。
- 可靠测试。
思考
- 应用间通信:RPC(dubbo)、消息队列。
- RPC框架实时性更高;消息队列传输数据包小,但数据量大,对实时性要求不是很高。
- 应用间数据库设计,每个应用都有独立的数据库。
- 避免事务操作跨应用。分布式事务非常消耗资源。
限流
限流:限制一段时间内某段代码执行的频率。
如果直接将大量数据直接插入到master库,则很可能导致master库崩溃。并且master库还要同步到slave库,slave库的延迟也会很大,导致slave的查询也会错误。
如果使用限流,则可以以恒定速率写入到master库,并以正常同步的速度同步到slave库。
限流算法
计数器法:
设置一个计数器,对于A接口,每当一个请求到达,Counter++,如果在时间到达1分钟,则重置Counter。
但是存在临界问题:在重置节点附近瞬间发出大量请求。
滑动窗口:
每过一定时间窗口向右滑动一格,每一格都有对应的Counter。窗口越多越平滑。
漏桶:
请求到达之后存放在一个桶当中,桶中的请求以一个恒定的速率做处理。
令牌桶:
一个固定容量的令牌桶。
- 系统的守护线程一直在向令牌桶内部丢令牌。
- 当请求到达时,桶内存在令牌,则执行业务。如果没有令牌则根据处理策略处理请求,但一定不会让你执行。
对业务峰值有一定承担能力。
服务降级与熔断
服务降级
流量对一些服务和页面做有策略的降级,以此缓解保证部分或大部分的客户得到正确的响应。
如果当前服务处理不了或请求出错了,则给出一个默认的返回。
服务降级可以根据不同的接口做不同的或默认的定级。
分类
- 自动降级:超时、失败次数、故障、限流。
- 超时:超时时间和超时次数。定时用异步机制探测回复情况。
- 失败:不稳定的API一定的失败次数。使用异步机制探测回复情况。
- 故障:调用的远程服务挂掉了,可能是DNS、网络故障等。
- 限流。限流后后续的服务会自动降级。
- 人工降级:
- 秒杀、双11等。
考虑:
- 核心服务、非核心服务。
- 是否支持降级,降级策略。
- 业务放通场景,策略。
服务熔断
当服务过载,为防止整个系统崩溃,则停止额外的服务。
服务降级与服务熔断
- 共性:
- 目的:从可用性、可靠性着想,防止系统整体缓慢或崩溃。
- 最终表现:某些用户感觉服务不可达或不可用。
- 粒度:一般是服务级别。
- 自治性:要求自动处理。
- 区别:
- 降级:从整体负荷考虑而引起。降级有层级,一般从最外围开始。
- 熔断:某个服务或下游服务故障而引起。熔断是框架级处理,每个服务都需要。
- 实现不同。
数据库分库分表
数据库瓶颈:
- 单个库数据量太大(1-2T):多个库。
- 单个数据库服务器压力太大、读写瓶颈:多个库。
- 单表数据量过大:分表。
切库:
- 实际应用:读写分离。
分表:
- 什么时候考虑分表:
- 当表数据量很大,即使SQL和索引优化后,操作时延还是影响使用。
高可用手段
高可用的手段是数据和服务的冗余备份及失效转移,一旦某些服务器宕机,就将服务切换到其他可用的服务器上。
- 任务调度系统分布式:elastic-job + zookeeper。
- 主备切换:apache curator + zookeeper分布式锁实现。
- 监控报警机制。
总结
- 利用云计算资源,便可以对所需要的一切技术资源:计算、存储、网络都可以按需购买,线性伸缩。
- 因此亲身经历架构演化之路的人会越来越少。架构师更应该对这个过程深刻了解,理解成熟的网站架构技术方案的来龙去脉和历史渊源,才能在技术选型和架构决策时有的放矢,直击要害。