网站架构学习(六)之网站的伸缩性架构
前言
所谓网站的伸缩性是指不需要改变网站的软硬件设计,仅仅通过改变部署的服务器数量就可以扩大或者缩小网站的服务处理能力。
每一个大型网站都是从小型网站演化而来的。在这个渐进式的演化过程中,最重要的技术手段就是使用服务器集群,通过不断地向集群中添加服务器来增强整个集群的处理能力。这就是网站系统的伸缩性架构,只要技术上能做到向集群中加入服务器的数量和集群的处理能力成线性关系,那么就可以以此手段提升自己的规模。
网站架构的伸缩性设计
网站架构发展史就是一部不断向网站添加服务器的历史。只要工程师能向网站的服务器集群中添加新的机器,只要新添加的服务器能线性提高网站的整体服务处理能力,网站就无需为不断增长的用户和访问而焦虑。
网站伸缩性分为两类,第一类是根据功能进行物理分离实现伸缩,第二类是单一功能通过集群实现伸缩。前者是不同的服务器部署不同的服务,提供不同的功能;后者是集群内的多台服务器部署相同的服务,提供相同的功能。
不同功能进行物理分离实现伸缩
每次分离都会有更多的服务器加入网站,使用新增的服务器处理某种特定服务。
纵向分离(分层后分离):将业务处理流程上的不同部分分离部署,实现系统伸缩性。
横向分离(业务分割后分离):按不同的业务模块分离部署,实现系统伸缩性
横向分离的粒度可以非常小,甚至可以一个关键网页部署一个独立服务,比如对于电商网站非常重要的产品详细页面,商铺页面,搜索列表页面,每个页面都可以独立部署,专门维护。
单一功能通过集群规模实现伸缩
当单一服务器不能满足业务规模的要求,需要使用服务器集群,即将相同服务部署在多态服务器上构成一个集群整体对外提供服务。
以搜索服务器为例,如果一台服务器可提供每秒1000次的请求服务,即QPS(Query Per Second)为1000。那么如果网站高峰时每秒搜索访问量为10000,就需要部署10台服务器构成集群。
以缓存服务器为例,如果每台服务器可缓存40GB数据,那么要缓存100GB数据,需要部署3台服务器构成一个集群。
集群伸缩性可分为应用服务器集群伸缩性和数据服务器集群伸缩性。技术实现和对数据状态的管理都不同。 数据服务器集群也可分为缓存数据服务器集群和存储数据服务器集群,这两种集群的伸缩性设计也不大相同。
应用服务器集群的伸缩性设计
应用服务器应该设计成无状态的,即应用服务器不存储请求上下文信息,如果将部署有相同应用的服务器组成一个集群,每次用户请求都可以发送到集群中任意一台服务器上去处理,任何一台服务器处理结果相同。
如果HTTP请求分发装置可以感知或者可以配置集群的服务器数量,可以及时发现集群中新上线或下线的服务器,并能向新上线的服务器分发请求,停止向已下线的服务器分发请求,那么实现了应用服务器集群的伸缩性。
HTTP请求分发装置被成为负载均衡服务器
负载均衡的几种方法
HTTP重定向负载均衡
HTTP重定向服务器是一台普通的应用服务器,其唯一功能是根据用户的HTTP请求计算一台真实的Web服务器地址,并将该web服务器地址写入HTTP重定向响应中(响应码为302)返回给用户浏览器。
浏览器访问域名,然后DNS解析将IP地址返回(HTTP重定向服务器ip地址),然后浏览器根据ip地址访问HTTP重定向负载均衡服务器后,服务器根据某种负载均衡算法计算获得一台实际物理服务器的地址,构造一个包含该实际物理服务器地址,完成访问。
优点:简单。
缺点:浏览器需要两次请求服务器才能完成一次访问,性能差;重定向服务器自身的处理能力有可能成为瓶颈,整个集群的伸缩性规模有限;使用HTTP302响应码重定向,有可能使搜索引擎判断为SEO作弊,降低搜索排名。
DNS域名解析负载均衡
在DNS服务器中配置多个A记录,如:www.mysite.com IN A 114.100.81.1、www.mysite.com IN A 114.100.80.2。每次域名解析请求都会根据负载均衡算法计算一个不同的IP地址返回。
优点:将负载均衡工作交给DNS,神雕了网站管理维护负载均衡服务的麻烦,同时DNS还支持基于地理位置的域名解析,即会将域名解析成距离用户地理最近的一个服务器地址。
缺点:目前的DNS是多级解析,每一级DNS都可能缓存A记录,当下线某台服务器,即修改A记录,使其生效时间较长,可能导致用户访问失败。
大型网站部分使用DNS域名解析,利用域名解析作为第一级负载均衡手段,即域名解析得到的一组服务器并不是实际提供web服务的物理服务器,得到负载均衡服务器,然后再进行负载均衡算法,得到真正的web服务器
反向代理负载均衡
反向代理服务器处于Web服务器前面(可缓存Web响应,加速访问),大多数反向代理服务器同时提供负载均衡的功能,管理一组Web服务器,将请求根据负载均衡算法转发到不同Web服务器上。Web服务器处理完成的响应也需要通过反向代理服务器返回给用户。由于Web服务器不直接对外提供访问,因此Web服务器不需要使用外部IP地址,而反向代理服务器则需要配置双网卡和内部外部两套IP地址。
由于反向代理服务器转发请求在HTTP协议层面,因此也叫应用层负载均衡。
优点:是和反向带来服务器功能集成在一起,部署简单。
缺点:反向代理服务器是所有请求和响应的中转站,其性能可能会成为瓶颈。
IP负载均衡
在网络层通过修改请求目标地址进行负载均衡。
用户数据包到达负载均衡服务器后,负载均衡服务器在操作系统内核进程获得网络数据包,根据负载均衡算法计算得到一台真实的Web服务器,然后将数据目的IP地址改为Web服务器地址,不需要通过用户进程处理。真实Web服务器处理完成后,响应数据包回到负载均衡服务器,负载均衡服务器再把数据包源地址改为自身的IP地址,发送给用户浏览器。
这里的关键在于真实物理Web服务器响应数据包如何返回给负载均衡服务器。
第一种方案:负载均衡服务器在修改目的IP地址的同时修改源地址,将数据包源地址设为自身IP(即源地址转换 SNAT)
第二种方案:负载均衡服务器同时作为真实物理服务器集群的网关服务器,这样所有响应数据都会达负载均衡服务器。
IP负载均衡是在内核进程完成数据分发,较反向代理服务器有更好的处理性能。但是由于所有请求都需要经过负载均衡服务器,集群的最大响应数据吞吐量不得不受制于负载均衡服务器网卡带宽。对于提供下载服务或者视频服务等需要传输大量数据的网站,难以满足需求。
数据链路层负载均衡
数据链路层负载均衡是指在通信协议的数据链路层修改mac地址进行负载均衡。
这种数据传输方式称为三角传输模式。负载均衡数据分发过程中不修改IP地址,只修改目的mac地址,通过配置真是物理服务器集群所有机器虚拟机IP和负载均衡服务器IP地址一致,从而达到不修改数据包的源地址和目的地址就可以进行数据分发的目的。由于实际处理请求的真是物理服务器IP和数据请求目的IP一致,不需要通过负载均衡服务器进行地址转换,可将响应数据包直接返回给用户浏览器,避免负载均衡服务器网卡带宽成瓶颈。(这种负载方式又称作直接路由方式DR)。
简述上图,用户请求到达负载均衡服务器(114.100.20.200),负载均衡服务器将请求数据的MAC地址改为(00:1e:ec:bc:5e:03),不修改数据包的目的IP地址,由于Web服务器集群所有虚拟IP地址和负载均衡服务器IP地址相同,因此数据可以正常传输到MAC地址为(00:1e:ec:bc:5e:03)对应的服务器,该服务器处理完成后发送响应数据到网站的网关服务器,网关服务器直接将该数据包发送到用户浏览器,响应数据不需要通过负载均衡服务器。
Linux平台最好的链路层负载均衡开源产品是LVS(Linux Virtual Server)。
负载均衡算法
实现负载均衡服务器分层两部分:
1.根据负载均衡算法和Web服务器列表计算得到集群中一台Web服务器的地址。
2.将请求数据发送到该地址对应的Web服务器上。
负载均衡算法种类
1.轮询
所有请求被依次分发到每台服务器上,即每台服务器需要处理的请求数目都相同,适合于所有服务器硬件都相同的场景。
2.加权轮询
根据应用服务器硬件性能的情况,在轮询的基础上,按照配置的权重将请求分发到每个服务器,高性能的服务器能分配更多请求。
3.随机
请求被随机分配到各个应用服务器,在许多场合下,这种方案都很简单实用。好的随机数本身就很均衡。即使应用服务器硬件配置不同,也可以使用加权随机算法。
4.最少连接
记录每个应用服务器正在处理的连接数(请求数),将信道的请求分发到最少连接的服务器上。
5.源地址散列
根据请求来源的IP地址进行Hash计算,得到应用服务器,这样来自同一个IP地址的请求总在同一个服务器上处理,该请求的上下文信息可以存储在这台服务器上,在一个会话周期内重复使用,从而实现会话粘滞。
分布式缓存集群的伸缩性设计
分布式缓存服务器集群中不同服务器中缓存的数据各不相同,缓存访问请求不可以在缓存服务器集群中的任意一台处理,必须先找到缓存有需要数据的服务器,然后才能访问。这个特点严重制约分布式缓存集群的伸缩性设计,因为新上线的缓存服务器没有缓存任何数据,而已下线的缓存服务器还缓存着网站的许多热点数据。
必须让新上线的缓存服务器对整个分布式缓存集群影响最小,也就是说新加入缓存服务器后应使整个缓存服务器集群中已经缓存的数据尽可能还被访问到。
Memcached分布式缓存集群的访问模型
应用程序通过Memcached客户端访问Memcached服务器集群,Memcached客户端主要由一组API、Memcached服务器集群路由算法、Memcached服务器集群列表及通信模块构成。
用了别人的图,
其中路由算法负责根据应用程序输入的缓存数据KEY计算得到应该将数据写入到Memcached的哪台服务器(写缓存)或者应该从那台服务器读数据(读缓存)。
读缓存和写缓存一样,由于使用同样的路由算法和服务器列表,只要应用程序提供相同的KEY,Memcached客户端总是访问相同的服务器(Node1)去读取数据。只要服务器还缓存着该数据,就能保证缓存命中。
Memcached分布式缓存集群的伸缩性挑战
简单的路由算法使用余数Hash:用服务器数目除以缓存数据KEY的Hash值,余数为服务器列表下标编号。但当,分布式缓存集群需要扩容时,就很麻烦。
一种解决办法是在网站访问量最少的时候扩容缓存服务器集群,这时对数据库的负载冲击最小。然后通过模拟请求的方法逐渐预热缓存,使缓存服务器中的数据重新分布。
第二种一致性Hash算法。
分布式缓存的一致性Hash算法
一致性Hash算法通过一个叫做一致性Hash环的数据结构实现Key到缓存服务器的Hash映射。
具体算法过程:先构造一个长度0~2的23次方的整数环,根据节点名称的Hash值(分布范围同样是0~2的23次方)将缓存服务器节点放置在这个Hash环上。然后根据需要缓存的数据的KEY值计算得到其Hash值,然后再Hash环上顺时针查找距离这个KEY的Hash值最近的缓存服务器节点,完成KEY到服务器的Hash映射查找。
解决上述一致性Hash算法带来的负载不均衡问题,也可以通过使用虚拟层的手段:将每台物理缓存服务器虚拟为一组虚拟缓存服务器,将虚拟服务器的Hash值放在Hash环上,KEY在环上先找到虚拟服务器节点,再得到物理服务器的信息。
这样新加入物理服务器节点时,是将一组虚拟节点加入环中,如果虚拟节点的数目足够多,这组虚拟节点将会影响同样多数目的已经在环上存在的虚拟节点,这些已经存在的虚拟节点有对应不同的物理节点。最终结果是:新加入一台缓存服务器,将会较均匀地影响原来集群中已经存在的所有服务器,也就是说分摊原来缓存服务器集群中所有服务器的一小部分负载。
每个物理节点对应的虚拟节点越多,各个物理节点之间的负载越均衡。(一般是150)
数据存储服务器集群的伸缩性设计
数据存储服务器集群的伸缩性对数据的持久性和可用性提出了更高的要求。
缓存的目的是加速数据读取的速度并减轻数据存储服务器的负载压力,因为部分缓存数据的丢失不影响业务的正常处理,因此数据还可以从数据库等存储服务器上获取。
而数据存储服务器必须保证数据的可靠性,任何情况下都必须保证数据的可用性和正确性。因此缓存服务器集群伸缩性架构方案不能直接适用于数据库等存储服务器。
关系数据库集群的伸缩性设计
MySQL集群伸缩性方案,多台服务器部署MySQL实例,数据写操作都在主服务器上,由主服务器将数据同步到集群中其他从服务器,数据读操作及数据分析等离线操作在从服务器上进行。
除了数据库主从读写分离,前面提到的业务分割模式也可以用在数据库,不同业务数据表部署在不同的数据库集群上,即数据分库。这种方式的制约条件是跨库的表不能 进行join操作。
在大型网站的实际应用中,即使进行分库和主从复制,对一些单表数据仍然很大的表,还需要进行分片,将一张表拆开分别存储在多个数据库中。
学习一下,Cobar(Alibaba)的架构设计。
Cobar是一个分布式关系数据库访问代理,介于应用数据器和数据库服务器。应用程序通过JDBC驱动访问Cobar集群,Cobar服务器根据SQL和分库规则分解SQL,分发到MySQL集群不同的数据库实例上执行。
前端通信模块负责和应用程序通信,接收到SQL请求(select * from users where userid in (12,22,23)
)后转交给SQL解析模块,SQL解析模块解析获得SQL中的路由规则查询条件(userid in(12,22,23))再转交给SQL路由模块,SQL路由模块根据路由规则配置(userid为偶数路由至数据库A,userid为奇数路由至数据库B)将应用程序提交的SQL分解成两条SQL(select * from users where userid in(12,22);select * from users where userid in (23);
)转交给SQL执行代理模块,发送至数据库A和数据库B分别执行。
数据库A和数据库B的执行结果返回至SQL执行模块,通过结果合并模块将两个返回结果集合并成一个结果集,最终返回给应用程序,完成在分布式数据库中的一次访问请求。
Cobar如何做集群的伸缩呢?
Cobar伸缩有两种:Cobar服务器集群的伸缩和MySQL服务器集群的伸缩。
Cobar服务器可以看做是无状态的应用服务器,其集群伸缩性可以简单使用负载均衡的手段实现。而MySQL中存储着数据,要想保证集群扩容后数据一致负载均衡,必须要做数据迁移,将集群中原来机器中的数据迁移到新添加的机器中。
具体迁移哪些数据可以利用一致性Hash,尽量使需要迁移的数据最少。但迁移数据需要遍历数据库每一条记录,重新进行路由计算确定其是否需要迁移,这会对数据库访问造成一定压力。并且需要解决迁移过程中数据一致性、可访问性、迁移过程中服务器宕机时的可用性等问题。
Cobar利用MySQL的数据同步功能进行数据迁移。数据迁移不是以数据为单位,而是以Schema为单位。在Cobar集群初始化时,在每个MySQL实例创建多个Schema。集群扩容的时候,从每个服务器中迁移部分Schema到新机器中,由于迁移以Schema为单位,迁移过程可以使用MySQL的同步机制。
同步完成时,即新机器中Schema数据和原机器中Schema数据一致的时候,修改Cobar服务器的路由配置,将这些Schema的IP修改为新机器的IP,然后删除原机器中的相关Schema,完成MySQL集群扩容。
由于Cobar服务器处理消耗时间很少,时间主要花费在MySQL上,通过Cobar访问分布式关系数据库,性能和直接访问相当。由于Cobar代替应用程序连接数据库,数据库只需要维护更少的连接,减少不必要的资源消耗,改善性能。
由于Cobar路由后只能在单一数据库实例上处理查询请求,因此无法执行跨库的JOIN操作,当然更不能执行跨库的事务处理。