1.1 分布式系统的架构演变过程
在移动互联网的浪潮中,你我正生逢其时地享受着当下,如果你愿意做一只站在风口上等待起飞的猪,那么请认真地问问自己,是否已经准备好了?互联网究竟是什么?简而言之,互联网诠释的是一种精神,融入了高度开放、分享,以及自由的精神。如果你想融入这个圈子,那么请务必先舍弃掉与互联网精神背道而驰的陈旧观念和思维,否则你自始至终都只会被孤立在局外。
互联网悄然改变了世界,改变了人们对事务的认知,缩短了人与人之间的距离。无论你是否愿意承认,互联网已经完全影响并融入我们的生活中。我们的长辈们,也从早期对新鲜事物的排斥,变成现在的欣然接受,这就是互联网与生俱来的魅力和魔力。笔者的母亲从来就不是一个喜欢追赶潮流的人,但是她早已智能设备从不离身,每天早上起床的第一件事情就是拿起智能手机,刷刷朋友圈、看看时事政治、做回“吃瓜群众”,八卦下娱乐新闻,甚至衣食住行也几乎是通过互联网这个载体一键搞定的,如图1-1所示。既然互联网能使我们的生活质量更好,那就请张开双臂紧紧拥抱它。
图1-1 拥有互联网的生活
拥有互联网的夜晚是明亮的,当然为这寂静夜空点燃光明的正是聚集在各个互联网企业内部的技术团队,正是这些家伙昼夜颠倒的辛勤付出(无数的通宵、无数的会议、无数的版本迭代、无数的交互体验优化、无数的系统架构升级),才换来今日互联网的光彩夺目。不过任何事物都如同硬币一般具备两面性,我们不能“光见贼吃肉,不见贼挨打”,这句话放在互联网领域似乎相当应景,很多电商网站为了吸引用户流量,往往都会以极低的价格作为诱饵,但是当促销活动正式开始后,峰值流量却远远超出了系统所能够承受的处理能力,这必然只会产生一种结果——宕机。如何帮助企业顺利走出困境,打造真正具备高性能、高可用、易扩展、可伸缩,以及相对安全的网站架构才是架构师、技术大牛们应该重点思考的问题和责任,哪怕是系统宕机,也要尽最大努力做到“死而不僵”,用技术支撑业务的野蛮生长,活下去才会有更美好的明天。
一般来说,网站由小变大的过程,几乎都需要经历单机架构、集群架构、分布式架构。伴随着业务系统架构一同演变的还有各种外围系统和存储系统,比如关系型数据库的分库分表改造、从本地缓存过渡到分布式缓存等。当系统架构演变到一定阶段且逐渐趋向于稳定和成熟后,架构师们需要对技术细节追本溯源,如果现有的技术或者框架不能有效满足业务需要,就需要从“拿来主义”的消费者角色转变为自行研发的生产者角色。当然,在这条技术之路离你和你所在的企业还很遥远的时候,尽管未雨绸缪利大于弊,但这却并不是你现阶段的工作重点。在此大家需要注意,对技术由衷的热爱和痴迷本身并没有什么不对,但是千万不能够过于沉醉而选择刻意忽略掉业务,任何技术的初衷都是为了更好地服务业务,一旦脱离业务,技术必然会失去原有的价值和意义,切勿舍本逐末。
1.1.1 单机系统
任何一个网站在发布初期几乎都不可能立马就拥有庞大的用户流量和海量数据,都是在不停的试错过程中一步一步演变其自身架构,满足其自身业务。所以我们常说,互联网领域几乎没有哪一个网站天生就是大型网站,因为往往系统做大与业务做大是呈正比的,大型网站都是从小型网站逐渐演变过来的,而不是被刻意设计出来的。试想一下,如果业务不见起色,一味地追求大型网站架构又有何意义呢?
对于一个刚上线的项目,我们往往会将Web服务器、文件服务器和数据库全都部署在同一台物理服务器上,这样做的好处只有一个——省钱,如图1-2所示。资金紧张且用户流量相对较小的网站,采用这样的架构进行部署确实非常实惠,毕竟用户流量上不去,自然就没有必要考虑更多的问题,哪怕是系统宕机,影响范围也不大。当然,一旦业务开始加速发展,用户逐渐增多,系统瓶颈便会开始暴露,这时架构师就可以考虑对现有网站架构做出以下四点调整:
• 独立部署,避免不同的系统之间相互争夺共享资源(比如CPU、内存、磁盘等);
• Web服务器集群,实现可伸缩性;
• 部署分布式缓存系统,使查询操作尽可能在缓存命中;
• 数据库实施读/写分离,实现HA(High Availability,高可用性)架构。
图1-2 在同一台机器上部署单机系统
1.1.2 集群架构
一旦用户开始增多,并发流量上来后,为了让用户拥有更好的操作体验,我们不得不对单机系统架构做出调整和优化,因此在这个阶段主要需要解决的问题就是提升业务系统的并行处理能力,降低单机系统负载,以便支撑更多的用户访问操作。
集群(Cluster)技术可以将多台独立的服务器通过网络相互连接组合起来,形成一个有效的整体对外提供服务,使用集群的意义就在于其目标收益远高于所付出的实际成本和代价。一般,互联网领域都有一个共同的认知,那就是当一台服务器的处理能力接近或已超出其容量上限时,不要企图更换一台性能更强劲的服务器,通常的做法是采用集群技术,通过增加新的服务器来分散并发访问流量,1台不够就扩到2台,2台不够就扩到4台,只要业务系统能够随意支持服务器的横向扩容,那么从理论上来说就应该无惧任何挑战,从而实现可伸缩性和高可用性架构。
如图1-3所示,对于无状态的Web节点来说,通过Nginx来实现负载均衡调度似乎是一个不错的选择,但是在生产环境中,Nginx也应该具备高可用性,这可以依靠DNS轮询来实现。在集群环境中,Web节点的数量越多,并行处理能力就越强,哪怕其中某些节点因为种种原因宕机,也不会使系统的整体服务不可用。
图1-3 在独立的机器上部署集群系统
伴随着Web集群改造的还有分布式缓存和数据库,对于查询操作我们应该尽可能在缓存命中,从而降低数据库的负载压力。尽管缓存技术可以解决数据库的大部分查询压力,但是写入操作和无法在缓存命中的数据仍然需要频繁地对数据库进行读/写操作,因此对数据库实施读/写分离改造也迫在眉睫。
业务发展到一定阶段后必然会变得更加复杂,用户规模也会线性上升,这时架构师就可以考虑对现有网站架构做出以下两点调整:
• 利用CDN加速系统响应;
• 业务垂直化,降低耦合,从而实现分而治之的管理。
由于中国的网络环境相对比较复杂,跨网络、跨地域的用户访问网站时,速度有较大差别,因此当用户流量增大之后,我们需要考虑将系统中的一些静态资源数据(如图片、音频、视频、脚本文件及 HTML 网页等)缓存在 CDN 节点上,因为CDN 正变得越来越廉价,如图1-4 所示。由于用户的请求并不会直接落到企业的数据中心,而是请求到离用户最近的ISP(Internet Service Provider,互联网服务提供商)上,因此可以大幅提升系统整体的响应速度。
图1-4 使用CDN加速网站响应
1.1.3 拆系统之业务垂直化
尽管业务系统可以通过集群技术来提升并行处理能力和实现高可用架构,但是一般到了这个阶段,不仅用户规模暴增,业务需求也变得更加复杂。由于业务逻辑全部耦合在一起,并且部署在同一个 Web 容器中,这必然会导致系统中不同业务之间的耦合过于紧密,除了扩展和维护困难,在生产环境中极有可能会因为某个业务功能不可用而影响系统整体服务不可用,因此在这个阶段主要需要解决的问题就是降低业务耦合,实现高内聚低耦合,提升系统容错性,避免牵一发而动全身的风险。
下面为大家分享笔者亲身经历过的一次真实生产事故。
在一次“双 11”抢购活动中,我们预计零点开抢时的那一拨峰值流量肯定会远高于平时,因此在几天前运维部门的同学就提前扩容了上百台服务器来应对“双11”。在活动开始前,大家摩拳擦掌,屏住呼吸紧盯着电脑屏幕“观赏”流量监控曲线图时,悲催的事情发生了。随着倒数结束,活动正式开始,流量曲线居然没有太大的起伏,这让当时在场的小伙伴们瞬间懵掉了,紧急排查故障后发现,前端APP在每一次业务请求提交之前都会先将客户端埋点数据上报到专门用于数据收集的Web服务器上(后期需要利用大数据平台对埋点数据进行一些用户行为的实时/离线分析),然后再请求到业务系统上执行逻辑处理。但因为我们的疏忽,负责数据收集的 Web服务器和核心业务系统使用的是同一个 Nginx 服务器进行请求转发,由于没有对数据收集的机器做扩容,导致 Nginx 日志中出现大量的请求超时异常,从而严重影响了核心业务的正常运转。据不完全统计,这次活动至少导致了2/3的用户无法正常访问,而那些“侥幸”正常访问并下单的用户,也因为在指定时间内无法顺利完成支付而导致大量的订单回流。这次惨痛的教训说明,细节决定成败的重要性不言而喻。当然,既然踩坑了,吸取经验教训避免悲剧重现才是最重要的。
大家一定要具备大系统小做的意识,所谓拆系统其实指的就是业务垂直化,简而言之,架构师可以根据系统业务功能的不同拆分出多个业务模块(一般大型电商网站都会拆分出首页、用户、搜索、广告、购物、订单、商品、收益结算等子系统),再由不同的业务团队负责承建,分而治之,独立部署,如图1-5所示。在此大家需要注意,拆分粒度越细,耦合越小,容错性越好,每个业务子系统的职责就越清晰。但是如果拆分粒度过细,维护成本将是一个不小的挑战。对业务系统完成拆分后,就意味着系统已经过渡到分布式系统了,这也为企业实施服务化改造和数据库分库分表改造提前做好了准备。关于数据库分库分表的相关知识点,大家可以直接阅读本书的第5章。
图1-5 业务垂直化改造
1.1.4 为什么需要实现服务化架构
在大部分互联网企业的系统架构演变过程中,不得不提到的就是服务化改造,那么究竟什么是服务化,以及为什么要实现服务化架构呢?
随着用户规模逐渐庞大,需求更加复杂,我们一定会对耦合在一个Web容器中的业务系统进行垂直化改造,以业务功能为维度拆分出多个子系统,这样做就是为了能够更清晰地规划和体现出每个子系统的职责,降低业务耦合,以及提升容错性。但是在多元化的业务需求下,子系统中一定会存在较多的共享业务,这些共享业务肯定会被重复建设,产生较多的冗余业务代码。而且,业务系统中数据库连接之类的底层资源必然会限制业务系统所允许横扩的节点数量,因为在横扩过程中,连接数是机器数的平方(若机器数为1,则连接池中的连接数为2;机器数为2,则连接池中的连接数为4;机器数为3,则连接池中的连接数为9,以此类推)。除了共享业务重复建设和资源连接受限,还有一个不容忽视的问题,当业务做大时,技术团队的人员规模也开始膨胀,太多人共同维护一个系统肯定会坏事,尤其是那些“手潮”的同学,经常会导致SVN中的版本冲突。因此为了避免这些问题,服务化架构(Service Oriented Ambiguity,SOA)改造刻不容缓,如图1-6所示。
图1-6 服务化架构
当然,如果你现阶段并不满足上述任何一个条件,笔者其实是不建议实施服务化改造的,因为这显然会提升系统的复杂度和维护成本,甚至还需要提前规划一些服务化场景下才需要考虑的技术难题,如分布式事务、服务治理等。
1.1.5 服务拆分粒度之微服务
业务系统实施服务化改造后,原本共享的业务被拆分,形成可复用的服务,可以在最大程度上避免共享业务的重复建设、资源连接瓶颈等问题出现。那么那些被拆分出来的服务,是否也需要以业务功能为维度来进行拆分,使之能够独立进行部署,以降低业务耦合和提升容错性呢?
由于Web容器中子系统的服务层被剥离,因此这些子系统从某种意义上来说仅仅只是一个控制层(Controller),由于控制层无须处理任何复杂的业务逻辑,因此吞吐量自然会得到提升,但是业务的逻辑处理由独立部署的服务层负责,所以架构师需要认真思考如何有效提升服务层的整体服务质量。最近几年,微服务绝对是一个被炒得火热的词汇,就连 Spring 也希望在分布式领域中分一杯羹,借此推出了Spring-Cloud工具集。那么究竟什么是微服务呢?它和服务化之间又存在什么必然联系呢?从本质上来说,这其实完全就是一回事,所谓的微服务架构,从宏观上来看,无非就是细化了服务拆分过程中的粒度,粒度越细,业务耦合越小,容错性越好,并且后期扩展会越容易,如图1-7所示。
图1-7 微服务架构
那么服务到底应该如何拆分才称得上“微服务”呢?一般来说,笔者建议大家以子系统为维度进行服务拆分,也可以在此基础上对服务的读/写进行分离,或者拆分出单独的服务,这样服务的拆分粒度大小适中,所付出的成本与实际收益会相对更加均衡,否则粒度过细不仅会导致维护成本提升,而且系统出现问题时定位和排查问题困难重重,更重要的是很难梳理服务之间的依赖关系。