大部分公司并不需要微服务

本来之前标题的名字是不要被技术buzzword误导,觉得还是得标题党一些,:),请大家谅解,技术圈时长会不断的产生一些新的buzzword,很容易被误导,最可怕的是一些技术团队在没搞明白的情况下,就按buzzword去做或者去靠拢,好像生怕如果自己做的技术和buzzword不相关或者不一样,就很low一样,感觉这现象在技术圈太常见了,有些看的不太爽,写篇文章来讲讲自己的观点。

作为技术圈的我们大家,对各种buzzword一定要慎重,了解它产生的背景,可执行的各种前提和条件,技术始终是为公司的战略而服务的,buzzword是不是真的给你的场景带来了帮助,要想清楚,拿时下比较流行的微服务、AI、AR/VR来说说吧。

说到微服务这个buzzword,必须承认到现在为止我都没搞明白和服务化的区别,我都搞不太清楚淘宝在2008年做的服务化改造后形成的SOA体系到底是不是和现在的这个buzzword就是一回事,在各种文章里,微服务简直就被宣传的像是技术界一些场景的救世主,直接误导了很多同学上来就必搞微服务体系,但不知道有多少同学仔细想过有没有必要,对业务发展来说采用微服务到底是帮助还是变成了阻碍,在互联网类型快速迭代的业务中,业务的迭代效率是核心问题,以我自己的认知,对服务化我的观点一直是如果能不进这个坑,最好不进,一个单一应用的复杂度远比N个应用组成的分布式系统简单、快速多了,一旦进入分布式的坑,在技术上就不得不有比较大的投入,而对于一些还处于中小规模的公司而言,我觉得完全没有必要,Google的Jeff Dean在一次分享时讲到他对于Google做服务化的观点:让Google具备了千人并行协作开发的能力,在看到这观点以前,我一直觉得服务化重点解的是水平伸缩能力的问题,其次是并行协作的问题,但我现在基本更加赞同服务化重点是让一家公司具备了百人以上的并行协作开发能力,我认为在几十个研发同学的情况下,并行协作开发不会成为太大问题,这个时候的并行协作上的一些投入会远比进入服务化后的投入小很多,所以以前有一些朋友问我公司到底要不要改变为服务化时,我都问两问题:1. 公司研发团队现在总共多少人? 2. 目前的水平伸缩瓶颈是? 如果在这两个问题上服务化并不是核心的瓶颈,或者只需要付出少量的人或机器代价就可以解决,我会强烈建议不要做服务化,所以拜托受微服务这个buzzword诱惑的同学们,请大家在采用这样的架构前一定,千万要慎重思考,策略应该是以尽量不采用去推导会产生的代价和问题,如果这个代价和问题并不是那么大,就不要用,除非真的万不得已,那就请做好组织、团队人员方面的布局,以真正的做好服务化,不要让这个东西最后变成业务发展的障碍。

说说AI这个buzzword,我拿运维这个领域来举例说吧,AI实在是太火了,同样导致了运维界很多的工作也恨不得赶紧和AI绑上关系,当然不可否认的是,在运维这个领域,AI绝对是可以产生巨大帮助的,但首先要想明白的是你的整个环境真的为AI做好了准备吗?没想清楚这个问题,很容易最后出现一个状况是,各种算法,智能动作等等都准备好了,结果是基础的技术层面或环境层面压根就不具备这个能力,一切白扯,例如在运维这个领域,我认为要引入AI让其发挥作用,前提是要先把数据化、自动化、无人化做好,如果连这些都没做好,千万别先跳进AI的坑,AI通常依赖大量的数据去智能化的执行动作,这种情况下没有数据,不能自动执行,自动执行过程中需要人介入,那都意味着没法玩,很典型的在运维领域的一个case,容量的弹性伸缩,如果连判断一个应用容量够不够的数据都不充分,连应用能自动部署和启动都做不到,那先做了一个弹性伸缩的系统又有什么用呢?所以我更赞同的是AI确实是前景,但首先要把AI需要的一些前提给做好了,然后再进坑,千万别走反了。

最后说说同样极度火爆的AR/VR,AR/VR是个非常复杂的话题,同样很多的业务一冲动就决定投入大量资源去玩这两个方向,觉得不玩就挂了,但在玩这两个方向前,同样要想清楚的核心问题是,对于你的业务场景而言,AR/VR的一些技术普及的条件是不是都具备了,例如你生产了AR/VR内容,但现在的AR/VR的用户数,AR/VR设备的情况是不是真的到了值得你投入大量资源去做,君不见很多创业公司在做app的时候都先只做ios版本。

所以总的来说,就是技术圈的各种流行buzzword呢,当然会有它一定的道理,但是不是真的要去采用,千万别纯粹跟风,或者纯粹从技术角度判断,仔细的思考如果要采用buzzword,会发生什么,要做好什么样的准备,能获得什么样的收益(这和每家公司的战略地位,业务发展情况直接相关),毕竟一家公司的资源都是有限的,进了一个坑就意味着另一个坑可投入的资源少了。

系统设计的核心:设计原则

各种系统设计文档中,都会有专门的设计原则这个篇章,我记得我在几年前写系统设计文档时,都会觉得这个部分没什么可写的,通常会随便写上几条类似松耦合的原则,并且在之后的概要、详细设计中也不会有什么和设计原则这个部分太相关的部分,这种情况下就会越来越觉得设计原则这个小章节可写可不写,随着近几年从做单一系统到更广的多团队协作的大系统设计后,对设计原则这个章节需要传达的信息有了更多的理解,这篇文章就来谈谈自己的一些感受。

设计原则并不是什么空话,我认为设计原则表述的是架构师对整个系统的核心设计思想,并且要求把这个设计思想贯穿到所有子系统的概要/详细设计中,所以在这些子系统的概要/详细设计中要充分体现出对设计原则的考虑。

对于系统而言,什么才是设计思想呢,每个架构师在做系统的设计之前一般是会有思考的,思考的内容基本就会是要实现需求核心的几个点是什么,这些核心的点就是设计思想,举两个我自己做的设计的例子来说下,会更容易理解一些。

1. 当年在做T4(基于LXC的“虚拟化”产品)时,T4相对以前的Xen,是一个全新的替代产品,在思考T4的设计时,除了一些技术选型外,很重要的一点我判断是对用户透明,对用户而言要做到用的是T4,还是Xen,都感受不到区别,这一点必须贯穿到T4所有部分的设计中,因此这一点就是我当时列入设计原则的。

2. 前几年在做异地多活的设计时,异地多活中最重要的特色是多个异地的机房的数据库都是可写的,在这种情况下在设计的时候要考虑的重点我认为是数据正确性的问题,怎么保证用户数据不会写错乱是关键,所以在设计原则上我写入了数据正确性这条,这样确保了后续在整个跨多个技术领域的子系统的设计中都能仔细考虑数据正确性如何去做到。

3. 还有在做某些系统的设计时,平滑迁移我认为是项目能成功的关键因素,所以在设计原则上我也会写上平滑迁移,确保各个子系统在设计时会把如何从现有迁移到新的结构上放入关键。

从上面的几个例子可以看到,设计原则不仅仅需要表达设计中的一些非常技术层面的共同点(例如关键路径/非关键路径的划分、非关键路径的异步化等),更重要的是表达风险控制要素和优先级,确保在多人合作时整个项目的技术、时间风险的可控,对于一个架构师而言,如何控制好项目在设计、实现时的技术/时间风险,是我一直认为的最为关键的能力,也是我在观察很多架构师时最为看重的。

对于一个大型的项目而言,由于是多团队合作,设计思想的有效传达是非常重要的,设计思想传达的是架构师对整个系统设计的核心思考(不仅仅是结果,更是思考过程),这个思考的表达会非常有助于确保在多个架构师合作的情况下整个系统设计的一致性,以及项目核心目标的完成(之前另外一篇文章的多个团队技术方案冲突的决策原则对系统设计也是非常关键的,相同的是都是把架构师做决定的背后的原因讲清楚,以便多人协作,仅传达结果没有过程的那种是很难真正达成一致和留下深刻印象的),不过即使是小的单一系统的设计,就算架构师是同一个人,设计原则这块也需要陈述清楚,以确保在做各子模块设计时能遵守,同时也是让实现各部分代码的同学能更容易理解设计。

=============================
欢迎关注微信公众号:hellojavacases

公众号上发布的消息都存放在http://hellojava.info上。

多个团队的技术方案冲突,怎么决策

作为一个架构师或技术Leader而言,技术方案的决策是常见的要做的事,毕竟很多时候并不会只有一条路能走到目的地,这个时候到底怎么决策很容易成为一个巨大的纠结点,在涉及多团队合作的情况下,甚至有可能会成为block整件事进展的关键因素,这篇文章就来聊聊这个。

自从我开始做一些比较大的跨多团队的基础技术项目后,就会经常面临一些技术方案的决策的事,从一开始的不知所措,痛苦无比,到现在,也算是积累了一些经验和方法,技术方案的决策上,最难的其实就在于可能多个技术方案都是可以走到目的地的,这个时候怎么选择,尤其是多个团队产生冲突的时候,怎么去选择就更加复杂化了。

对于一个由多个技术团队共同完成的大的技术方案而已,会出现两种情况。

一种情况比较简单,就是每个团队各尽其职,分工清晰,这个时候大的技术方案通常不会产生太多的冲突,可能会产生的就是A团队对B团队所负责的部分的技术方案有质疑,这种情况下通常其实不会太难办,大的技术方案是需要一个大架构师的,这个架构师需要定义出整个技术方案的设计原则(之后准备写一篇什么是看起来很虚但其实是核心的设计原则),只要在遵循了设计原则的情况下,我认为B团队所负责的部分的技术方案其他团队就不需要去质疑,谁负责哪块谁决定哪块的技术方案。

另外一种情况就会比较复杂,就是这个大的技术方案中,有所做的部分重叠的不同团队,这个时候就非常容易产生技术冲突,不同团队很有可能会给出不同的方案,这个时候作为整个大技术方案的owner或架构师,怎么去决策就是巨大挑战了,毕竟就算大技术方案的架构师是独裁的,但独裁的还是要有些道理的,不能完全靠行政手段,在这样的情况下,我的观点是这个时候的选择可以基于以下几点考虑去做:
1. 设计原则
多年前在写设计文档中的设计原则时,总是会觉得没什么值得写的,松耦合等等经常会成为常用词,但貌似然并卵,近几年才越来越明白其实设计原则是设计文档中的精髓所在,看起来通常设计原则会很短的几条,但好的架构师会通过这几条控制好整个项目的技术风险,确保各团队的技术方案不偏离关键航道,设计原则里写的点要在每部分的技术方案中贯穿,否则就和没写没什么区别,更形象一点说就是设计原则是在排技术方案细节中关注的重点的优先级,例如有些项目中平滑切换是最高优先级,有些项目中技术创新是最高优先级,设计原则这个部分很值得专门写篇文章,因此这里就不再展开了。
在多个冲突的技术方案选择时,设计原则是其中关键的衡量因素。

2. 团队分工、核心价值和定位
对于产生冲突的团队,需要站在公司角度来做一个判断,产生冲突的部分的技术要做成,核心成功的关键技术点到底掌握在哪个团队手里,还有是对于哪个团队而言是更为核心的价值,只有是那个团队的核心价值才能在人力投入,未来持续发展上有保障,每个团队在一个大的组织里都是有分工的,分工也对应到了其核心价值,在多个冲突的技术方案选择时,一定要考虑这个关键的衡量因素,显然在考虑这个因素的时候要非常中立,站在公司层面而不是小团队层面来考虑,以便确保最后的决策是符合组织对每个团队分工的期望的。
举我自己经历过的两个case来说说。
Case I: 在某个大的技术方案里,有两个团队在某个技术点上都有自己的实现方案,但我最后的决策是选择了当时相对而言反而更不成熟的一方,原因就是我的判断是这个技术点要做成,核心技术其实在这个当时还不是那么成熟的团队手里,
Case II: 在某个大的技术方案里,同样也是几个团队在同一个技术点上都有自己的方案,我最后选择的那个团队基于的判断是:我相信对于公司而言,那个团队才是应该掌控这项技术的组织。

3. 团队能力状况
除了团队分工外,团队的能力状况也是关键,有可能会出现一种状况是从组织层面考虑而言,是应该在B团队做最合适,但B团队的能力在当前可能不具备,反而是另外一个团队更具备,这个时候需要做的决策会是由另外一个团队承担起来,但同时逐步的操作组织层面的变化,确保在整个大的技术方案落地后,以后有持续发展的保障。

从上面这三个衡量因素可以看出,一个大技术方案的owner或架构师,光有技术是远远不够的,在多个冲突的技术方案选择时,更主要的不是评判技术方案的优劣,一旦陷入纯粹技术方案的优劣之争,其实是很难有结果的,越大的项目越是如此,通常来说其实方案可能没有优劣之分(当然,少数冲突的情况下是有明显的优劣之分的),而是哪个更适合的问题,甚至有些时候不同方案在最终多次迭代后走向的是同一方案,只是路径不同,和语言之争类似,所以作为大技术方案的决策者,首先要做的不是去评判技术方案的优劣,而是大家先一起对齐目标、设计原则,然后清晰阐述自己做选择的因素,最后做出决策,当然因为因素毕竟有主观性,要完全得到认可仍然是不容易的,但至少让参与大项目的大伙们清楚的知道你决策的原因,这样大家才能更好的理解整个技术方案,确保最终项目的实现和落地。

大家也来聊聊你碰到过的冲突?作为非决策者的感受?又或是作为决策者你最终的选择和感受?

=============================
欢迎关注微信公众号:hellojavacases

公众号上发布的消息都存放在http://hellojava.info上。

分布式领域架构师要掌握的技术

分布式系统无疑是持久的热门话题,但其实如果不是一定有必要,强烈建议不要进入分布式领域,在集中式的情况下很多问题都会简单不少,技术人员千万不要因为外界火热的例如微服务,就把自己的产品的也去做改造,一定要仔细判断是否有必要,不要为了技术而技术,那么在必须分布式的情况下(访问量、存储量或开发人数),一个分布式领域的合格的架构师要掌握哪些技术呢,这篇文章就聊聊这个话题。

简单重复下我对架构师的标准,一个架构师最重要的不是画几个框,连几条线(这是基本要求),而是控制技术风险,要控制技术风险显然不是看几个结构性的ppt就能学会的。

通信
既然是分布式系统,系统间通信的技术就不可避免的要掌握。
首先要掌握一些基础知识,例如网络通信协议(诸如TCP/UDP等等)、网络IO(Blocking-IO,NonBlocking-IO、Asyn-IO)、网卡(多队列等);更偏应用的层面,需要了解例如连接复用、序列化/反序列化、RPC、负载均衡等。
学了这些基本知识后,基本上可以写一个简单的分布式系统里的通信模块,但这其实远远不够,既然进入了分布式领域,对规模其实就已经有了不低的要求,通常也就意味着需要的是能支持大量连接、高并发、低资源消耗的通信程序。

大量的连接通常会有两种方式:
1. 大量client连一个server
在现如今NonBlocking-IO这么成熟的情况下,一个支持大量client的server已经不那么难写了,但在大规模,并且通常长连接的情况下,有一个点要特别注意,就是当server挂掉的时候,不能出现所有client都在一个时间点发起重连,那样基本就是灾难,在没有经验的情况下我看过好几起类似的case,到client规模上去后,server一重启基本就直接被冲进来的大量建连冲垮了(当然,server的backlog队列首先应该稍微设置大一些),通常可以采用的方法是client重连前都做随机时间的sleep,另外就是重连的间隔采取避让算法。

2. 一个client连大量的server
有些场景也会出现需要连大量server的现象,在这种情况下,同样要注意的也是不要并发同时去建所有的连接,而是在能力范围内分批去建。
除了建连接外,另外还要注意的地方是并发发送请求也同样,一定要做好限流,否则很容易会因为一些点慢导致内存爆掉。

这些问题在技术风险上得考虑进去,并在设计和代码实现上体现,否则一旦随着规模上去了,问题一时半会还真不太好解。

高并发这个点需要掌握CAS、常见的lock-free算法、读写锁、线程相关知识(例如线程交互、线程池)等,通信层面的高并发在NonBlocking-IO的情况下,最重要的是要注意在整体设计和代码实现上尽量减少对io线程池的时间占用。

低资源消耗这点的话NonBlocking-IO本身基本已经做到。

伸缩性
分布式系统基本就意味着规模不小了,对于这类系统在设计的时候必须考虑伸缩性问题,架构图上画的任何一个点,如果请求量或者是数据量不断增大,怎么做到可以通过加机器的方式来解决,当然,这个过程也不用考虑无限大的场景,如果经历过从比较小到非常大规模的架构师,显然优势是不小的,同样也会是越来越稀缺的。

伸缩性的问题围绕着以下两种场景在解决:
1. 无状态场景
对于无状态场景,要实现随量增长而加机器支撑会比较简单,这种情况下只用解决节点发现的问题,通常只要基于负载均衡就可以搞定,硬件或软件方式都有;
无状态场景通常会把很多状态放在db,当量到一定阶段后会需要引入服务化,去缓解对db连接数太多的情况。
2. 有状态场景
所谓状态其实就是数据,通常采用Sharding来实现伸缩性,Sharding有多种的实现方式,常见的有这么一些:
2.1 规则Sharding
基于一定规则把状态数据进行Sharding,例如分库分表很多时候采用的就是这样的,这种方式支持了伸缩性,但通常也带来了很复杂的管理、状态数据搬迁,甚至业务功能很难实现的问题,例如全局join,跨表事务等。
2.2 一致性Hash
一致性Hash方案会使得加机器代价更低一些,另外就是压力可以更为均衡,例如分布式cache经常采用,和规则Sharding带来的问题基本一样。
2.3 Auto Sharding
Auto Sharding的好处是基本上不用管数据搬迁,而且随着量上涨加机器就OK,但通常Auto Sharding的情况下对如何使用会有比较高的要求,而这个通常也就会造成一些限制,这种方案例如HBase。
2.4 Copy
Copy这种常见于读远多于写的情况,实现起来又会有最终一致的方案和全局一致的方案,最终一致的多数可通过消息机制等,全局一致的例如zookeeper/etcd之类的,既要全局一致又要做到很高的写支撑能力就很难实现了。

即使发展到今天,Sharding方式下的伸缩性问题仍然是很大的挑战,非常不好做。

上面所写的基本都还只是解决的方向,到细节点基本就很容易判断是一个解决过多大规模场景问题的架构师,:)

稳定性
作为分布式系统,必须要考虑清楚整个系统中任何一个点挂掉应该怎么处理(到了一定机器规模,每天挂掉一些机器很正常),同样主要还是分成了无状态和有状态:
1. 无状态场景
对于无状态场景,通常好办,只用节点发现的机制上具备心跳等检测机制就OK,经验上来说无非就是纯粹靠4层的检测对业务不太够,通常得做成7层的,当然,做成7层的就得处理好规模大了后的问题。
2. 有状态场景
对于有状态场景,就比较麻烦了,对数据一致性要求不高的还OK,主备类型的方案基本也可以用,当然,主备方案要做的很好也非常不容易,有各种各样的方案,对于主备方案又觉得不太爽的情况下,例如HBase这样的,就意味着挂掉一台,另外一台接管的话是需要一定时间的,这个对可用性还是有一定影响的;
全局一致类型的场景中,如果一台挂了,就通常意味着得有选举机制来决定其他机器哪台成为主,常见的例如基于paxos的实现。

可维护性
维护性是很容易被遗漏的部分,但对分布式系统来说其实是很重要的部分,例如整个系统环境应该怎么搭建,部署,配套的维护工具、监控点、报警点、问题定位、问题处理策略等等。

从上面要掌握的这些技术,就可以知道为什么要找到一个合格的分布式领域的架构师那么的难,何况上面这些提到的还只是通用的分布式领域的技术点,但通常其实需要的都是特定分布式领域的架构师,例如分布式文件系统、分布式cache等,特定领域的架构师需要在具备上面的这些技术点的基础上还具备特定领域的知识技能,这就更不容易了。

随着互联网的发展,分布式领域的很多技术都在成熟化,想想在8年或9年前,一个大规模的网站的伸缩性是怎么设计的还是很热门的探讨话题,但是到了今天基本的结构大家其实都清楚,并且还有很多不错的系统开源出来,使得很多需要经验的东西也被沉淀下去了,在有了各种不错的开源产品的支撑下以后要做一个分布式系统的难度一定会越来越大幅降低,云更是会加速这个过程。

ps: 在写这篇文章的过程中,发现要判断一个技术人的功底有多厚,其实还真不难,就是请TA写或者讲自己觉得懂的所有技术,看看能写多厚或讲多久…要写厚或讲很久其实都不容易,尽管我也不否认要很简洁的写明白或讲清楚也不容易,但一定是先厚然后薄。

=============================
欢迎关注微信公众号:hellojavacases

公众号上发布的消息都存放在http://hellojava.info上。

我在系统设计上犯过的14个错

在上篇《架构师画像》的文章中提到了自己在系统设计上犯过的一些错,觉得还挺有意义的,这篇文章就来回顾下自己近八年来所做的一些系统设计,看看犯的一些比较大的血淋淋的错误(很多都是推倒重来),这八年来主要做了三个基础技术产品,三个横跨三年的大的技术项目(其中有两个还在进行中),发现大的错误基本集中在前面几年,从这个点看起来能比较自豪的说在最近的几年在系统设计的掌控上确实比以前成熟了很多。

第1个错
在设计服务框架时,我期望服务框架对使用者完全不侵入,于是做了一个在外部放一个.xml文件来描述spring里的哪些bean发布为服务的设计,这个版本发布后,第一个小白鼠的用户勉强在用,但觉得用的很别扭,不过还是忍着用下去了,到了发布的时候,发现出现了两个问题,一是这个xml文件研发也不知道放哪好,所以到了发布的时候都不知道去哪拿这个xml文件。
这个设计的关键错误就在于在设计时没考虑过这个设计方式对研发阶段、运维阶段的影响,后来纠正这个错误的方法是去掉了这个xml文件,改为写了一个Spring FactoryBean,用户在spring的bean配置文件中配置下就可以。
因此对于一个架构师来说,设计时在全面性上要充分考虑。

第2个错
服务框架在核心应用上线时,出现了前端web应用负载高,处理线程数不够用的现象,当时处理这个故障的方式是回滚了服务框架的上线,这个故障排查了比较长的时间后,查到的原因是服务框架用的JBoss Remoting在通信时默认时间是60s,导致一些处理速度慢的请求占据了前端web应用的处理线程池。
上面这里故障的原因简单来说是分布式调用中超时时间太长的问题,但更深层次来思考,问题是犯在了设计服务框架时的技术选型,在选择JBoss-Remoting时没有充分的掌握它的运行细节,这个设计的错误导致的是后来决定放弃JBoss-Remoting,改为基于Mina重写了服务框架的通信部分,这里造成了服务框架的可用版本发布推迟了两个多月。
因此对于一个架构师来说,在技术选型上对技术细节是要有很强的掌控力的。

第3个错
在服务框架大概演进到第4个版本时,通信协议上需要做一些改造,突然发现一个问题是以前的通信协议上是没有版本号的,于是悲催的只能在代码上做一个很龌蹉的处理来判断是新版本还是老版本。
这个设计的错误非常明显,这个其实只要在最早设计通信协议时参考下现有的很多的通信协议就可以避免了,因此这个错误纠正也非常简单,就是参考一些经典的协议重新设计了下。
因此对于一个架构师来说,知识面的广是非常重要的,或者是在设计时对未来有一定的考虑也是非常重要的。

说到协议,就顺带说下,当时在设计通信协议和选择序列化/反序列化上没充分考虑到将来多语言的问题,导致了后来在多语言场景非常的被动,这也是由于设计时前瞻性的缺失,所谓的前瞻性不是说一定要一开始就把未来可能会出现的问题就解掉,而是应该留下不需要整个改造就可以解掉的方法,这点对于架构师来说也是非常重要的。

第4个错
在服务框架切换为Mina的版本上线后,发布服务的应用重启时出现一个问题,就是发现重启后集群中的机器负载严重不均,排查发现是由于这个版本采用是服务的调用方会通过硬件负载均衡去建立到服务发布方的连接,而且是单个的长连接,由于是通过硬件负载均衡建连,意味着服务调用方其实看到的都是同一个地址,这也就导致了当服务发布方重启时,服务调用方重连就会集中的连到存活的机器上,连接还是长连,因此就导致了负载的不均衡现象。
这个设计的错误主要在于没有考虑生产环境中走硬件负载均衡后,这种单个长连接方式带来的问题,这个错误呢还真不太好纠正,当时临时用的一个方法是服务调用方的连接每发送了1w个请求后,就把连接自动断开重建,最终的解决方法是去掉了负载均衡设备这个中间点。
因此对于一个架构师来说,设计时的全面性要非常的好,我现在一般更多采用的方式是推演上线后的状况,一般来说在脑海里过一遍会比较容易考虑到这些问题。

第5个错
服务框架在做了一年多以后,某个版本中出现了一个严重bug,然后我们就希望能通知到用了这个版本的应用紧急升级,在这个时候悲催的发现一个问题是我们压根就不知道生产环境中哪些应用和机器部署了这个版本,当时只好用一个临时的扫全网机器的方法来解决。
这个问题后来纠正的方法是在服务发布和调用者在连接我们的一个点时,顺带把用的服务框架的版本号带上,于是就可以很简单的知道全网的服务框架目前在运行的版本号了。
因此对于一个架构师来说,设计时的全面性是非常重要的,推演能起到很大的帮助作用。

第6个错
服务框架这种基础类型的产品,在发布时会碰到个很大的问题,就是需要通知到使用者去发布,导致了整个发布周期会相当的长,当时做了一个决定,投入资源去实现完全动态化的发布,就是不需要重启,等到做的时候才发现这完全就是个超级大坑,最终这件事在投入两个人做了接近半年后,才终于决定放弃,而且最终来看其实升级的问题也没那么大。
这个问题最大的错误在于对细节把握不力,而且决策太慢。
因此对于一个架构师来说,技术细节的掌控非常重要,同时决策力也是非常重要的。

第7个错
服务发布方经常会碰到一个问题,就是一个服务里的某些方法是比较耗资源的,另外的一些可能是不太耗资源,但对业务非常重要的方法,有些场景下会出现由于耗资源的方法被请求的多了些导致不太耗资源的方法受影响,这种场景下如果要去拆成多个服务,会导致开发阶段还是挺痛苦的,因此服务框架这边决定提供一个按方法做七层路由的功能,服务的发布方可以在一个地方编写一个规则文件,这个规则文件允许按照方法将生产环境的机器划分为不同组,这样当服务调用方调用时就可以做到不同方法调用到不同的机器。
这个功能对有些场景来说用的很爽,但随着时间的演进和人员的更换,能维护那个文件的人越来越少了,也成为了问题。
这个功能到现在为止我自己其实觉得也是一直处于争议中,我也不知道到底是好还是不好…
因此对于一个架构师来说,设计时的全面性是非常重要的。

第8个错
服务框架在用的越来越广后,碰到了一个比较突出的问题,服务框架依赖的jar版本和应用依赖的jar版本冲突,服务框架作为一个通用技术产品,基本上没办法为了一个应用改变服务框架自己依赖的jar版本,这个问题到底怎么去解,当时思考了比较久。
可能是由于我以前OSGi这块背景的原因,在设计上我做了一个决定,引入OSGi,将服务框架的一堆jar处于一个独立的classloader,和应用本身的分开,这样就可以避免掉jar冲突的问题,在我做了引入OSGi这个决定后,团队的1个资深的同学就去做了,结果是折腾了近两个月整个匹配OSGi的maven开发环境都没完全搭好,后来我自己决定进去搞这件事,即使是我对OSGi比较熟,也折腾了差不多1个多月才把整个开发的环境,工程的结构,以及之前的代码基本迁移为OSGi结构,这件事当时折腾好上线后,效果看起来是不错的,达到了预期。
但这件事后来随着加入服务框架的新的研发人员越来越多,发现多数的新人都在学习OSGi模式的开发这件事上投入了不少的时间,就是比较难适应,所以后来有其他业务问是不是要引入OSGi的时候,我基本都会建议不要引入,主要的原因是OSGi模式对大家熟悉的开发模式、排查问题的冲击,除非是明确需要classloader隔离、动态化这两个点。
让我重新做一个决策的话,我会去掉对OSGi的引入,自己做一个简单的classloader隔离策略来解决jar版本冲突的问题,保持大家都很熟悉的开发模式。
因此对于一个架构师来说,设计时的全面性是非常重要的。

第9个错
服务框架在用的非常广了后,团队经常会被一个问题困扰和折腾,就是业务经常会碰到调用服务出错或超时的现象,这种情况通常会让服务框架这边的研发来帮助排查,这个现象之所以查起来会比较复杂,是因为服务调用通常是多层的关系,并不是简单的A–>B的问题,很多时候都会出现A–>B–>C–>D或者更多层的调用,超时或者出错都有可能是在其中某个环节,因此排查起来非常麻烦。
在这个问题越来越麻烦后,这个时候才想起在09年左右团队里有同学看过G家的一篇叫dapper的论文,并且做了一个类似的东西,只是当时上线后我们一直想不明白这东西拿来做什么,到了排查问题这个暴露的越来越严重后,终于逐渐想起这东西貌似可以对排查问题会产生很大的帮助。
到了这个阶段才开始做这件事后,碰到的主要不是技术问题,而是怎么把新版本升级上去的问题,这个折腾了挺长时间,然后上线后又发现了一个新的问题是,即使服务框架具备了Trace能力,但服务里又会调外部的例如数据库、缓存等,那些地方如果有问题也会看不到,排查起来还是麻烦,于是这件事要真正展现效果就必须让Trace完全贯穿所有系统,为了做成这件事,N个团队付出了好几年的代价。
因此对于一个架构师来说,设计时的全面性、前瞻性非常重要,例如Trace这个的重要性,如果在最初就考虑到,那么在一开始就可以留好口子埋好伏笔,后面再要做完整就不会太复杂。

第10个错
服务的发布方有些时候会碰到一个现象是,服务还没完全ready,就被调用了;还有第二个现象是服务发布方出现问题时,要保留现场排查问题,但服务又一直在被调用,这种情况下就没有办法很好的完全保留现场来慢慢排查问题了。
这两个现象会出现的原因是服务框架的设计是通过启动后和某个中心建立连接,心跳成功后其他调用方就可以调用到,心跳失败后就不会被调到,这样看起来很自动化,但事实上会导致的另外一个问题是外部控制上下线这件事的能力就很弱。
这个设计的错误主要还是在设计时考虑的不够全面。
因此对于一个架构师来说,设计时的全面性非常重要。

第11个错
在某年我和几个小伙伴决定改变当时用xen的模式,换成用一种轻量级的“虚拟机”方式来做,从而提升单机跑的应用数量的密度,在做这件事时,我们决定自己做一个轻量级的类虚拟机的方案,当时决定的做法是在一个机器上直接跑进程,然后碰到一堆的问题,例如从运维体系上来讲,希望ssh到“机器”、独立的ip、看到自己的系统指标等等,为了解决这些问题,用了N多的黑科技,搞得很悲催,更悲催的是当时觉得这个问题不多,于是用一些机器跑了这个模式,结果最后发现这里面要黑科技解决的问题实在太多了,后来突然有个小伙伴提出我们试用lxc吧,才发现我们之前用黑科技解的很多问题都没了,哎,然后就是决定切换到这个模式,结果就是线上的那堆机器重来。
这个设计的主要错误在于知识面不够广,导致做了个不正确的决定,而且推倒重来。
因此对于一个架构师来说,知识面的广非常重要,在技术选型这点上非常明显。

第12个错
还是上面这个技术产品,这个东西有一个需求是磁盘空间的限额,并且要支持磁盘空间一定程度的超卖,当时的做法是用image的方式来占磁盘空间限额,这个方式跑了一段时间觉得没什么问题,于是就更大程度的铺开了,但铺开跑了一段时间后,出现了一个问题,就是经常出现物理机磁盘空间不足的报警,而且删掉了lxc容器里的文件也还是不行,因为image方式只要占用了就会一直占着这个大小,只会扩大不会缩小。
当时对这个问题极度的头疼,只能是删掉文件后,重建image,但这个会有个要求是物理机上有足够的空间,即使有足够的空间,这个操作也是很折腾人的,因为得先停掉容器,cp文件到新创建的容器,这个如果东西多的话,还是要耗掉一定时间的。
后来觉得这个模式实在是没法玩,于是寻找新的解决方法,来满足磁盘空间限额,允许超卖的这两需求,最后我们也是折腾了比较长一段时间后终于找到了更靠谱的解决方案。
这个设计的主要错误还是在选择技术方案时没考虑清楚,对细节掌握不够,考虑的面不够全,导致了后面为了换掉image这个方案,用了极大的代价,我印象中是一堆的人熬了多次通宵来解决。
因此对于一个架构师来说,知识面的广、对技术细节的掌控和设计的全面性都非常重要。

第13个错
仍然是上面的这个技术产品,在运行的过程中,突然碰到了一个虚拟机中线程数创建太多,导致其他的虚拟机也创建不了线程的现象(不是因为物理资源不够的问题),排查发现是由于尽管lxc支持各个容器里跑相同名字的账号,但相同名字的账号的uid是相同的,而max processes是限制在UID上的,所以当一个虚拟机创建的线程数超过时,就同样影响到了其他相同账号的容器。
这个问题我觉得一定程度也可以算是设计问题,设计的时候确实由于对细节掌握的不够,考虑的不全导致忽略了这个点。
因此对于一个架构师来说,对技术细节的掌控和设计的全面性都非常重要。

第14个错
在三年前做一个非常大的项目时,项目即将到上线时间时,突然发现一个问题是,有一个关键的点遗漏掉了,只好赶紧临时讨论方案决定怎么做,这个的改动动作是非常大的,于是项目的上线时间只能推迟,我记得那个时候紧急周末加班等搞这件事,最后带着比较高的风险上了。
这个问题主要原因是在做整体设计时遗漏掉了这个关键点的考虑,当时倒不是完全忽略了这个点,而是在技术细节上判断错误,导致以为不太要做改动。
因此对于一个架构师来说,对技术细节的掌控是非常重要的,这里要注意的是,其实不代表架构师自己要完全什么都很懂,但架构师应该清楚在某个点上靠谱的人是谁。

=============================
欢迎关注微信公众号:hellojavacases

关于此微信号:
分享Java问题排查的Case、Java业界的动态和新技术、Java的一些小知识点Test,以及和大家一起讨论一些Java问题或场景,这里只有Java细节的分享,没有大道理、大架构和大框架。

公众号上发布的消息都存放在http://hellojava.info上。

架构师画像

架构师,这个title就和总监之类的title一样,已经彻底被用烂了,但在一个软件产品的生命周期中,架构师是实实在在的一个极度重要的角色,这篇文章就来讲讲我觉得的架构师的画像,到底具备什么素质的同学是贴合架构师形象的,同时欢迎大家回复下在你心目中NB的架构师的画像是怎么样的呢。

业务理解和抽象能力
架构师的第一职责是理解业务,并转换为可被研发理解的实现方案,因此业务理解能力是架构师的必备技能,通常来说一个资深的业务架构师,对业务会有非常深的认识和积累,一个极好的业务架构师应该能大概预判业务未来的发展趋势,以便在系统的可扩展性上留好一定的空间,所以也会很自然的出现有些业务架构师做着做着就干脆转为PD类型的角色。
抽象能力是通过对业务的理解转换为系统实现的模型,这显然也是重要的能力,抽象很多时候也承担了分解清楚多个团队的职责,分工清晰化。

NB的代码能力
之所以现在很多的架构师都会被认为是大忽悠,就是有一堆顶着架构师头衔,又不干活的人(甚至会出现对技术几乎不太懂的人),光说不干,再加上说的不靠谱的话自然很容易被认为是大忽悠,就我自己而言,我一直认为架构师有个非常重要的职责是编写整个系统中核心部分的代码,这个部分并一定是技术挑战最高的,但对整个系统的质量/成败与否是具备非常关键的控制作用的,所以架构师必须是从写核心代码的人中诞生出来的。
在一个跨多领域的大型系统中,架构师不太可能什么都擅长,不可能写各个部分的核心代码,这种时候架构师一定要知道怎么判断非自己知识领域的部分实现是否OK,以确保各部分组合在一起的时候是符合架构设计预期的,通常这种确保各部分组织在一起work的机制部分的代码应该由架构师自己操刀。

全面
全面是一个架构师展现出来的最关键素质,全面会体现在三点上:
1. 在面对业务问题上,架构师脑海里是否会浮现出多种技术方案,这点其实挺重要的,否则可能就会出现明明有一个简单成熟的方案,但由于不知道而做了其他复杂不成熟的方案,所以广阔的技术视野是架构师的必备,另外架构师不可能全部擅长,在自己不擅长的点上,需要知道找哪个专业的人是靠谱的,这点也非常重要;

2. 在做系统设计时是否考虑到了足够多的方方面面:
例如很多系统设计容易遗漏上线环节的细节,导致在上线时发现漏掉了什么考虑,临时解决或只能重来,记得有一年我做的一个设计没有考虑到上线阶段的一个细节,导致上线的时候发现由于网段的问题完全不work,并且没有临时解决方案,只好重来,系统设计不仅仅指导研发同学怎么写代码,也包括指导其他所有相关技术同学的工作;
又例如我2008年在做服务框架设计的时候,集群和集群之间通过硬件负载均衡设备来访问的,连接的方式是单个长连接,这个设计导致了运行过程中如果要发布被调用的服务方,很容易出现压力都集中在前面重启的机器上,这也是典型的整个链路没有考虑清楚造成的设计问题;
再例如2013年我在做一个比较大范围的系统改造的设计时,由于对其中一部分的软件了解的不够,判断错误,导致后来这个改造在进行过程中才发现有些需要改造的关键软件的设计做的太粗糙,最后上线进度差不多推迟了一个多月,而且那些后来补的设计都是紧急做的,风险非常高;
回顾自己设计过的软件,发现在这个点上犯的错可以讲好几天,看来我应该整理另外一篇文档《我在系统设计上犯过的xxx个错误》,里面有些其实靠一份好的系统设计模板也许就能避免掉,一份好的系统设计模板是可以帮助架构师思考全面些的。

3. 在做系统设计时是否考虑到了未来的一些发展,尽可能不要出现未来的一点变化就导致现在白干或要花大量力气来改造的现象,想当年做服务框架的时候,后来就发现由于当年做设计的时候没有考虑到将来服务调用trace的问题,导致了后来为了弥补这点花了巨大的力气(不是技术上,而是实施上)。

全面需要架构师有足够广的技术领域知识和足够多的经验积累,从全面这点就可以看到架构师的工作绝不是画几个框,连几根线那么简单。

对架构师全面这点的挑战,会随着系统的范围越大(一个系统的设计,和100个系统组成的大系统的设计挑战是完全不同的)而变得越难,无论是知识的广度、考虑的点的覆盖度、还是未来趋势,更复杂的情况甚至会出现架构的调整对应着组织结构的调整,这种也要考虑到,例如服务化这种大的架构改造,就意味着专职的专业领域服务团队的成立。

全局
全局观通常是指在系统设计时是否考虑到了对上下游的系统的影响,毕竟通常所设计的系统不是一个孤立的系统,如果没有足够好的全局观,有可能会导致自己的系统做完上线,其他上下游系统(尤其有些连上下游是谁,怎么用的都不知道的情况下)出现问题,这种案例同样不少。

权衡
权衡同样也是架构师极度重要的能力,或者也可以认为是决策能力,技术方案的拍板是一个架构师最重要的职责。
上面说的全面是架构师在思考时开的过程,而权衡就是收的过程,收的过程结束基本就意味着技术方案的确定,同时也确定了节奏,权衡在两点上会体现的特别突出:
1. 技术方案决策原则
通常一个问题都会有多种可解决的技术方案,怎么来决策就至关重要了,而决策通常又和全面相关,大的来说通常决策的原则就是性价比和可持续发展。
性价比简单来说是方案的实现成本,这个成本要包括非常多的方面,例如有些场景可能会是用硬件解决看起来是花钱,但最终折算成本是最划算的,很多系统设计在决策性价比时都过于随意,例如一个另外常见的场景就是建设一套新系统替代旧系统,这个时候可能完全没考虑旧系统的迁移代价甚至超过了改造旧系统的代价;
可持续发展简单来说就是所选择的技术方案在公司是否可持续,例如简单的案例是公司主体的研发人员都是php,却搞一个其他语言,且只有极少人懂的(当然,这还是要看性价比,如果搞一个其他语言带来的效益超过了语言/人才体系的更换成本),又例如引入一个开源产品,有无专业团队维护这都是要考虑的关键因素。

2. 优先级和节奏控制
经常我会问做系统设计的同学一个问题:对于这个业务场景而言,在系统设计上最需要把握的一个点是什么;这是一个关键问题,全面意味着考虑到了很多地方的问题,但通常业务需求实现都是有很强的时间要求的,因此在这个时候必须考虑清楚不同点的优先级,同时也包括技术方案在决策时也要做出取舍,有可能选了一个不是那么好的技术方案,但通过留下一些可改造的空间,为以后的重构做好铺垫,那就是很不错的,尤其技术同学有些时候比较容易陷入解决技术问题的场景去,但那个问题其实有可能不是现阶段最重要的。

其实优先级和节奏控制是我认为一个最NB的架构师的最佳体现,优先级意味着把握住了重点,可以确保在所设计的架构指导下业务实现不会出现大问题,节奏控制则意味着全面,为将来做好了铺垫。

=============================
欢迎关注微信公众号:hellojavacases

关于此微信号:
分享Java问题排查的Case、Java业界的动态和新技术、Java的一些小知识点Test,以及和大家一起讨论一些Java问题或场景,这里只有Java细节的分享,没有大道理、大架构和大框架。

公众号上发布的消息都存放在http://hellojava.info上。

运维工具真的很容易做吗

在从研发转到运维之前,我一直就觉得运维工具这东西,应该是相当简单的,但在我自己带领一个运维工具团队一年多后,完全颠覆了我自己以前对运维工具的认知,才真正明白了运维工具这东西要做好,对技术的要求其实是极高的。

之前之所以觉得运维工具简单,是因为从在线业务系统角度来看,运维工具访问量低,数据量也很小,完全看不到什么技术难点。

在带领一个运维工具团队一年多后,看到的是运维工具系统对技术的要求其实和在线业务系统只是角度不同而已,先看看运维工具系统主要承担的职责:
1. 运维操作的自动化;
2. 线上故障出现时救命操作的执行;

这两个职责决定了在运维工具系统设计时除了功能实现外需要考虑一些非功能特性:
1. 运维操作的自动化
自动化要真正做到,有一个核心的关键指标:成功率,可以想象下,如果一个自动化的运维操作的成功率只能做到60%,那对用的人来说体现出来就是10次操作失败4次,这种情况下多数会造成的结果就是用户就不用了,因为用户会觉得还不如手工操作来得快,因此运维操作要真正做到自动化,必须确保成功率,这点和在线业务系统更不一样,在线业务系统对单次操作的成功率的要求会远比运维工具系统低,而且在线业务为了确保操作的响应时间等,fail-fast是在线高可用系统的基本策略。
一个复杂的运维操作,例如应用扩容机器,和在线业务系统其实很像,也是要操作N个其他的系统,业务逻辑也很复杂,是一次巨复杂的分布式操作,要保障好成功率,就意味着在A调用B出现异常的时候,得决定后续的动作,有可能需要做重试、跳过(有些可能还需要在完成后再异步操作什么的)再等动作,因此运维工具系统在依赖出异常时的处理策略必须做的非常清楚,尽可能确保成功率。
从这点可以看到,设计运维工具系统时需要更加趋向保障单次操作的成功率上,在各种异常出现时需要有各种处理策略,这和设计大多数在线业务系统是完全不一样的。

2. 线上故障出现时救命操作的执行
线上故障出现后,通常会非常依赖运维工具系统来处理故障,例如监控、发布、切流量等等,而如果在故障出现的时候这些运维工具也出问题,那就悲剧了,记得我们在很早以前讨论系统的一个救命招怎么实现时,一开始谈到了好几个高大上的方案,但最后选择了一个极为土的方案,原因就是因为这是救命招,基本就是能不依赖就不要依赖任何其他东西。
按照这样的要求,运维工具系统中如果是对于线上故障出现时属于救命型的操作,必须确保绝对的稳定,不论是小的故障,还是大到机房的故障,救命操作的系统都得保证绝对的稳定。
而且通常来说,一个救命操作看似一个简单按钮,但背后对应的多数都是N套系统的联动,怎么保障这个看似简单的按钮完全的可信赖,背后是有大量的事情需要做的。

因此从对运维工具系统需要承担的职责分析来看,在技术上运维工具系统其实也是有相当高的要求的,怎么样能保障好成功率、救命操作简单按钮的绝对稳定可靠,是运维工具系统必须做到的。

顺带再说下规模的事,简单举一个例子是可能很多小一些的业务场景里很难碰到发布量本身会变得很大的状况,而在我们的场景里曾经多次碰到过发布系统本身支撑能力不够导致的各种临时找办法的状况,因此对于运维工具系统来说,同样也必须做到知道当前系统能够承受的压力范围,以及怎么水平扩展。

所以,小看了运维工具系统的同学们,包括从前的自己,请正视运维工具系统面临的技术挑战,有兴趣的同学欢迎一起加盟来挑战!

最后,再次推荐下6年前黄易山讲在FB时的工程管理心得中很重要的一句话:Tools Are Top Priority,文章请Tools Are Top Priority

=============================
欢迎关注微信公众号:hellojavacases

关于此微信号:
分享Java问题排查的Case、Java业界的动态和新技术、Java的一些小知识点Test,以及和大家一起讨论一些Java问题或场景,这里只有Java细节的分享,没有大道理、大架构和大框架。

公众号上发布的消息都存放在http://hellojava.info上。

打造高可用的系统

两年前曾经写过一篇《构建高可用系统的常用招数》,主要是从软件设计的角度来阐述,而自从自己从研发转到运维后,这两年来看到了更多的故障,同时更深刻的感受是要打造一个高可用的系统,不是仅仅在软件设计、研发、测试阶段保障质量就ok的,但基本事实是随着业务的发展,系统的复杂度、新加入人员的冲击都会对质量带来极大的影响,因此故障基本是不可避免的,这个时候故障的恢复能力就非常重要,而故障的恢复能力不仅仅是一个简单的操作,其涵盖的一整套体系,从系统的架构到变更到故障出现后的响应机制。

在故障应对这个领域,很容易犯的一个错误就是在故障的根源定位上投入非常大的精力,这个领域投入固然是有价值的,但更多的情况会发现这个领域投入产出比很低,因为真心太难做了,之所以很容易投入更大的精力在故障的根源定位上,是因为在传统的思维上,我们都认为只有定位到了故障的根源才能制定相应的策略来修复故障,但事实上随着系统架构的演变,完全可以有更好的办法。

首先我们看看故障发生时影响的范围大小,通常来说是以下几种:
1. 单实例
单台机器出问题,可能是各种原因,例如硬件故障,或应用出了个偶发故障。
2. 单应用集群
系统里的单个应用集群全体出现故障,出现的故障的原因同样可能多种多样,网络、应用软件bug等,都有可能。
3. 多应用集群
系统里的多个应用集群都出现故障,不过话说其实单应用集群出故障也很容易导致这种现象。
4. 单机房
单个机房整体出现故障,这种通常来说是网络或机房(例如雷击,:))出现故障。
5. 单地域
单个地域出现问题,可能大家觉得很罕见,但事实上有多种因素可能会造成,例如运营商在某片的故障等。
6. 全局
所有应用集群都出故障了,对于没有多个机房、多个地域的而言,单机房的就算全局故障了。

上面的几种故障范围出现后,是否一定要先查出原因才能恢复故障呢,那是不一定的,恢复故障和修复故障完全是两个概念,就上面几种故障范围而言,在恢复故障上可以采用这样的方法:
1. 单实例
对于单实例故障的快速恢复,可以通过改造为集群方式,对于无状态的比较容易搞定,集群+七层健康检查基本就非常有效了,可以做到在出问题时自动应对;
对于有状态的,比较复杂一点,通常需要做主备,或类似paxos的一主多备等结构来做到单个实例出问题时的自动应对。
因此通常来说,单实例的故障是完全不应该影响到业务的。
2. 单应用集群
对于单应用集群整体故障的现象,如果是非关键路径的应用,可通过自动降级或手工降级来快速恢复故障;
如为关键路径的应用,应用部署在多个机房,并且只有一个机房的有问题,那么可以通过快速的切掉这个机房的流量来恢复故障。
3. 多应用集群
和单应用集群采用同样的方法。
4. 单机房
对于单个机房出故障,如系统是多个机房部署的,那么可以采用切掉这个机房的流量来快速恢复故障,这要求系统部署在多个机房,并且具备快速切换能力。
5. 单地域
对于单地域出故障的情况,如系统能做到异地多点部署,那么同样可以采用切掉机房流量来快速恢复故障。
6. 全局
如为全局出故障,那基本上只能靠定位故障,然后来看看怎么快速恢复。

从上面这个简单的阐述可以看到,除了全局故障外,其他类型的故障在系统架构演变到一定阶段后,基本都可以做到在不定位故障的情况下快速恢复,当然要做到影响面越大的故障快速恢复,系统架构的复杂度和难度也会自然的要求更高,同时要确保不同影响范围情况下的故障快速恢复按钮能100%生效,则需要靠不断的演练来确保。

故障发生主要的一个因素是变更(对线上做的任何影响行为的动作都可以认为是变更),即使是在有强大的系统架构情况下,也需要通过靠控制变更范围来发挥出系统架构具备的故障恢复能力,例如系统已经做到了多机房部署和切换的能力,但变更时同时操作了所有机房(例如最典型的是推配置信息),那么这个时候如果出故障,就完全没办法,最多能依靠的是快速回滚等,因此在系统架构具备较强的故障快速恢复能力的情况下,也要相应的改变变更操作的流程,以控制每次的影响范围。

要发挥出这种大杀器级的故障快速恢复的能力,还有最后一点重要的就是在故障发生后要立刻尝试上面的恢复招数,而不是去分析和定位故障的原因,这需要改变习惯。

所以总的来说,要做到故障的快速恢复,其实是一个体系化的工作,非常不容易,而一旦能做到,那对系统的高可用可以起到极大的帮助,我的团队为阿里制定了一个这样的故障恢复体系的标准,感兴趣的同学可以看看QCon北京上的《分钟级故障恢复的高可用保障》的ppt。

=============================
欢迎关注微信公众号:hellojavacases

关于此微信号:
分享Java问题排查的Case、Java业界的动态和新技术、Java的一些小知识点Test,以及和大家一起讨论一些Java问题或场景,这里只有Java细节的分享,没有大道理、大架构和大框架。

公众号上发布的消息都存放在http://hellojava.info上。

Borg论文读后感

Borg之前号称是G家内部和PageRanking可以相提并论的同等重量级的东西,在之前终于是对外发论文了,这篇论文一出就引起了很多人的关注,我的团队在这个方面也做了几年,尽管之前从各种渠道也对Borg有一定了解,但论文揭露的很多内容还是我以前都完全不知道的,个人觉得这篇论文还算挺实在的,G家把自己的很多数据、经验都公开出来了,所以确实值得做资源管理和调度的同学看看。

G家内部所有要放到生产环境运行的东西都称为任务,任务到底放到哪台机器执行由Borg来调度和管理,因此Borg掌握了所有生产环境资源的管理,管理的高效就可以极大程度的节省成本,而这也是Borg出名的原因。

论文里讲到的以下一些内容是我自己印象比较深刻的,同样也是我认为一套优秀的资源管理平台很需要学习的:

1. Prod & Non-Prod Tasks
很早以前就知道Borg是把离线的任务和在线的任务放在一起跑的,当时有点好奇的就是那borg是怎么区分像Gmail之类在线应用的,论文里的Prod(Production)和Non-Prod的划分就直接回答了这个问题。

2. Jobs and tasks
每个Job的描述里可以有很多约束性的一些描述,例如操作系统版本,处理器的架构等,这个真心比我们现在内部的资源分配系统强不少,通用能力自然也就好很多,job通过优先级来做资源抢占,级别划分为:monitoring,production,batch and best effort(also known as testing or free),prod jobs也就是monitoring and production级别。
每个task对应的就是一台机器上的一个container(linux container),task描述所需的资源,不过这个资源信息竟然还包括了tcp端口什么的。

3. Utilization
这是我最关心的部分,在这个部分前论文里还讲了不少关于Borg的架构,以及它是如何保障自己的可用性的等等,这块我就不在这讲了。
Borg最主要的一个目的就是通过一个全局的资源管理系统来提升资源的使用率,在这个部分中论文阐述了Borg通过哪些方法来提升使用率。
第一个方法是离线在线任务混部,论文里有讲到大部分其他的公司都是将离线集群和在线集群分开,G家的一个研究分析表明如果G家把它的离线任务和在线任务集群分开,那么G家会大概需要增加20%-30%的机器,G家的机器数应该在500w+,20%就是100w台机器,这这算到成本的话是非常非常夸张的,所以可以看到离线在线混部是节省资源的一个关键手段,当然每家公司这么做带来的成本节省会不一样,论文里写到之所以离线在线混部能达到这么好的效果,主要原因是prod任务通常是为高峰负载来申请的机器数,而事实上高峰时间通常都是非常短的,就意味着prod任务申请的资源大部分时候其实是空闲的,所以Borg的做法是利用prod任务空闲的资源来跑non-prod任务,从而大幅减少机器数,我自己觉得这是Borg区别于目前大部分其他类似东西的最主要的地方,而且也是最本质的地方。

在给任务分配资源时,Borg采用的方法是prod任务直接占用资源,prod任务互相之间不共享资源,Borg会预估这些prod任务能空闲出的资源,分配给non-prod任务使用,之所以很多公司都不这么做,是担心non-prod任务的执行影响到了prod任务。

Borg在资源的隔离上采用的做法为:
Borg按照两种方法来控制overload和overcommitment,overload通过将任务分为Latency-sensitive(LS)和batch两种来控制资源;overcommitment则通过任务对资源的占用划分为compressible(例如cpu、I/O带宽)和non-compressible(例如内存、磁盘空间)。
如果机器上non-compressible的资源不够用了,那么Borglet会立刻按照优先级杀掉本机上优先级低的任务释放出资源;如compressible的资源不够用,那么Borglet会先尝试控制batch任务对cpu的使用来度过高峰,如果还不行,就会由borgmaster拿掉机器上的一个或更多的低优先级任务。
Borglet里面还有一个用户态的程序来控制分配给prod和non-prod任务的内存,如内存超过了task的限制,或本机的内存不够用了,内核中处理OOM事件的部分将按照优先级来kill task。
在CPU方面,在资源的分配上,对于LS tasks允许占用整个cpu核,而batch tasks则可以在任何核上运行,但batch tasks会在较低优先级上,Borg修改了内核的CPU调度器,允许根据每个container的load状况来动态决定是否要kill batch tasks,同时避免多个LS tasks在一个cpu上争抢,目前Borg仍然在尝试的是在cpu调度时更好的考虑线程亲和、NUMA亲和之类的。

可以看到Borg为了能实现离线在线混部,在资源的分配、隔离上还是做了很多改进的,如果这些patch能公开出来就好了,否则的话其实多数公司做的话也只能是重新尝试,靠不断摔跤来绕过这些问题。

论文的最后有总结下Borg的好和不好的地方,其中竟然也提到了一台物理机一个ip所有container共享的问题,从我们实际对container的使用来看,运维更友好的话其实还是每个container一个ip比较好。

尽管论文中有提到其他很多相似的系统,例如mesos,yarn,facebook’s tupperware,ms autopilot,甚至还提到了alibaba的fuxi,但我始终觉得这些和Borg的巨大区别就是Borg是用来管理所有资源,并且离线在线混部的,而上面这些相似的基本都只是某一款软件的资源管理,这在资源的利用率上相比的话会完全不是一个档次。

据之前的信息,Borg在G家内部应该在2005年左右就相当成熟了,而我们看到论文的时候已经是2015年了,这个差距…

国内腾讯、百度也都尝试过类似Borg的方向,目前腾讯基本是不玩了,百度的Matrix则成功了,同样是离线在线混部,由于给百度节省了巨多的成本,所以这个团队之前得到过百度的100w大奖,不过百度的Matrix其实和Borg实现上的话还是不太一样的,从Borg论文可以看出Borg的话是所有的调度器,这样实现的话有一个坏处是会比较重,百度的Matrix选择了一条更轻量的改造路线,对于一个有不少历史包袱的公司来说是不错的选择,对Matrix感兴趣的同学可以看看这篇文章

我自己这边的话其实是在去年下半年才想明白我们原来所做的提升资源利用率的方向一定程度是错误的,现在我是比较坚信在线离线混部,区分好prod、non-prod,内核改造是比较好的提升资源利用率的方向,我的团队今年在这块需要重点突破,欢迎有兴趣的同学加盟,:)

=============================
欢迎关注微信公众号:hellojavacases
题目来源:http://www.wired.com/wiredenterprise/wp-content/uploads/2013/03/borg.gif

关于此微信号:
分享Java问题排查的Case、Java业界的动态和新技术、Java的一些小知识点Test,以及和大家一起讨论一些Java问题或场景,这里只有Java细节的分享,没有大道理、大架构和大框架。

公众号上发布的消息都存放在http://hellojava.info上。

加机器、切流量真的那么low吗

每当说到应对活动高峰流量的策略时,加机器这招总是会被认为很low,而同样,在另外一个场景中,当系统出现问题,通过切流量来快速恢复时,也会被认为很low,但这两个大招真的很low吗?

第一个点:加机器
首先说说加机器这回事,通常系统最早都是由一台应用机器再加一台或两台数据库机器组成,访问量高的情况下,自然最希望的就是靠加机器来解决。

简单分开看看,应用机器直接从一台变成两个,在技术上会有一些问题是要解掉的:
1. 应用的状态问题
例如最常见的会有用户的登录信息,也许还会有一些其他的状态信息,而如果不解决这些状态数据的问题,就会导致用户访问两台机器时不一致的现象;
2. 一台到两台组集群的问题
这里也将涉及到一堆的问题,例如负载均衡、健康检查等。

如果是数据库加机器呢,就更加复杂了,在技术上通常要解决分库分表的问题,如果不做分库,那么即使加数据库机器也分摊不了压力,分库通常是物理上的拆分,而如果是同一张表的压力,那么通常还需要做分表。

而随着应用机器数的增长,到数据库的连接池很容易成为瓶颈,这个时候通常会碰到有钱买机器,但不能加机器的致命现象,因此也是必须去解决掉的。

仅仅从上面这个最简单的过程就可以看到,系统要纯粹做到靠加机器就能支撑流量其实并不容易,是需要相当多的技术支撑的,就更不用说,当系统复杂到演变为下面的场景时:
当系统演变为由几百、几千个应用组成的时候,要做一次活动,请问到底要加多少机器,每个应用又加多少台呢;
加的机器一个机房都装不下的时候,那该怎么办呢;

很多时候架构演变要解决的主要问题就是怎么让系统能做到仅靠加机器就能支撑增长的流量,规模问题随着规模的数量级的变化,遇到的挑战会更加的大,从而也会导致系统的架构更加复杂。

这里还没有说加的机器实在太多了,成本该怎么控制住的问题,所以要做到加机器其实真心不low,一个极强的架构师,需要能根据人力的投入、规模增长的预期来权衡在一个架构版本中需要支撑到的规模级别,合理设计架构,从而确保在一个周期内可以仅靠加机器就解决,并同时又确保了在下一个周期到来前已经做好了各方面的技术积累和准备。

第二个点:切流量
有些人看到阿里这边碰到系统出问题的时候切流量这招,会觉得很low,但事实上我们简单来看下,切流量通常指的是把一个机房的流量切到另外一个机房,初级的版本是切流量到同城的另一个机房,这里要解决掉一些技术问题才能做到:
1. 数据库等的地址切换,当做流量切换时,通常是一个机房内出了不确定的故障,因此通常数据库也需要做好主备的切换,除了数据库外,可能还会有其他的一些指定的地址,这个时候都应该做到在不需要重启应用的情况下完成地址切换,这说实话如果在一开始系统简单的时候没有管理好这些地址类型的信息的话,等到系统复杂了再来做这个还是挺花时间的;
2. DNS disable vip,这里就是一个很容易被说到的点,DNS是没有健康检查的,因此当要拿掉一个机房的vip时,就必须等待dns生效,但其实这个是有一招可以来解掉的。

如果是异地的机房的流量切换,就更加复杂了,由于异地的情况下网络的延时会是一个大型分布式应用没法接受的,这个时候通常会启用异地多点写,而要做到这一点就已经非常不容易了,只有在做到这一点的情况下,才有得说异地机房切流量这一说,而在做到的情况下,怎么保障异地机房切流量时的数据的正确性是非常关键的点,感兴趣的可以看下我在这片文章里讲的关于异地多活的技术点。

所以和加机器一样,我们可以看到切流量这招的背后也是需要有强大的技术支持的,我自己是认为切流量是提升系统可用性的极为有效的大招,在多个机房且可以快速切流量(要做到快其实也非常的难)的情况下,单个机房内无论出现任何故障,都可以仅仅靠切流量来应对,这对提升系统的可用时间是非常有帮助的。

欢迎大家讨论这里面讲的一些点,:)

=============================
欢迎关注微信公众号:hellojavacases
题目来源:http://www.bhmpics.com/wallpapers/low_battery_logo-1680×1050.jpg

关于此微信号:
分享Java问题排查的Case、Java业界的动态和新技术、Java的一些小知识点Test,以及和大家一起讨论一些Java问题或场景,这里只有Java细节的分享,没有大道理、大架构和大框架。

公众号上发布的消息都存放在http://hellojava.info上。