所谓高可用性指的是系统如何保证比较高的服务可用率,在出现故障时如何应对,包括及时发现、故障转移、尽快从故障中恢复等等。本文主要以点评的交易系统的演进为主来描述如何做到高可用,并结合了一些自己的经验。需要强调的是,高可用性只是一个结果,应该更多地关注迭代过程,关注业务发展。
  可用性的理解
  理解目标

  业界高可用的目标是几个9,对于每一个系统,要求是不一样的。研发人员对所设计或者开发的系统,要知道用户规模及使用场景,知道可用性的目标。
  比如,5个9的目标对应的是全年故障5分钟。

  拆解目标
  几个9的目标比较抽象,需要对目标进行合理的分解,可以分解成如下两个子目标。
  频率要低:减少出故障的次数
  不出问题,一定是高可用的,但这是不可能的。系统越大、越复杂,只能尽量避免问题,通过系统设计、流程机制来减少出问题的概率。但如果经常出问题,后面恢复再快也是没有用的。
  时间要快:缩短故障的恢复时间
  故障出现时,不是解决或者定位到具体问题,而是快速恢复是第一要务的,防止次生灾害,问题扩大。这里要求要站在业务角度思考,而不仅是技术角度思考。
  下面,我们按这两个子目标来分别阐述。
  频率要低:减少出故障的次数
  设计:根据业务变化不断进行迭代
  以点评交易系统的演进过程为例。
  幼儿时期:2012年前
  使命:满足业务要求,快速上线。
  因为2011年要快速地把团购产品推向市场,临时从各个团队抽取的人才,大部分对.NET更熟悉,所以使用.NET进行了第一代的团购系统设计。毕竟满足业务要求是第一的,还没有机会遇到可用性等质量问题。考虑比较简单,即使都挂了,量也比较小,出现问题,重启、扩容、回滚解决问题了。
  系统架构如下图所示。

  ###少年时期:垂直拆分(2012-2013)
  使命:研发效率&故障隔离。
  当2012年在团单量从千到万量级变化,用户每日的下单量也到了万级时候,需要考虑的是迭代速度、研发效率。垂直拆分,有助于保持小而美的团队,研发效率才能更高。另外一方面也需要将各个业务相互隔离,比如商品首页的展示、商品详情页的展示,订单、支付流程的稳定性要求不一样。前面可以缓存,可以做静态化来保证可用性,提供一些柔性体验。后面支付系统做异地容灾,比如我们除了南汇机房支付系统,在宝山机房也部署了,只是后来发现这个系统演进太快,没有工具和机制保证双机房更新,所以后来也不好使用了。
  系统演进如下图所示。服务垂直化了,但是数据没有完整隔离开,服务之间还需要互相访问非自己的数据。

  青年时期:服务做小,不共享数据(2014-2015)
  使命:支撑业务快速发展,提供高效、高可用的技术能力。
  从2013年开始,Deal-service (商品系统)偶尔会因为某一次大流量(大促或者常规活动)而挂掉,每几个月总有那么一次,基本上可用性在3个9徘徊。这里订单和支付系统很稳定,因为流量在商品详情页到订单有一个转化率,流量大了详情页挂了,订单也没有流量了。后来详情页的静态化比较好了,能减少恢复的速度,能降级,但是Deal-service的各个系统依赖太深了,还是不能保证整体端到端的可用性。
  所以2014年对Deal-service做了很大的重构,大系统做小,把商品详情系统拆成了无数小服务,比如库存服务、价格服务、基础数据服务等等。这下商品详情页的问题解决了,后面压力来了,订单系统的压力增大。2014年10月起,订单系统、支付系统也启动了全面微服务化,经过大约1年的实践,订单系统、促销系统、支付系统这3个领域后面的服务总和都快上百个了,后面对应的数据库20多个,这样能支撑到每日订单量百万级。
  业务的增长在应用服务层面是可以扩容的,但是大的单点——数据库是集中式的,这个阶段我们主要是把应用的数据访问在读写上分离,数据库提供更多的从库来解决读的问题,但是写入仍然是大的瓶颈(MySQL的读可以扩展,而写入QPS也小2万)。
  这时系统演变成如下图所示。这个架构大约能支撑QPS 3000左右的订单量。