大型网站架构实现伸缩性的最佳实践
前言
为防止成为标题党,首先定义一下什么是大型网站,
(1)业务依赖多,一个业务流程,需要调用多种资源,Memcached/DB/其他模块等等;
(2)业务快速增长且不平均,比如电商网站中某一类产品增长特别明显;
(3)并发访问量高,数据量大,请求响应时间要求严格;
(4)业务要求7*24小时高可用性;
(5)业务模块多,研发团队多,业务部门多;
?
以上条件满足一部分就可以称为大型网站了,如果是都满足的话,可以想象开发和维护一个业务有多么令人头疼了。
?
接下来再看看什么是伸缩性,
(1)各个业务单独扩展,或者缩小,不影响整体服务;
(2)某个业务挂掉,不影响整体服务;
(3)开发维护成本不随着研发团队/部门数量增长而增长。也就是说,即便公司规模从几十人扩展几百人,开发维护一项服务的成本也不会比几十人的小团队高。
(4)通过水平扩展集群即可解决达到扩展承载能力的要求;
?
实现伸缩性伸缩性对于每个大型网站都是研发投入的重点,因此已经很多优秀的经验分享。代码中我们强调“不要重复造轮子”,经验分享的时候也是一样,先看看下面两篇好文吧。
http://www.programmer.com.cn/11800/
http://www.infoq.com/cn/articles/ebay-scalability-best-practices
?
结合自己的经验,实现伸缩性的最佳实践如下。
最佳实践 #1:业务/组件服务化,单独部署,http/RPC调用把主要服务单独部署,对外提供HTTP/RPC调用服务。这些服务中大部分应该是无状态的,这样热门服务可以方便的水平扩展。而有状态服务,无论是电商业务还是社交网络业务都存在大量有状态服务,则是对其它一些无状态服务进行聚合响应的服务。当无状态服务单点失效时,可以通过Varnish等负载均衡和故障检测前端透明的实现容错。当无状态服务整体出现故障时,聚合服务可以有选择性的降级某些服务。当一个业务流程需要调用多个服务时,采用“快速失败”的策略,避免业务处理机阻塞。即使某个服务性能出现问题,也不会影响全部用户。
?
?
单独部署将业务冷热业务分离,为热门业务提供更好的硬件环境和更大规模集群部署。比如IM系统中包括很多业务,好友状态变更集群是处理请求最多的,而可以将聊天消息存储,查询,群组聊天,各种前端接入集群抽取出来部署成独立模块。
?
最佳实践 #2:避免业务阻塞分布式事务会导致业务阻塞。分布式事务是一个业务要调用多次分布式服务,等这些调用都成功后再返回成功,或者全部回滚。程序员都知道即使是大粒度的本地锁都会对服务器性能和吞吐能力造成很大影响,更不要说分布式调用总是处于脆弱的平衡中。和“快速失败”策略一致,不要让服务器为等待某个调用而停止很长时间。消息队列是常用的策略,比如DB的写入能力可能成为瓶颈时,先写入消息队列,然后直接返回用户“写入DB成功”。此时虽然数据没有真正落入DB,但因消息队列吞吐远远大于DB的吞吐能力,不会对业务造成阻塞。虽然会造成可能的“不一致性”,但大数时候都是可以接受的。Inktomi的Eric Brewer十年前提出的CAP公理是这样说的:分布式系统的三项重要指标——一致性(Consistency)、可用性(Availability)和 分区耐受性(Partition-tolerance)——在任意时刻,只有两项能同时成立。对于高流量的网站来说,我们必须选择分区耐受性,因为它是实 现可伸缩的根本。对于24x7运行的网站,选择可用性也是理所当然的。于是只好放弃即时一致性(immediate consistency)。
?
异步调用也是一个常用的方法。ebay这篇文章里是这么说的:如果组件A同步调用组件B,那么A和B就是紧密耦合的,而紧耦合的系统其可伸缩性特征是各部分 必须共同进退——要伸缩A必须同时伸缩B。很显然,同步调用无伸缩性可言。
?
最佳实践 #3:使用分布式Memcached/DB/本地缓存共享数据如何提高伸缩性?假设一个场景:有一个提醒服务,本来有100w用户,2台机提供服务,每次启动时从DB加载用户数据防止到本地内存。当服务扩展为500w用户时 ,服务器上线重启时从DB加载数据需要5-10分钟,这期间服务时完全停止的。另外如果本地内存已经不能完全承载500w用户数据时怎么办?解决办法:将全部用户数据通过合理的分布式算法(比如一致性Hash)储存在Memcached中。Memcached和业务处理机可以独立的扩容,但即使是一致性Hash也难免造成震荡,因此分布式算法的选择是实现分布式数据存储的关键。解决的办法可以参考我的http://maoyidao.iteye.com/blog/1328131。
当业务增长时,Memcached并发连接数或者带宽就可能成为瓶颈,这个时候还需要增加本地缓存加速。但本地缓存可能和remote cache存在不一致的问题。一种常用办法是定期同步,另外一种方法就是本地缓存迅速失效。
数据库分库分表也是常用的方法,往往各个业务的存储需求差异很大,如果不事先规划好,扩容时就会造成很大的麻烦。实现数据库分库分表可以参考我的http://maoyidao.iteye.com/blog/1456870。
?
总结互联网业界很多最佳实践,比如大系统小做,本质都是为了提高系统伸缩性。从运营层面上讲,能提高用户体验的运营方式比如灰度发布,能给公司带来显著收益的降低IT运营成本等,都依赖高伸缩性的支持。本文仅仅阐述了一些业内常用的提高网站伸缩性的通用方法,更多的工作需要在代码层完成,以及针对不同场景和业务进行具体设计。