第一种死法:Big ball of Mud

架构里最常用的反面案例是 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的系统有三种死法:

  • 因为性能和稳定性不满足要求而死,这个时候需要懂内核懂数据结构的架构师来好好做一下拆分。
  • 因为业务逻辑复杂得hold不住而死,开发效率慢到爆,这个时候需要做过复杂业务的架构师来重新梳理一下模块拆分。
  • 因为软件架构不符合组织架构,导致团队间吵架吵到死的,这个时候老板重新思考什么样的组织架构才是合适的(大boss通过组织架构体现其才是最终极的一切的架构师)。

如果一个系统兼具三个难点,简直难到爆:首先对延迟及其敏感,需要超级懂计算机体系的人。其次业务逻辑很复杂,需要很强的建模能力。同时又开发工作量巨大,需要很多人参与从而引入各种政治问题。金融衍生品交易等领域可能符合这些特征。假如微信的架构师来做办公自动化系统,上来就搞共享内存和高性能队列,我只能呵呵了。

第二种死法:Dependency Hell or RPC Hell

这是淘宝的某个人写的ppt的截图。QZone的后台其实也差不多。互联网现在解决big ball of mud的所谓最佳实践就是“微服务”。把一个大的系统拆成小的服务,用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,体现出来是日常开发也是阻塞的。老板为了提高整体开发团队的产出,就是加线程的方式,招更多的人。而招更多人为了职级晋升又需要把组织架构划分得更加的细。这个和网络异步编程的原理其实是一样的。

其实在我看来,这种RPC的解法的来源应该是来自于这个循环:

需求做不完=>那么加人吧=>加人没有坑,那么加坑吧=>我是leader我做主,咱们拆分模块吧=>模块拆分哪家强,RPC谁都懂,那就上吧=>继续循环

RPC可能不是最优的解法

诚然,BAT都是RPC嵌套的解法。诚然,我也不是什么专家。但是拆分子系统显然不是只有RPC一途,Event Driven的架构其实历史同样悠久。

具体的做法三言两语讲不清楚。最好有实际的案例。这里有两个非常具体的案例:

电子商务的案例:http://pan.baidu.com/s/1i3o6J7f

医疗记录的案例:http://pan.baidu.com/s/1c05BXLm

星巴克:http://pan.baidu.com/s/1eQvy31g

We are doomed, and RPC does not help的更多相关文章

  1. 从RPC开始(一)

    这是一篇关于纯C++RPC框架的文章.所以,我们先看看,我们有什么? 1.一个什么都能干的C++.(前提是,你什么都干了) 2.原始的Socket接口,还是C API.还得自己去二次封装... 3.C ...

  2. RPC 使用中的一些注意点

    最近线上碰到一点小问题,分析其原因发现是出在对 RPC 使用上的一些细节掌握不够清晰导致.很多时候我们做业务开发会把 RPC 当作黑盒机制来使用,但若不对黑盒的工作原理有个基本掌握,也容易犯一些误用的 ...

  3. 谈谈如何使用Netty开发实现高性能的RPC服务器

    RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议.说的再直白一点,就是客户端在不必知道 ...

  4. 游戏编程系列[1]--游戏编程中RPC协议的使用[3]--体验

    运行环境,客户端一般编译为.Net 3.5 Unity兼容,服务端因为用了一些库,所以一般为4.0 或往上.同一份代码,建立拥有2个项目.客户端引用: WindNet.Client服务端引用: OpL ...

  5. python通过protobuf实现rpc

    由于项目组现在用的rpc是基于google protobuf rpc协议实现的,所以花了点时间了解下protobuf rpc.rpc对于做分布式系统的人来说肯定不陌生,对于rpc不了解的童鞋可以自行g ...

  6. spider RPC入门指南

    本部分将介绍使用spider RPC开发分布式应用的客户端和服务端. spider RPC中间件基于J2SE 8开发,因此需要确保服务器上安装了JDK 8及以上版本,不依赖于任何额外需要独立安装和配置 ...

  7. Netty实现高性能RPC服务器优化篇之消息序列化

    在本人写的前一篇文章中,谈及有关如何利用Netty开发实现,高性能RPC服务器的一些设计思路.设计原理,以及具体的实现方案(具体参见:谈谈如何使用Netty开发实现高性能的RPC服务器).在文章的最后 ...

  8. 基于Netty打造RPC服务器设计经验谈

    自从在园子里,发表了两篇如何基于Netty构建RPC服务器的文章:谈谈如何使用Netty开发实现高性能的RPC服务器.Netty实现高性能RPC服务器优化篇之消息序列化 之后,收到了很多同行.园友们热 ...

  9. Redola.Rpc 的一个小目标

    Redola.Rpc 的一个小目标 Redola.Rpc 的一个小目标:20000 tps. Concurrency level: 8 threads Complete requests: 20000 ...

随机推荐

  1. win7的svchost.exe占用内存过高如何解决

    方法/步骤 1 在我的电脑上点击鼠标右键,选择[管理] 步骤阅读 2 选择右侧[服务和应用程序]下的[服务]选项 步骤阅读 3 找到名称我Superfetch的服务,双击鼠标左键. 步骤阅读 4 选择 ...

  2. MFC 配合 protobuff libevent 实现的Socket 的GM工具 框架

    MFC 配合 protobuff libevent 实现的Socket 的GM工具 框架

  3. Groovy basic

    1. print println "Hello Groovy!" you can use java in Groovy System.out.println("Hello ...

  4. [原创][LaTex]LaTex学习笔记入门

    0. 简介 LaTEX(/ˈlɑːtɛx/,常被读作/ˈlɑːtɛk/或/ˈleɪtɛk/),文字形式写作LaTeX,是一种基于TEX的排版系统,由美国电脑学家莱斯利·兰伯特在20世纪80年代初期开发 ...

  5. 什么是侧翼区(flanking region)和侧翼区单核苷酸多态性(Flanking SNPs)

    侧翼区(flanking region) 根据维基定义:The 5' flanking region is a region of DNA that is adjacent to the 5' end ...

  6. MJExtension 中model嵌套Model

    + (NSDictionary *)objectClassInArray { return @{@"comment": [Comment class]};}

  7. 解决KDE桌面环境下Eclipse崩溃的问题--让Eclipse使用特定的GTK2主题运行

    最近在Kubuntu14.04上安装Eclipse,由于Ubuntu软件中心中的版本太老(3.8),而且会自动安装OpenJDK,于是到官网下载最新的4.4版.(Luna,代号很有亲切感有木有,女神万 ...

  8. canvas基本用法

    1.canvas和其他标签一样使用,但是IE8以下是不支持的,可以在canvas里面加一个span用来提示,例如: <canvas> <span>IE8不支持canvas< ...

  9. 设置阿里云maven中央仓库的settings.xml

    本来想找一个可用的设置文件,结果乱七八糟的,干脆自己做了一个,同时还放上了Spring的SNAPSHOT和MILESTONE/RELEASE仓库,希望能帮到一些人. <?xml version= ...

  10. java常用类

    String 字符串类 System 可得到系统信息 Runtime类 StringBuilder(StringBuffer)类 Thread 线程类 Math 与数学有关的工具类 Date 日期类( ...