文/吴雪峰
如今微服务架构正逐渐演变成一种主流的架构风格,那么微服务架构是一颗银弹吗?我们提倡微服务架构的目的是什么?
1987 IBM大型机之父Fred Brooks在《没有银弹:软件工程的本质性与附属性工作》中提出软件工程包括本质性工作和附属性工作。本质性工作是创造出一种由抽象的软件实体所组成的复杂概念结构;附属性工作是用程序语言来表达抽象概念,并在空间和时间的限制下,翻译成机器语言。《没有银弹》主张并断言在未来的十年之内(从1986年文章发表后开始计算),不会有任何单一软件工程上的突破,能够让程序开发的生产力得到一个数量级(10倍)的提升。
我们讨论或推广一项软件实践或技术的时候,实际上是在谈如何提高生产力。本文试图利用《没有银弹》对本质性工作四个原因的归类,去认识微服务架构的生产力。《没有银弹》认为本质性工作大部分的活动是发生在人们的脑海里,具有四大难题:复杂性、隐匿性、配合性和易变性。
1、复杂性
软件要解决的问题通常牵扯到多种元素和计算步骤,这是一种人为的、抽象的智能活动,多半是复杂的。随着“软件吞噬世界”不断深入,软件所对应的社会活动越来越多,也越来越复杂。
单体架构系统的困境
系统往往是承载的业务越成功,越容易失败。因为系统会随着业务的发展,增加越来越多的特性和功能,使得系统复杂到没有一个人能全面理解,没有一个人敢去修改原有的功能或代码。
微服务的救赎
微服务提出以业务边界作为模块划分原则,每个模块独立进程。一个业务由很多独立的小业务组合而成,系统也是由独立的小系统组合而成,这样的好处是每个小系统都很容易被理解,一个大系统可以根据业务组合微服务,或者逐渐发展独立的微服务。
微服务救赎的代价
但是微服务架构真的降低了系统的复杂性吗? 其实并没有, 而且是增加了系统的总体复杂性!
微服务之间的连接更加复杂,网络通讯不可靠和性能损耗,协议匹配,接口对接和转换,版本协作,微服务注册和发现,编排和调度,分布式业务和数据一致性等等复杂性都是单体架构不需要考虑的。
微服务为了降低单个微服务的复杂性,导致整体系统的复杂性急剧增加。
如果单体架构根据业务进行模块划分,每个模块之间根据宿主语言接口进行交互,就像OSGi那样模块化/插件架构完全可以做到模块化开发。
那么为了降低系统复杂性,是不是放弃使用微服务? 答案既是肯定也是否定。
我们刚才发现微服务架构带来的复杂性,大多是附属性问题。对于本质性复杂性,微服务使用上下文界限实践,可以更好的隔离依赖业务概念的复杂性,从而降低单个微服务的复杂性。
利弊权衡
既然有得有失,我们是否需要做个权衡?
《没有银弹》的主张只说对了一半,10年内对于本质性问题确实是不会有十倍生产力的提高。但历史上来看,附属性问题是有十倍生产力提高的。 而我们在软件工程实践中,往往本质性问题和附属行问题混合在一起,我们需要不断的加深认识,区分两者,并相互改进。
微服务架构带来的附属性复杂性必须要有一个功能齐全的PaaS进行转移。
微服务推广初期(包括现在)所面临的很多问题,其实是缺乏一个功能齐全的PaaS,比如AWS/Azue,Rancher/OpenShrift,用以解决附属性问题。
所以我们不需要做太多的利弊权衡,因为PaaS日趋成熟,成本将降到几乎可以忽略不计。微服务架构利用PaaS对附属性问题的转移,降低一点点本质性问题的复杂性,也是非常价值的。只是发挥这种价值,需要掌握DDD设计实践,利用好上下文界限来降低单个微服务的本质性复杂性。
软件在没有应用到业务之前,各种信息和思考大多在每个人的脑海里,很难完全呈现和想象出来。客户只是模糊的知道自己想要什么,但并不知道要怎样的软件;项目经理有把握软件开发进度的能力,但不知道软件每个阶段后能长成什么样;业务分析员能把业务分析清楚但不能确认转变成最终软件会成为什么样;开发设计人员知道怎么做软件,但又不能完全理解业务需求。
单体架构系统的困境
单体架构中的各个模块隐藏在大系统的页面下,在交付之前对外界是不可见的。 更麻烦是单体架构往往是按技术维度进行分层设计,导致没有人能看清全貌,各自都想把事情做到最好,但组合起来却不是客户想要的东西。
微服务的救赎
微服务强调小、独立业务价值,独立进程独立部署交付,使软件尽快尽早得交付到最终用户手中,来交付和验证业务价值。
微服务架构并没有改变软件开发过程中的隐匿性,而是通过缩短从需求到交付这段软件开发周期,减少隐匿时间,来降低软件工程总体的隐匿性。
利弊权衡
听起来快速交付很好,但会有什么问题吗?
组织必须要具备自动部署持续交付能力。假如一个系统上线需要3个小时进行部署,如果我们要持续部署,每天都部署一次,那就需要每天拿出3小时做部署,我想这个成本是不能接受的。一个全自动化的持续部署平台是必须的,而且还需要保证每次交付都是高质量的,就需要做到全流程内建质量。
这里就引出另外一个问题,由于要求快速交付,就需要打通或模糊软件开发过程的需求、设计、实现、测试、验收、运维、运营等环节,对照以往按照单体架构组织的软件开发流程会发现打通这些过程是有生产力损耗的。比如可以专门做需求分析一个月,不用考虑开发实现,这样更加专注在需求分析当然效率更高了,我们为什么要拉通需求、开发、测试,使得各个环节不专注,生产效率反而下降呢?
在大型软件环境中,各子系统的接口必须协同一致。考虑到时间和环境的演变,要维持这样的一致性通常十分困难。
单体架构系统的困境
一个系统在最初开发的时候往往是最舒服的,虽然没有现成的可重用组件,但也没有沉重的历史包袱。假如系统从零开始做的话,头三个月开发会比较慢,因为需要搭建和熟悉一些开发、测试、部署基础设施,随着基础设施和公共组件的完善,接下来的半年到一年开发会加速,但是再往后开发速度又会逐渐降低。因为那些一开始提高开发效率的接口、共享表、依赖组件都变成了复杂网络缠绕在一起,变成了所谓的牵一发而动全身,改一行代码都不知道会影响到什么地方。重构也变得非常困难,因为需要相互配合的地方太多了,协同成本极速占据开发成本的比例。
微服务的救赎
既然一个系统在初步开发、规模不大的时候生产效率最高,要想使得生产效率维持在一个较高的水平,那就保持系统总是很小。这样因为系统本身很小,微服务系统的配合性问题总是在一个可控的范围内。比如微服务独立数据库表结构,那我们根据业务需要改表结构的时候就不需要去考虑会不会影响到其他业务,因为其他业务和这个表结构完全没关系。
我们区别看待微服务对外和对内的配合。刚才讨论的单体架构配合性问题都是对内的问题,但微服务架构因为把系统做小,从而把一些原本对内的配合问题变成对外的配合了,极大增加了对外配合性问题。
然而单纯从接口协同一致上来说,微服务架构非常糟糕。 单体架构的接口之间配合是相同的编程语言,基本上在编译时就能发现错误。而微服务的接口往往是远程服务,在开发时没法对齐。
利弊权衡
微服务控制了对内配合性问题,增加了对外配合性。版本依赖怎么管理、公共组件怎么共享、事务一致性如何协调都是让人纠结的问题。所以微服务的划分就变得非常重要,总体的指导思想是减少对外配合,把复杂的配合性问题留在微服务内部。
软件所应用的环境常是由人群、法规、硬件设备、应用领域等各因素汇集而成,而这些因素皆会快速变化。
单体架构系统的困境
单体架构往往需要将所有的模块都整合在一起才能发布,所有模块必须要步调一致。但需求的变化却是不同节奏的,单体架构系统只能强制的把对需求的变化响应放在一个时间点发布,进而使需求得不到及时响应。
单体架构也不能很好的响应性能变化的需求。比如某个模块的流量突然增加,或者需要大内存,单体架构只能为极少的模块增加整个系统的计算资源,又因为增加整个系统的计算资源成本很高、实施时间长,导致性能需求迟迟不能得到满足。
微服务的救赎
微服务要求独立进程,可以完全根据需求定制不同类型的计算资源,更精细化分类的利用内存、IO、CPU。因为小,可以更快水平扩展响应性能需求变化。
更关键是,微服务小,强调独立业务价值。根据康威定律,系统架构和组织架构相互影响,微服务小组需要是独立特性的全功能团队,面向最终用户需求直接对战。小团队直接面对客户需求做决策,所有信息和想法在小范围内快速交流,业务价值流动更容易可见,更快速的响应变化。
利弊权衡
微服务架构需要改变组织结构小团队充分授权、业务交付模式。
对于传统组织而言,这点是最难的,尤其是大公司往往采用层级组织结构,要求把权力下放到小团队,这会触动已有的权力结构不说,还会引起组织的不安全感。
微服务的交付模式也会挑战客户接受软件更新的习惯,这也是成功的传统企业面对传统客户需要面对的挑战。
微服务架构解放小团队生产力,提高市场响应力。
微服务是颗子弹,需要PaaS作枪,瞄准的是快速变化的目标。