架构里最常用的反面案例是 big ball of mud。很大程度上可以说打格子,把复杂的系统拆解成小格子是架构师最重要的工作。这个小格子有很多种名字,比如:组件,模块,子系统,库,bounded context林林种种。
但是仔细想想?为什么需要打这些格子?为什么要把一个进程干的活拆分出这么多进程去干?
一个东西打成多少个格子来做,不是受一种因素影响的。不同类型的应用,其面临的主要挑战是不同的,所以上面三种因素里对解决方案的影响的权重就不同。
比如说:如果我们做的是微信的架构。微信的主要架构问题是什么?其实是给海量低价值的用户情况下提供免费服务。微信的后台架构里决定因素的是成本,所以在腾讯的后台业务里决定架构师能否做得好,主导的是对计算机体系架构是否足够精通。微信的kv存储能够做到同城三园区容灾,同时比竞争对手少一分备份达到同样的数据安全性,这就是核心竞争力之一。在海量但是业务逻辑简单的系统里,主要要考虑是怎样打格子可以最大化利用现代计算机体系架构。也就是这些格式主要是为了非功能性需求切分出来的。一个进程挂了就挂了,三个进程负载均衡就可以做到很高的可用性。一个流程写在一个函数内,一次执行需要很长时间,并发来跑可能需要很多进程。但是考虑SEDA的因素,可能把一个流程切成三个函数来写,每个函数独立成不同的线程或者进程来并发可以获得更好的总并发性能。等等这些,切分的模块的时候考虑的是内核,cache友好,网卡带宽,copy-less这些因素。如果我们做的业务是海量的简单逻辑的互联网业务,不精通Linux内核,TCP/IP协议栈和算法与数据结构是无法做一个好的架构师,打好格子做出高性能低成本的架构的。这个方面的架构师的典型代表是:Google 的 Jeff Dean,以及 Map/reduce Spanner 为代表的经典分布式架构。
但是系统是多种多样的。除了海量简单逻辑的业务,世界的另外一端是业务极端复杂但是使用量并不大的架构,比如典型的企业流程自动化系统。在处理复杂业务的时候,业务架构就是主要的影响因素。什么是业务架构?比如说电子商务要处理真实世界的商品交易流程,就需要把系统与真实世界的业务边界对应起来。一个电子商务系统需要拆分成“库存管理系统”,“定价系统”,“商品目录系统”等等。这种拆分不是因为架构师精通红黑树,认为这几个模块的数据结构不一样必须分开,而是不这么分的业务复杂度无法控制。越是架构做得好的系统,其子系统越是可以写得简单,因为每个都只处理一个领域的业务逻辑,而不是把不同的业务逻辑交织地扯在一块来写。决定系统的架构必须跟着业务架构走的根本原因在于普通人的脑容量是有限,不分治之,复杂度根本hold不住。这个方面的架构师典型代表是:Eric Evan 和 Udi Dahan,以及他们代表的领域驱动设计思想。
世界是多样的,很多事情不是在台面上的。摆在台面上的拆分系统的理由往往是业务逻辑,性能与稳定性等等。实际上真正主宰一个系统的模块怎么拆分的其实是组织架构。如果开发者向同一个管理40人左右的leader汇报,那么这些命运紧密相关的人(从年终奖的角度)无论写的系统是什么很难不紧密耦合,如果两个leader带的团队共同参与一个项目开发,很难去组织他们把工作拆成两个模块来写,中间再各自加一道隔离层来保护自己。要是哪天这哪个团队合了,或许他们做的第一件事情就是把两个模块给合回去,名义可能是这样开发效率更高。这个现象叫Conway's Law,最有名的架构师是:James Coplien,以及他所撰写的Organization Patterns(http://orgpatterns.wikispaces.com/BookOutline)
big ball of mud的系统有三种死法:
如果一个系统兼具三个难点,简直难到爆:首先对延迟及其敏感,需要超级懂计算机体系的人。其次业务逻辑很复杂,需要很强的建模能力。同时又开发工作量巨大,需要很多人参与从而引入各种政治问题。金融衍生品交易等领域可能符合这些特征。假如微信的架构师来做办公自动化系统,上来就搞共享内存和高性能队列,我只能呵呵了。
这是淘宝的某个人写的ppt的截图。QZone的后台其实也差不多。互联网现在的所谓最佳实践就是“微服务”。把一个大的系统拆成小的服务,用RPC一级级连接起来。A调用B,B再调用C。一个用户的请求要经过n个团队的系统转一圈出来。现在干什么事情都要讲求资历,而资历往往又和业务成功是挂钩的。淘宝的业务牛x了,意味着其架构就是完美无缺的么?当然,如果我没有身披某某大公司技术副总裁的光环,而自动具有资格来评价这个问题。但是仅仅从一些技术细节的狭窄视角来看,这个架构也是有很多问题的,我称之为dependency hell:
1、从业务解耦的角度来看:RPC与共享数据库没有本质区别,拆开之后两个系统仍然是强依赖
这很糟糕
但是改成这样就真的更好了么?服务C和DB没有什么本质区别。
2、从非功能需求的角度。RPC依赖双方是绑死的。多次RPC调用叠加的可用性是叠加下降的,每个点都要保证自己的高可用,这对基础设施(运维自动化,集群管控)提出了很高的要求。而且对升级的依赖关系造成了很大挑战,哪个升级会影响哪个。同时对于容量规划简直是灾难,谁都不知道一个服务降级之后对其他所有依赖的方的雪崩效应到底是如何的。像QZone为了理解自己的后台服务架构到底是怎样互相依赖的,已经开始用上网络抓包的手段了,因为已经没有人知道这个网状的关系到底是怎样的了。从一个具体的技术细节来看,RPC调用需要主调方保留上下文的context在内存中(以及tcp socket),下游相应越慢对上游保留context的压力就越大。当然这一切都可以通过加机器,更好的负载均衡,更好的高可用机制来弥补。无非就是请几个高工来做集群管理系统,再加上机器和带宽费用嘛。大部分互联网公司的估值不是因为这个公司如何能节省成本,而是如何能花钱。所以非功能性需求不是决定性因素。
3、从组织架构的角度,要想获得最大的效率,必须要团队自治。RPC Hell的后果是每个团队都与其他团队紧耦合了。做个小功能,也需要拉一个跨组织架构的虚拟团队来协调接口。每个开发者脱离了所谓的那个线下环境就无法开发了,每个功能都有n个rpc call别人的系统,而别人的系统当然是我们自己搭不起来的。因为你去搭A系统的时候就会发现依赖B系统,B系统依赖C系统。在没有很好的基础设施支撑的情况下,比如分布式日志收集,docker的开发环境管理。开发者在这个微服务架构下是欲死欲仙的,干什么都是联调。每个人都对别的团队做阻塞的call,体现出来是日常开发也是阻塞的。老板为了提高整体开发团队的产出,就是加线程的方式,招更多的人。而招更多人为了职级晋升又需要把组织架构划分得更加的细。这个和网络异步编程的原理其实是一样的。
诚然,BAT都是RPC嵌套的解法。诚然,我也不是什么专家。但是拆分子系统显然不是只有RPC一途,Event Driven的架构其实历史同样悠久。
具体的做法三言两语讲不清楚。最好有实际的案例。这里有两个非常具体的案例:
电子商务的案例: http://pan.baidu.com/s/1i3o6J7f
医疗记录的案例: http://pan.baidu.com/s/1c05BXLm