阿里毕玄:系统架构师如何做好系统设计?

https://mp.weixin.qq.com/s/LrpvaAQSn_TITMwH7XhG3A

除了例子,其它话基本上虚蛋。。。

目录

系统建设的目的:

可衡量的系统目标:

达成目标的核心问题:

1.

2. 

3. 

4. 

解决核心问题的设计:

HSF的设计

T4的设计

异地多活的设计

架构师需要具备的能力:


系统建设的目的:

结合我自己的经历来说,在早期做HSF时,在系统建设的目的这点上是最为缺失的,也导致了自己在HSF阶段犯下了几次大的错误,例如最典型的就是HSF做动态化的那次系统架构改造,如果仔细的去分析当时做这件事的目的,就会看到这个是出于技术情怀,而不是业务端面临的业务挑战,或者说HSF的用户面临的问题,也就是之前说的出发点的问题,我觉得这是很多技术人员非常容易犯的错误,就是纯粹是出于技术诉求发动的很大动作的系统重构,我自己是在有一年有一位阿里的高管在给我做辅导时,提到首先要思考清楚为什么做某件事,并且能讲清楚原因,才明白了动机真的是非常重要的,后面做事的时候才能没那么技术化了。

因为HSF/Ali HBase的经历,到了后面做阿里容器/调度、异地多活的时候在目的这块才算是能更好的把握,能更好的去结合阿里的业务所面临的挑战来看要做的事。

好处:

总得来说,我认为做系统设计前,一定要先对于系统建设的目的分析清楚,确保系统建设有价值和有意义,同时确保后面的整个系统设计是能让目的达成的。

可衡量的系统目标:

第一个是2011年做容器化,建设这套系统的目的是为了应对预计会越来越大的机器成本,目标相应的制定为支撑相同的业务量,机器下降一半

第二个是2013年做异地多活,建设这套系统的目的是为了能够让业务具备更强的抵御灾害的能力,尽管后面发现因为有了异地多活,有了更多的好处,但那些确实在系统设计之初是完全没放在建设目的里的,后面能做到纯属巧合,例如因为有了异地多活使得后面的弹性借助云资源成为了现实,因为有了异地多活,基础设施技术的演进可以更加快速,在设计之初根据目的相应制定的目标为业务能够部署在中国多个地点(地点间距离>1千公里),多个地点部署的业务都处于承接流量的状态,且流量从A点切换到B点能在30s内完成。

好处:避免偏题,便于跟踪效果;

达成目标的核心问题:

1.

最开始做HSF的时候,为什么要做HSF是比较清晰的,在可衡量的目标上也有一个大概的要支持每条上亿的服务调用,但由于当时的技术功底问题,导致了在提炼核心问题上是有很大差距的,这些也造成了后来HSF总是不断的重构、修修补补之类。所以,我从来就不认为技术功底不好的人能做好一个架构师,架构师绝对不是看到的随手画几个框那么简单,那通常只是个结果,但要合理的把框画出来是需要基于非常坚实的技术功底,HSF在最初设计时认为的核心问题就是怎么实现一个易用有服务定义的RPC框架,但对于如何支撑好上亿的交互调用量服务化上线后给业务研发会带来什么问题(例如排查问题变复杂了),在核心问题上是有很大的缺失的,例如HSF上线后才发现的中间的负载均衡的问题,而这个问题是导致了HSF结构重新设计的,这个后来回头看就会发现如果是一个知识面更广的架构师可能一开始就会想到这个核心问题,所以如果回过头去看,HSF这样的框架,要达成目标,要解决的核心问题应该是:

  1. 易用、能支撑上亿次服务交互的RPC框架;

  2. 服务间的软件负载均衡问题;

  3. 服务交互的问题排查;

2. 

在做T4(容器)的时候,目的、目标都还比较清晰,问题的提炼现在回顾也做的还ok,T4要解的核心问题为如何实现在一台机器上跑20个应用(还是上面已经说过的容器化,砍机器成本撒?),T4出现的问题更多是对于核心问题的设计方案上,这个到下篇讲围绕核心问题的系统设计上再写。

3. 

到了做异地多活的时候,目的、目标的清晰化都ok,对于异地多活而言,要做到在中国多个城市都可同时支撑流量,并且可在几十秒内完成流量切换,异地多活中物理距离所带来的网络延时是不可突破的,怎么做到多地活且流量可动态切换,要做到这个,面临的核心问题是:

  1. 如何将流量进行切分,且让请求的整个处理过程能封闭在local完成

  2. 如何保障异地多活后的数据一致性

4. 

到了最近几年做统一调度的时候,整个做系统设计的思考框架我觉得算是比较熟练了,所以统一调度的目的、目标都很清晰,结合当时的情况,要实现统一调度的目标,其面临的核心问题是:

  1. 如何实现一套在线业务资源的调度系统去满足各种资源诉求?

  2. 如何尽可能扩大统一的资源池,解决资源池统一面临的资源竞争、资源被抢、多种不同资源规格等问题?

  3. 如何实现在线业务、离线任务两套调度系统的互通?

  4. 如何解决在线业务、离线任务混合部署时的资源竞争的问题?

从上面的这些cases来看,可以看到,从可衡量的目标映射到技术层面要去解决的核心问题,是很需要技术功底的,对于工程类型的项目、产品而言,工程经验在这个时候也会特别重要,而通常我也觉得这是衡量一个优秀架构师很直接的地方。

解决核心问题的设计:

HSF的设计

HSF在设计之初要解决的第一个核心问题就是做一个易用,能支撑每天上亿次服务调用的服务方式的RPC框架。

易用这点在第一个版本犯了错,不过还好是第一个版本,否则纠正错误的代价会无比巨大,那个版本里,如果要把一个spring的bean发布为HSF服务,或者调用一个HSF服务,需要写一个文件,在文件里描述发布的服务和调用的服务,并且在这个文件放在jboss的某个目录里,这个方式看起来对在写代码的过程中完全没有侵入,但导致的巨大问题是这文件放在哪里写,写完后部署的阶段怎么自动放到对应的目录去,在第二个版本里才把这个调整为用一个Spring Bean的方式来做服务的发布和调用,尽管这一定程度导致了业务代码需要有对HSF的明显的依赖,但对维护、部署等都变的很标准,所以从这里可以看到,设计是全方位的,要考虑到的不仅仅是怎么实现,还有别人怎么用,运行、维护阶段又是怎么样的。

HSF犯的第二个错,就是在能支撑每天上亿次服务调用的RPC框架这点上,是给我自己代码生涯最大的教训,甚至彻底改变了我之后做设计时的技术选型风格。在做HSF之前,我从来没做过一天访问量超过100w的系统,完全搞不清一个每天上亿次的系统到底有什么不同,HSF最早的版本在通讯框架上选择了JBoss-Remoting,原因也其实比较简单,因为我们用的Web容器是JBoss,结果这个版本在一个非常重要的系统上线时,出现了严重的故障,导致了整个网站的响应速度都变的很慢。当时查了几乎整整一天都没查出原因到底是什么,后来回滚恢复,所以可以肯定是HSF上线造成的,等到回滚后的一个星期内才查出原因,是因为JBoss-Remoting在调用远端时,默认的超时时间为60s,而我们后端的那个系统在处理某些服务的时候会特别慢,进而导致了共用的处理线程池满了,所以整个网站的表现就变慢了。

这次问题让我彻底明白了访问量大的系统最重要的是对整个系统的处理过程要非常的清楚,因为在访问量大的情况下,一些小的问题有可能会放大成很大的问题,进而到故障,所以访问量大的系统对技术的可控性要求是极高的,这也是最大的不同,可控性并不代表一定要完全自己写,但要求如果用到开源的东西,要对开源的东西的代码逻辑非常熟悉,为了解决上面的问题,HSF基于Mina写了一个自己版本的通讯框架,自己来处理连接方式、线程池等,后面在做各种HSF改造,以及其他技术改造时,基本都遵循了技术可控性这个原则。

在前面核心问题那篇里也讲到,HSF在设计时其实核心问题提炼的就是有问题的,导致了后面在负载均衡、服务化后问题排查这两点上出现了严重的返工现象,而这些本其实都可以避免,就像现在再去做服务化框架的人基本都不会犯这样的错了。

在负载均衡这点上,在早期版本里,是通过硬件负载均衡设备来做的,这里造成了好几个问题,一是需要先配置要调用的服务的vip地址,当然,这可以通过一个*的配置服务器之类的方式,第二个是HSF采用的是长连接的方式,通过vip去连接后端的一个集群时,这里会出现非常麻烦的问题,例如后端集群发布重启,很有可能就会造成连接的极度不均衡,进而导致故障。

除了上面两个问题后,还有一个触发HSF去做改造的原因是当时的硬件负载设备出现了流量跑满的现象,而这是必须要经过的一个点,会造成全站全部崩溃,不希望在未来系统中有个这么大的高风险的集中点,再加上上面的两个问题,决定做彻底的改造,于是HSF开始设计了目前看起来在服务框架体系中非常经典的软件方式的服务注册、发现和寻址的结构。

阿里毕玄:系统架构师如何做好系统设计?

在负载均衡这件事上,现在回顾也可以看出这个仍然是当初对一个访问量巨大的系统考虑不够全面造成的。

在服务化后会带来的排查问题这点上,当初设计的时候更是完全没有考虑到,导致了后面排查问题效率低、人力投入大等等问题,后来为了解决这个问题,学习了Google家Dapper的思想,但花了很长时间这东西才真正落地。

除了上面这些外,HSF其实还有各种设计问题,例如最早的通讯协议里竟然是没有版本号的,导致后面升级时处理兼容的复杂,又例如更麻烦的一个话题就是在多语言支持上。

HSF作为我第一个真正做的访问量巨大且核心的系统的设计,由于当初的技术功底,犯下了无数错误,导致了N次返工、故障和弥补,当然也让自己得到了很大的成长,这几年回过头想这个问题,越来越觉得必须无比感谢我当时的主管对我巨大的包容和支持,HSF的经历,让我在解决核心问题的设计这个环节上,明白的是作为一个架构师,在技术选型上深厚的技术功底,在整个设计方案上知识的广度,考虑的全面性(从开发态、部署态、运行态和运维态)都是要求极高的。

T4的设计

T4在核心问题的提炼上没有太大的问题,但在怎么解这个问题的设计上那犯下的错误现在来看都是低级到不行。

为了做到在一台机器上能比以前用虚拟机的方式运行更多的应用进程,最早我们采用的方法是各种hack,其实要实现的就是进程级隔离,结果就是hack到了一定程度后,确实勉强能用了,但上线了一些小范围,有了一些用户后,发现我们的hack是很难枚举的,非常痛苦,直到有一天“发现”了LXC,才走对了路线。

LXC为Linux Container的简写。可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。相当于C++中的NameSpace。容器有效地将由单个操作系统管理的资源划分到孤立的组中,以更好地在孤立的组之间平衡有冲突的资源使用需求。与传统虚拟化技术相比,它的优势在于:

(1)与宿主机使用同一个内核,性能损耗小;

(2)不需要指令级模拟;

(3)不需要即时(Just-in-time)编译;

(4)容器可以在CPU核心的本地运行指令,不需要任何专门的解释机制;

(5)避免了准虚拟化和系统调用替换中的复杂性;

(6)轻量级隔离,在隔离的同时还提供共享机制,以实现容器与宿主机的资源共享。

总结:Linux Container是一种轻量级的虚拟化的手段。

Linux Container提供了在单一可控主机节点上支持多个相互隔离的server container同时执行的机制。Linux Container有点像chroot,提供了一个拥有自己进程和网络空间的虚拟环境,但又有别于虚拟机,因为lxc是一种操作系统层次上的资源的虚拟化。

Sourceforge上有LXC这个开源项目。LXC项目本身只是一个为用户提供一个用户空间的工具集,用来使用和管理LXC容器。LXC真正的实现则是靠Linux内核的相关特性,LXC项目只是对此做了整合。基于容器的虚拟化技术起源于所谓的资源容器和安全容器。

LXC在资源管理方面依赖于Linux内核的cgroups子系统,cgroups子系统是Linux内核提供的一个基于进程组的资源管理的框架,可以为特定的进程组限定可以使用的资源。LXC在隔离控制方面依赖于Linux内核的namespace特性,具体而言就是在clone时加入相应的flag(NEWNS NEWPID等等)。

除了上面这个选型层面的问题外,T4的过程中还碰到过很多类似的问题,例如用什么方法去控制磁盘空间的限制,最早我们也是用的同样的image的方式,但image的方式对磁盘空间超卖其实是非常不友好的,后来为了把这个方案更换成dir quota的方案,一帮人几乎是连续折腾了一个多月,因为线上已经在运行的要通过cp文件等方法来弄。

14.1.1 什么是 Quota

在 Linux 系统中,由于是多用户多任务的环境,所以会有多人共同使用一个硬盘空间的情况发生, 如果其中有少数几个使用者大量的占掉了硬盘空间的话,那势必压缩其他使用者的使用权力! 因此管理员应该适当的限制硬盘的容量给使用者,以妥善的分配系统资源!避免有人*呀!

举例来说,我们使用者的默认主文件夹都是在 /home 下面,如果 /home 是个独立的 partition , 假设这个分区有 10G 好了,而 /home 下面共有 30 个帐号,也就是说,每个使用者平均应该会有 333MB 的空间才对。 偏偏有个使用者在他的主文件夹下面塞了好多只影片,占掉了 8GB 的空间,想想看,是否造成其他正常使用者的不便呢? 如果想要让磁盘的容量公平的分配,这个时候就得要靠 quota 的帮忙啰!

  • Quota 的一般用途 [1]

quota 比较常使用的几个情况是:

  • 针对 WWW server ,例如:每个人的网页空间的容量限制!
  • 针对 mail server,例如:每个人的邮件空间限制。
  • 针对 file server,例如:每个人最大的可用网络硬盘空间 (教学环境中最常见!)

上头讲的是针对网络服务的设计,如果是针对 Linux 系统主机上面的设置那么使用的方向有下面这一些:

  • 限制某一群组所能使用的最大磁盘配额 (使用群组限制): 你可以将你的主机上的使用者分门别类,有点像是目前很流行的付费与免付费会员制的情况, 你比较喜好的那一群的使用配额就可以给高一些!呵呵! ^_^...

  • 限制某一使用者的最大磁盘配额 (使用使用者限制): 在限制了群组之后,你也可以再继续针对个人来进行限制,使得同一群组之下还可以有更公平的分配!

  • 限制某一目录 (directory, project) 的最大磁盘配额: 在旧版的 CentOS 当中,使用的默认文件系统为 EXT 家族,这种文件系统的磁盘配额主要是针对整个文件系统来处理,所以大多针对“挂载点”进行设计。 新的 xfs 可以使用 project 这种模式,就能够针对个别的目录 (非文件系统喔) 来设计磁盘配额耶!超棒的!

大概有这些实际的用途啦!基本上,quota 就是在回报管理员磁盘使用率以及让管理员管理磁盘使用情况的一个工具就是了! 比较特别的是,XFS 的 quota 是整合到文件系统内,并不是其他外挂的程序来管理的,因此,通过 quota 来直接回报磁盘使用率,要比 unix 工具来的快速! 举例来说, du 这东西会重新计算目录下的磁盘使用率,但 xfs 可以通过 xfs_quota 来直接回报各目录使用率,速度上是快非常多!

  • Quota 的使用限制

虽然 quota 很好用,但是使用上还是有些限制要先了解的:

  • 在 EXT 文件系统家族仅能针对整个 filesystem: EXT 文件系统家族在进行 quota 限制的时候,它仅能针对整个文件系统来进行设计,无法针对某个单一的目录来设计它的磁盘配额。 因此,如果你想要使用不同的文件系统进行 quota 时,请先搞清楚该文件系统支持的情况喔!因为 XFS 已经可以使用 project 模式来设计不同目录的磁盘配额。

  • 核心必须支持 quota : Linux 核心必须有支持 quota 这个功能才行:如果你是使用 CentOS 7.x 的默认核心, 嘿嘿!那恭喜你了,你的系统已经默认有支持 quota 这个功能啰!如果你是自行编译核心的, 那么请特别留意你是否已经“真的”打开了 quota 这个功能?否则下面的功夫将全部都视为“白工”。

  • 只对一般身份使用者有效: 这就有趣了!并不是所有在 Linux 上面的帐号都可以设置 quota 呢,例如 root 就不能设置 quota , 因为整个系统所有的数据几乎都是他的啊! ^_^

  • 若启用 SELinux,非所有目录均可设置 quota : 新版的 CentOS 默认都有启用 SELinux 这个核心功能,该功能会加强某些细部的权限控制!由于担心管理员不小心设置错误,因此默认的情况下, quota 似乎仅能针对 /home 进行设置而已~因此,如果你要针对其他不同的目录进行设置,请参考到后续章节查阅解开 SELinux 限制的方法喔! 这就不是 quota 的问题了...

新版的 CentOS 使用的 xfs 确实比较有趣!不但无须额外的 quota 纪录档,也能够针对文件系统内的不同目录进行配置!相当有趣! 只是不同的文件系统在 quota 的处理情况上不太相同,因此这里要特别强调,进行 quota 前,先确认你的文件系统吧!

  • Quota 的规范设置项目:

quota 这玩意儿针对 XFS filesystem 的限制项目主要分为下面几个部分:

  • 分别针对使用者、群组或个别目录 (user, group & project):

XFS 文件系统的 quota 限制中,主要是针对群组、个人或单独的目录进行磁盘使用率的限制!

  • 容量限制或文件数量限制 (block 或 inode):

我们在第七章谈到文件系统中,说到文件系统主要规划为存放属性的 inode 与实际文件数据的 block 区块,Quota 既然是管理文件系统,所以当然也可以管理 inode 或 block 啰! 这两个管理的功能为:

  • 限制 inode 用量:可以管理使用者可以创建的“文件数量”;
  • 限制 block 用量:管理使用者磁盘容量的限制,较常见为这种方式。

  • 柔性劝导与硬性规定 (soft/hard):

既然是规范,当然就有限制值。不管是 inode/block ,限制值都有两个,分别是 soft 与 hard。 通常 hard 限制值要比 soft 还要高。举例来说,若限制项目为 block ,可以限制 hard 为 500MBytes 而 soft 为 400MBytes。这两个限值的意义为:

  • hard:表示使用者的用量绝对不会超过这个限制值,以上面的设置为例, 使用者所能使用的磁盘容量绝对不会超过 500MBytes ,若超过这个值则系统会锁住该用户的磁盘使用权;
  • soft:表示使用者在低于 soft 限值时 (此例中为 400MBytes),可以正常使用磁盘,但若超过 soft 且低于 hard 的限值 (介于 400~500MBytes 之间时),每次使用者登陆系统时,系统会主动发出磁盘即将爆满的警告讯息, 且会给予一个宽限时间 (grace time)。不过,若使用者在宽限时间倒数期间就将容量再次降低于 soft 限值之下, 则宽限时间会停止。

  • 会倒数计时的宽限时间 (grace time):

刚刚上面就谈到宽限时间了!这个宽限时间只有在使用者的磁盘用量介于 soft 到 hard 之间时,才会出现且会倒数的一个咚咚! 由于达到 hard 限值时,使用者的磁盘使用权可能会被锁住。为了担心使用者没有注意到这个磁盘配额的问题, 因此设计了 soft 。当你的磁盘用量即将到达 hard 且超过 soft 时,系统会给予警告,但也会给一段时间让使用者自行管理磁盘。 一般默认的宽限时间为七天,如果七天内你都不进行任何磁盘管理,那么 soft 限制值会即刻取代 hard 限值来作为 quota 的限制。

以上面设置的例子来说,假设你的容量高达 450MBytes 了,那七天的宽限时间就会开始倒数, 若七天内你都不进行任何删除文件的动作来替你的磁盘用量瘦身, 那么七天后你的磁盘最大用量将变成 400MBytes (那个 soft 的限制值),此时你的磁盘使用权就会被锁住而无法新增文件了。

整个 soft, hard, grace time 的相关性我们可以用下面的图示来说明:

阿里毕玄:系统架构师如何做好系统设计?

图14.1.1、soft, hard, grace time 的相关性

图中的长条图为使用者的磁盘容量,soft/hard 分别是限制值。只要小于 400M 就一切 OK , 若高于 soft 就出现 grace time 并倒数且等待使用者自行处理,若到达 hard 的限制值, 那我们就搬张小板凳等着看好戏啦!嘿嘿!^_^!这样图示有清楚一点了吗?

quota的使用限制

  • 仅能针对整个文件系统
  • 内核必须支持
  • 只对一般用户生效

HSF的那段看到的很多是在技术深度上的问题,而T4的这段设计,现在回顾最主要的问题是这个技术领域视野的严重问题,所以我认为作为架构师,在相应的技术领域要有足够的视野,一定要知道这个领域的工程界、学术界是什么情况,这样对自己在结合目的、目标以及一些约束条件下做出更合理的技术选型是非常重要的,之前也写过一篇关于如何扩充技术视野的文章。

异地多活的设计

到了做异地多活这个阶段,也许是因为有了前面的一些积累,总结反思,我自己觉得异地多活的设计更多的是选择,至于对错我总体认为还好,所以这里我就讲一些异地多活设计上为了解决核心问题所面临的一些权衡选择,而这也是架构师在做设计上非常重要的一个部分,如何去根据各种约束来做一些方案的权衡选择。

异地多活在核心问题上要解决的是请求封闭、数据一致性这两个关键问题,在为了解决这两个问题的设计上,参考了工程界的一些情况,最后发现我们所面临的状况还是很不一样。

在这里就抛出一些异地多活设计上所面临的选择,我就不去讲我的选择逻辑之类的了,方便大家思考,以及交流探讨。

  1. 流量/数据拆分的规则到底按什么好?买家/卖家/商品?

  2. 分流的规则和数据库分库分表的规则的关系:松耦合 Vs 强绑定?

  3. 数据同步策略的选择:部分 Vs 全量?

  4. 数据一致性的保障,在哪些层面做,CAP?

  5. 部署的选择:两地 Vs 三地,地域的分布选择?

  6. 落地节奏,一年?两年?三年?

架构师需要具备的能力:

  1. 对业务所面临的挑战的理解,从业务挑战到技术挑战映射的能力,或者说技术抽象的能力;

  2. 知识储备以及考虑的全面性,从开发、部署、运行、维护态;

  3. 技术选型能力,极厚的技术功底,开阔的技术视野;

  4. 在各种约束条件下权衡选择的能力,原则。