Skip to content

简介

当前,微服务架构在国内正处于蓬勃发展的阶段,无论是大型互联网公司还是传统的 IT 企业,纷纷采用微服务架构构建系统。
在过去几年里,DevOps、云原生、面向演进式架构等理念已经深入人心,围绕微服务生态也出现了大量的组件、框架、工具,这很好地支撑了海量的数据增长和服务业务需求的快速变化

纵观计算机产业,过去的半个世纪,遵从摩尔定律的规律,计算力一致保持着大跨度的提高。
1956 年,英特尔创始人戈登摩尔提出,集成电路的集成度每两年会翻一番;而后这个周期缩短到 18 个月,微处理器的性能每隔 18 个月提高 1 倍。
然而,硅芯片已逼近物理和经济成本上的极限,许多专业人士纷纷预测,摩尔定律在不久的将来会失效。
届时,我们会面临 CPU 性能提升放缓,计算力增长式微的局面。
同时云计算、大数据、物联网、边缘计算、人工智能等技术的进一步成熟所产生的海量数据,却加大了对后端数据中心计算力的需求

数字化经济的快速发展和云计算给底层 IT 系统带来的巨大变革正是当下微服务架构快速发展的时代背景。
Gartner 预计,从 2018 年到 2022 年,PaaS 将成为未来的主流平台交付模式,而 PasS 平台需要更加灵活的云原生应用架构做技术支撑,微服务架构正式云原生架构落地的关键技术。

正所谓“大道至简”,微服务本身是一个化繁为简的过程,它采用细粒度的分布式架构模式,通过系统化的思考方式,将纷繁复杂的业务逻辑映射到底层技术。
在软件构件方面,微服务倾向于使用面向服务和领域驱动设计(Domain Driven Design)的方法论,将现实中的问题投影到对象的世界。
"抽象、分解、扩展、复用" 是常见的微服务构建系统的内功心法。然而软件开发没有 "银弹",架构设计还要从使用、性能、成本、效率、团队、收益等多方面权衡,进行综合考虑。

微服务架构的目标是,将业务与技术的复杂度进行分离,使业务更专注于实现对客户的价值交付,而将非功能需求封装在平台或者底层 SDK 中。

微服务优势:

  • 更加快速地响应业务需求
  • 可提升应用的开发效率
  • 可满足对云原生的支持
  • 可满足系统的弹性、扩展性的需求
  • 容错性及生产就绪特性可保证服务的高可用
  • 有标准化的服务实现和交付方式
  • 基于“不可变基础设施”模式,可以减少环境因素的影响
  • 可提升系统性能及资源利用率

传统粗放式的开发和运维方式将逐渐被取代,而更加高效、智能化、自动化的开发和运维方式将使每个人从众获益。
当前,微服务和云原生应用架构还在快速演进之中,其间充满了机遇和挑战。
作为软件从业人员,面对技术的更新迭代,我们唯有整装待发,才能与时俱进

微服务的概念来源于 Martin Fowler 的一篇知名博文: MicroServices。
在博文中,"微服务架构"这个术语用来描述一种将软件应用程序设计为可独立部署的服务套件的特定方式

"细粒度自治服务" "自动化部署" "围绕业务能力" "端点智能" "语言和数据的分散控制",从这些描述微服务架构特征的术语中,我们发现了一种越来越吸引人的软件系统风格。

目前业内存在大量成熟的开源微服务开发脚手架,如 Go-kit、Go-micro、Spring Cloud、Tars 等,它们经受过大量线上真实业务环境的考验,能够为计划进行微服务架构实践的开发团队提供一站式服务,使得开发人员更加专注于业务开发

微服务是一种分布式系统解决方案,推动细粒度服务的使用,这些服务协同工作,且每个服务都有自己的生命周期。
因为微服务主要围绕业务领域建模,所以避免了由传统的分层架构引发的很多问题。
微服务也整合了过去十年来的新概念和技术,因此得以避开许多面向服务的架构中的陷阱

微服务架构介绍

背景介绍

目前不仅各大互联网公司已经在大规模地应用微服务架构,而且传统行业也逐渐接受了这种架构模式,纷纷开始采用微服务架构构建业务系统。
为什么微服务架构会如此受欢迎?微服务架构是设计而来还是演变而来的呢?要了解这些问题,我们需要从现代经济模式和企业组织架构入手来了解微服务架构崛起的时代背景

回顾早期的软件系统,企业采用单体架构可以快速满足业务的简单诉求,然而随着项目规模的扩大、业务模块的耦合、组织人员的膨胀,使得单体架构荣誉而数量庞大的代码越来越无法适应企业灵活应对变化的需求,
大量紧耦合的代码导致应用的模块界限日益模糊,业务发展急需匹配高可用、高扩展、隔离性好、复用性强的应用系统架构,而传统的单体架构显然无法满足企业的需求

在 Microservice Pattens 一书中,作者使用餐饮应用 FTGO(Food to Go)举例,说明了系统是如何一步步走向单体地狱的。
系统的过度复杂使得业务逻辑耦合、开发速度缓慢、交付周期长、难以扩展,这给开发人员带来了极大的挫折感,生产效率也随之大幅下降。
单体架构在运行状态下,出现故障难以隔离,局部一场问题往往会影响整个系统的正常运行,最终导致整体服务的不可用,给业务人员造成的收入损失、给客户造成的糟糕体验都让人无法忍受。

正是在这样的时代背景和业务诉求下,微服务架构成为了解决复杂问题的灵丹妙药。
微服务架构在应对需求的变化、容错处理、服务复用及扩展、提升开发效率,简化交互等方面都有明显的优势。

综上所述,持续快速响应市场、高度动态化、应对复杂场景的能力已经成为企业的核心竞争力,企业越来越需要一个能够面对变化、并且能够主动拥抱变化的软件架构,而微服务正是在这样的时代大背景下逐渐发展壮大起来的

微服务的定义

微服务并没有一个明确的官方定义,它可以解释为一种架构编程思维,更多地被描述为一种架构风格。
微服务架构的概念可以说来源于技术专家多年的工作积累和最佳实践总结,是通过不断发展、演进逐渐形成的。

微服务的一种定义来源于架构大师们多年的最佳实践总结,Martin Fowler 于 2014 年在他自己的博文中首次提出微服务的定义,概括总结如下:

"微服务架构" 是一种将单个应用程序作为一套小型服务开发的方法,服务之间相互协调、相互配合,每个服务运行在其独立的进程中,并以轻量级机制(通常采用 HTTP 协议)进行交互通信。
这些服务是围绕业务功能的构建的,它们可以通过全自动部署机制进行独立部署。
微服务采用去中心化的管理理念、可以用不同的编程语言编写,并使用不同的数据存储技术。总结起来微服务有以下几大特征:

  • 通过服务组件化
  • 围绕业务能力组织
  • 是产品不是项目
  • 智能端点和哑管道
  • 去中心化治理
  • 去中心化数据管理
  • 基础设施自动化
  • 为失效设计
  • 演进式设计

可以看出,Martin Fowler 试图将微服务定义为一个一般化的架构 "最佳实践" 集合。
微服务的这些特征也很好地融合了领域驱动设计、自动化、DevOps、容器等先进的技术实践、架构理念和方法论

与此同时,微服务的架构风格被描述为一种可以实现业务功能松散耦合(Loosely Coupled)的、具备一定服务边界(Bounded Context)的服务集合。
这种架构风格使得大型、复杂的应用实现 CI/CD(Continuous Integration/Continuous Delivery,持续集成/持续交付)成为可能,并且技术栈可以独立发展演进。

归根结底,微服务本质上还是一种分布式系统架构。它强调系统应该按照业务领域边界做细粒度的拆分和部署;
它剔除了 SOA 中的企业服务总线,使用轻量级、标准化的 HTTP(REST API)协议进行交互集成。
微服务架构有利于应用应对业务的快速变化和规模化发展,通过对软件复杂性的有机治理,使系统易于有序化重构及扩展

微服务与云原生

云原生(Cloud Native)可以理解为一系列技术及思想的集合,既包含微服务、容器等技术载体,也包含 DevOps(开发与运维的合体)的组织形式和沟通文化。
企业采用基于云原生的架构进行构建、运行、管理现代应用的技术模式能够平滑而快速地将业务迁移到云上,享受云的高效性和按需伸缩的能力。

云原生的提出:

"效率": 天下武功、唯快不破,面对激烈的市场竞争,企业把服务产品快速交付的能力作为制胜的法宝。
云原生架构的提出与应用的快速开发、快速交付的能力密不可分。作为对比,与传统企业为应用提供和部署软件按周、按月来计算,互联网公司经常在一天内可以进行上百次发布。
这种快速迭代和部署是建立在云基础设施和自动化持续交付能力之上的

"弹性": 随着用户规模和需求的增长,我们的应用需要能够快速扩展,提高服务能力。
传统企业依靠购买硬件的方式来提升扩展服务能力,而云原生架构可以通过虚拟化的技术实现按需扩展,动态地扩展服务实例以满足计算、存储、服务资源的弹性需求。

"可靠": 服务仅仅做到快速交付和弹性扩展是远远不够的,还需要兼顾系统的稳定性、可用性、持久性,而这种特征与服务的容错能力、故障隔离能力、可视化能力、系统快速恢复能力紧密相关,也就是系统所谓的 "反脆弱" 能力。
现代应用如果想在这些 "非功能需求" 上得到保障,就需要采用云原生技术和云原生基础设施保障服务的高可用性

云原生与微服务:

"云原生" 的本质和目标是一种应用模式,它能够帮助企业快速、持续、可靠、规模化地交付软件其中关键的支撑组件总结如下:

  • 容器化的抽象封装: 标准化代码和服务,每个部分(应用程序、进程等)都封装在自己的容器中,有助于复用和资源隔离。实现方式代表有 Docker、rkt
  • 动态管理: 通过集中式的编排和调度系统来动态管理及优化资源。实现方式代表有 Kubernetes、Swarm 和 Mesos
  • 面向微服务: 应用程序基于微服务架构,显著提升开发效率、提高架构演进的灵活性和可维护性

从云原生的代表组件可以看出,云原生的主要组成技术包括容器、服务编排管理和微服务技术。
云原生可以说是从概念上统一了构建、交付、运行现代应用的最佳实践集合。在运行环境上,强调应用程序的运行环境是以容器和 Kubernetes 为主的云基础设施;
在流程管理上,主要配合使用持续集成、持续发布以及 DevOps 能力;在软件开发上,基于微服务架构构建现代应用程序和软件。
虽然微服务架构也可以运行在传统虚拟机或物理机上,但是微服务架构的最佳运行载体是以容器为代表的云原生环境

系统架构的演进

单体架构

单体架构之所以时软件工程领域使用最早的架构,是因为单体架构师最朴素和最容易想到的。
所谓单体架构就是指一个项目或者一个归档包(jar 包或 war 包等)就完成了项目的所有功能。
这种架构非常传统,很多的 MVC(Model、View、Controller)分层设计都是为了解决单体应用架构过于复杂而演变出来的

比如一个电商的订单、用户、收款等功能都在一个应用内实现,这就是单体架构

下图所示的所有功能都是可以看为一体的。

单体应用示意图

但是随着应用的不断发展,单体架构缺点也是非常明显的,可以总结为以下几点。

  • 复杂性高: 整体项目工程量大,各个功能之间的界限比较模糊,逻辑不够清晰。随着应用的扩展,复杂性也越来越高
  • 知识转移难: 软件开发行业的人员流动非常正常,但是想要完成一个单体应用的知识转移却非常难。文档可能不够清晰,表达不够准确,频繁的人员更迭会导致很多代码的缺陷需要后续新人深入代码中查找跟踪,要熟悉一个单体应用需要耗费大量时间
  • 维护成本高: 随着单体应用的扩展,开发人员增多,沟通成本和管理成本迅速增长。一个问题出现时,往往需要几人协作才可以确定,并且一个问题的修复往往又牵扯到其他模块的问题,维护成本越来越高
  • 交付周期不可控性增大: 开发和运维越来越难,部署的周期也随着单体应用的复杂而变得问题频发,常常出现越临近上线问题越多的情况
  • 技术选型难度高: 单体架构倾向于一个整体框架可复制性地解决所有问题,所以在技术选型其间需要进行认真细致的评估,难度非常大
  • 可扩展性: 很多模型都是复用的,扩展应用时大概率会影响到原有服务,让扩展的工作变得颇为艰巨

垂直架构

因为单体架构存在种种缺点,所以软件工程领域开发思考如何优化,垂直架构应运而生。
垂直架构和单体架构最大的区别就在于对系统进行了划分,按照业务的独立性分为了不同的子单体

可以说,垂直架构是在最大化使用了单体架构优势的基础上进行优化,这种架构有不少优点,如下:

  • 分拆为多个项目,边界清晰
  • 各项目规模可控,不至于无限扩展
  • 不同的垂直结构可采用不同的技术框架
  • 和单体架构非常相似,方便开发人员去理解

但是,这种架构也有着非常明显的缺点:

  • 项目与项目之间存在着数据冗余,耦合性大,可能几个项目中都是用同一个表
  • 系统的性能提高只能依靠增加集群,成本比较高
  • 项目之间需要很多接口在彼此间同步数据

所以,架构继续演进,又有了下面的 SOA 架构

SOA

SOA(Service-Oriented Architecture)是面向服务的架构,这是在垂直架构的基础上发展而来的一种架构。
这个概念非常好理解,当垂直架构越来越多,所以把核心的业务抽取出来做成独立的服务,并且形成服务中心。

SOA 的重点是通过 ESB(Enterprise Service Bus)企业服务总线来提供的,并不关心服务之间彼此是否完全切分。

SOA 架构有很多优点,简单梳理如下:

  • SOA 可以将重要的功能提取成服务,避免重复开发
  • 采用 ESB 减少了系统之间的繁杂接口
  • 各个项目之间可以采用标准的 Webservice 或 RPC 进行调用

其实到目前为止也有很多项目是采用这种架构的,之所以现在慢慢地开始转向微服务架构,是因为 SOA 有以下缺点:

  • 各个服务之间并没有彻底的组件化,维护过程中仍然可能彼此影响,增加维护成本
  • ESB 的方式过重,内部包含各种协议,随着项目增大,运维难度增加

最后,就演进到现在的微服务架构了

微服务架构

微服务架构示意图

重点来看一下微服务的特征

  • 职责唯一性: 微服务中的每个服务都是单一的,或者说在整个架构中是唯一的。每个服务都是高内聚、低耦合的设计
  • 通信轻量级: 服务之间的通信采用轻量级的实现,就是说通信的实现与具体语言、平台无关。比如,比较常用的数据交换格式包括 XML、JSON 等,都是与平台和语言无关的,REST(Representational State Transfer)也是常用的轻量级通信方式之一
  • 独立性: 独立性指每个单个服务在开发、测试和部署过程中都是独立的,不受其他服务影响,也不会影响到其他服务
  • 进程隔离: 每个微服务都运行在自己独立的进程中,有独立的运行时环境,可以方便地部署在不同的机器上

微服务架构能够成为当前演进出的最先进框架,并非偶然,下面梳理一下微服务的优点

  • 开发效率高: 微服务将庞大复杂的系统进行拆分,每个微服务都变得功能单一,易于理解和方便开发,确保每个微服务的开发都很高效
  • 新增需求响应快: 因为对于服务的充分拆分,每个新的服务开发都非常高效,响应新需求非常快,非常适合敏捷开发
  • 部署更方便: 单个微服务的部署并不影响全局,特别是如果一个微服务运行在多个实例上,完全可以做到部署的同时向客户提供服务

微服务肯定同时存在缺点,下面来了解一下

  • 运维难度增加: 微服务的服务接口一般都是数量比较多的,因为数量太多,所以在整个服务出现问题的时候,要找出具体是哪个微服务出了问题是有难度的。要想象在单体应用里面一样通过单步的 debug 追溯原因是不可能的,可想而知,对运维团队的要求也就增加了
  • 分布式部署难度增加: 微服务是天然分布式的,也是因为必须采用分布式,所以部署和调度管理的难度就增加了
  • 接口修改成本高: 因为众多微服务之间是彼此调用的,所以当一个微服务接口要进行修改调整的时候,依赖该接口的其他众接口都需要检查或修改。因此,接口的修改成本增加
  • 部分代码重复: 因为每个微服务的开发、测试和部署都是独立的,运行时环境也是独立的,所以造成很多重复性的功能在每个微服务中都要重复开发

Go 语言微服务架构示意图:

go微服务架构示意图

微服务主要特性

粒度更细的服务

微服务架构相比 SOA 分布式架构强调按业务边界做细粒度的服务拆分。
SOA 架构使用粗粒度的服务模式来封装业务和技术能力,减少服务交互,但同时带来了业务耦合的复杂性。
而微服务架构本质上是一个做减法的架构,将规模庞大的单体系统进行服务拆分,每个细粒度服务的功能和职责单一。
当然,服务的粒度并不是拆得越细越好,如果拆分不当,还会造成服务频繁地垮网络操作,增加系统的整体复杂性

首先,微服务粒度的划分要求工程师充分理解和洞察业务领域的边界,保证你所拆分的服务是自包含的。
所谓"自包含"就是说你的服务是可以独立部署、独立演进的,你的服务可以自主地完成某个特定的、单一的功能

其次,细粒度服务应该同时具备高内聚和低耦合两个特征。
高内聚要求将系统中相关的元素和行为聚集在一起,把不相关的元素和行为放在别处;
低耦合是指降低微服务之间的相互依赖程度和相互作用关系,如果服务之间存在紧密联系,说明它们的耦合度比较高,最好不要做拆分操作,而应该做聚合操作,这样可以使信息的传递和协作比拆分成独立的服务更加简单可控。

另外,细粒度服务应该尽量做到独立。这一特性也适用于单一职责原则: SRP(Single Responsibility Principle),该原则由 Robert C.Martin 提出。
从面向对象设计的角度看,所谓职责是指一个类(Class)变化的原因。
如果一个类有多个改变动机,那么这个类就具有多个职责,而单一职责原则就是值一个类或者模块应该有且只有一个改变原因。

下面总结一下粒度更细的服务带来的好处

  • 粒度更细的服务使每一个服务专注做好一件事情。每个服务完成一个单一任务,在功能不变的情况下,应用被拆分为多个可管理的服务,很好地解决了系统的复杂性问题
  • 粒度更细的服务有助于新人对工程的学习。对于一个大型的、声明周期比较长的项目,人员的流动和组织变化是经常发生的事情,而庞大的单体架构容易使模块之间相互耦合,功能界限模糊,同时增加了新人的学习成本
  • 粒度更细的服务有利于部署。对于大型单体项目,模块之间往往存在紧密的代码耦合,一个子模块的编译错误往往会导致整个应用无法构建成功,而细粒度的服务可以通过独立工程解决 "牵一发而动全身" 的问题
  • 粒度更细的服务具备更好的复用性。在软件领域,我们一直提倡使用复用的方式构建系统,粒度更细的服务通过独立的部署,通过声明语言无关、平台无关的标准接口(REST API、gRPC)对外暴露服务,实现了积木式的架构搭建模式,提高了软件整体的开发效率

围绕业务划分团队

传统的 IT 企业习惯根据人员掌握的技能来划分组织。例如,熟悉前端的同事,都集中在一个前端开发团队;
熟悉数据库的同事,一般都会集中在 DBA(Database Administrator,数据库管理员)团队;
熟悉测试的同事,专门成立一个测试团队专职做测试工作。我们习惯于将这样的团队称为 "职能型组织",它的优势是资源集中,有利于同一职能内部的专业人士交流和经验积累

然而,职能型组织最大的问题是团队之间不容易协调利益冲突,容易形成部门墙或者叫部门壁垒。
当职能部门有多个项目同时进行时,就会产生资源失衡问题,不利于各职能部门之间的沟通交流和团队协作。
业务的需求变化如果牵涉多个职能型组织所负责的模块协作联调,往往会出现项目排期问题、优先级问题,对于跨地域、跨国家的组织,还会出现时差问题、沟通及文化差异问题,这个时候反而增加了团队之间的沟通和协调成本,降低了开发效率

微服务架构更加提倡以业务为中心,强调围绕业务领域来划分团队。
团队由具备不同能力象限的人员组成,而这样的全功能型团队相比职能型团队可以防止人员之间的互相扯皮、互相指责的问题。
同一个团队围绕业务领域沟通效率更高,团队合作更加积极主动,有更强的主人翁意识(Ownership)。
从技术的维度看,微服务架构倾向于在指定范围的 "业务界限上下文" 中定义标准规范的交互方式,这样能够保证业务接口(API)更加稳定,在后续服务的迭代升级过程中具备更好的业务兼容性和可演进性

综上所述,在围绕业务构建微服务架构的时候,解决的一个本质问题就是人员分工的问题,正如康威定律所说,任何组织所设计的系统、所交付的软件产品方案在结构上都应该与该组织的沟通结构和组织方式保持一致。

技术多样性

微服务架构不限定提供服务方所使用的技术栈和技术选型。微服务架构倾向于服务之间使用标准的轻量级的通信协议(HTTP)完成服务的集成和通信。
例如,对于性能要求比较高、对网络通信效率比较关注的服务,可以使用 C++ 语言构建;对于文本分析性的业务,可以采用 Python 脚本语言;
而对于企业应用级的 Web 项目,使用 Go 和 Java 语言开发比较合适。可见,每一种语言和技术都有其 "擅长" 的场景和适合解决的问题

微服务架构提倡数据存储的多样性和独立性。不同的数据存储引擎有各自擅长处理的业务类型数据。
对于公司的核心业务即 OLTP(ON-Line Transaction Processing, 联机事务处理)业务,可能会采用 MySQL 这样的关系型数据库。
关系型数据库的特点是遵循 ACID 原则,对事务的一致性有更好的支持,通过标准的 SQL 语言就可以方便地实现结构化数据的查询和更新

在 NoSQL 数据库阵营中,对于日志数据,可以存放在 Elasticsearch 这样的 LSM 树数据结构存储引擎中,适合日志搜索、查询操作;
对于分布式系统之间的共享数据,采用 Redis 这样的内存引擎,在读写效率、高并发性能上有更大的优势;
如果是文档型数据,使用 MongoDB 这样的文档存储引擎更加高效便利。

微服务架构提倡在技术多样性的场景中,选择最适合的技术栈。微服务通过使用标准的 API 接口对外暴露服务,给尝试新技术提供了更加友好的架构支持

然而,很多公司也推崇使用统一的编程语言和标准化的技术栈。统一技术栈的优势也是明显的,首先它会带来开发效率的提升;
单一技术栈的维护成本相对较低;新加入的开发人员也能够尽快适应统一的编程语言和架构风格;项目的风险相对比多技术栈有更好的可控性

即便如此,我们说微服务架构还是向着异构化、技术多样性的趋势在发展,因为只有保持技术的多样性,才能保证技术生态的生命力。
对于技术栈和技术选型来说,架构师需要一个 Trade-off(权衡利弊)的过程

去中心化

大型企业在集成异构系统和完成进程之间的通信时,一种传统的架构模式就是使用 ESB 消息总线技术,它可以完成信息路由、业务规则编排、协议转化等功能。
虽然,ESB 架构改变了传统软件的架构模式,消除了不同应用之间的技术差异,协调了不同应用服务的协作运行方式,实现了服务之间的集成和整合,但是,ESB 架构倾向于使用集中式的架构管理模式,它本质上是一种中心化的架构。
我们将这种企业服务总线或服务编配系统的方案称为"智能管道和哑终端"模式,它会导致业务逻辑的中心化和哑服务问题。

"哑终端"(Dumb Endpoint) 会导致 ESB 消息总线过度复杂,这种中央式的架构模式存在天然的技术与业务耦合问题。
业务编排和业务消息转化能力与业务功能全部集中在单一逻辑控制单元中,它并没有做很好的业务封装,而是将业务逻辑的复杂性全部传递到了消息总线中。
同时,随着服务规模的扩大,中心化架构的可扩展性会成为一个极大的障碍。
业务中的职责边界不清和 ESB 中心化的问题还会暴露性能问题,成为系统的瓶颈

微服务架构摒弃了 ESB 的设计理念,在微服务架构中,服务使用智能端点(Smart Endpoint)模式。
智能端点强调所有的业务逻辑应该自包含在业务内部的处理逻辑单元中,它可以确保在服务限界内服务的内聚性,而服务之间的通信应该尽量轻量化和简单化。
同时,微服务使用哑管道(Dumb Pipe)通信机制,将业务无侵入的公共组件抽象出来,封装到通用的消息基础设施中(API 网管、消息中间件等)

我们把微服务架构这样的设计理念称为 "去中心化"。微服务架构倾向于服务之间订立标准化的服务契约,目标是通过明确清晰的服务边界和服务契约机制让服务可以各自独立迭代和演进

为了最大化微服务能带来的自治性,我们需要给拥有服务的团队委派决策和控制权。
去中心化管治的最高境界就是亚马逊所宣扬的 "构建并运行它" 的理念,团队对构建的软件的方方面面负责

自动化运维

微服务架构的采用也引入了很多复杂性,关键问题是我们不得不管理大量的服务。
微服务增大了运维负担;有更多的东西需要部署,有更多的地方需要监控,错误自然也成倍增加。
而解决这些问题的一个关键方法就是拥抱 "自动化文化"。
前期花费一定的成本,构建支持微服务的工具时很有意义的,比如,自动化测试保证开发迭代中的代码质量,使用自动化发布工具将微服务部署到各个环境,使用配置文件来明确不同环境间的差异,创建自定义镜像来加快部署,创建全自动化的不可变服务器

自动化一直是软件系统运维的最佳实践,也是微服务架构强调的重要特性。
云技术使用底层基础设施及运行在之上的组件自动化变得非常简单。尽管前期投入通常会很高,但从中长期来看,无论是人力运维成本,还是在系统的弹性和性能方面,几乎总能获得更多的回报。
自动化可以比人更快地修复、扩展和部署系统

自动化贯穿软件声明周期的整个过程,在持续集成领域,我们经常使用 Jenkins 等工具自动构建、测试和不是微服务软件包。
微服务不仅应该自动化部署,还应该努力实现金丝雀测试和回滚等过程的自动化。

除非系统负载几乎从未发生变化,否则应该根据负载的增加对微服务进行自动扩展,并根据负载的持续下降进行自动收缩。
通过扩展可以确保服务仍然可用,通过按比例收缩可以降低成本。

随着微服务及云原生架构的大规模推广和使用,部署和运维的复杂度会逐渐从业务端下沉到以 Kubernetes 为代表的基础设施 PasS 平台,利用云和微服务架构,我们可以更加快速地部署和交付我们的服务,围绕快速交付的基础设施建设市微服务架构规模化发展的首要任务。

快速演进

软件的固有特性随着时间的推移会变得越来越难以改变,软件的组成部分会因为各种各样的问题变得脆弱,难以操作。
软件和现实世界一样,当人类的需求和环境供给达到平衡时,世界时美好的,然而当这种平衡因为虫害或者气候变化被打破时,人类需要向生态中引入变化,重新建立平衡。
对于软件系统,同样存在这种动态的平衡,我们需要提早对系统进行规划和设计。

尽管很多人喜欢在一个理想的环境下来讨论架构,然而,对于庞大复杂的单体架构,很多因素可能促使我们呢将混乱引入系统工程: 业务需求的快速变化、工作任务的优先级冲突、有限的人力资源和预算、软件工程师水平的参差不齐、缺乏规范的开发流程和部署方式。
另外,如果是遗留系统,还会存在代码版本混乱、冗余的代码逻辑等技术债务。这种技术包袱总会带来灾难性的后果。
通常,业务人员往往不想放弃还在工作的系统,而开发人员,面对单体系统的腐化,只能通过不断地堆叠功能完成任务。
不停地做加法,架构成为塞满各种功能和修复逻辑的庞然大物,最终产生破窗效应。
而这种架构上的缺陷也将持续加重我们的技术债务,业务人员要么忍受这样糟糕的设计、不断地妥协,要么丢弃已有系统,推倒重来,这样的做法对于资源有限的团队和公司来说,显然是难以承受的

微服务架构强调在项目早期将软件分成若干个阶段及不同的模块,从时间、业务维度及架构维度上做水平和垂直化的分解。
微服务构建的首要任务就是理解业务的问题域,好的架构师会充分考虑业务领域的内聚性,降低业务之间的耦合,寻求两者的平衡,并将架构的可扩展性作为重要的设计考量因素。
微服务架构的一个特征就是面向架构演进,微服务架构的目标正是通过业务领域的边界划分、通过服务的隔离来分解问题,逐个击破,因此微服务架构天然具备了可演进性

架构设计哲学

如果说软件开发的本质是不断挖掘问题领域中隐藏的错综复杂性,那么架构解决的问题就是如何管理这些复杂性。
而在软件领域,最为复杂的软件实体莫过于软件操作系统。
从数以千计的工程师参与开发的 UNIX 操作系统到 Linux 开源系统的成功,越来越多的人开始关注和思考 UNIX 技术背后隐藏的设计哲学

UNIX 设计哲学概括为一句话就是 "小而专注"。可以说,微服务架构理念和 UNIX 设计哲学一脉相承,微服务将 UNIX 设计哲学中的核心准则通过概念的抽象,描述成了更加通用的架构风格和设计原则。
下面,让我们跟随经典重新认识在 AT&T 公司诞生的 UNIX 操作系统和它背后的设计哲学,所谓 "温故而知新",这些经典思想能加深我们对微服务架构的认识和理解

小即是美

在软件的设计开发过程中,软件系统的规模很容易膨胀,工程师喜欢将纷繁复杂的功能全部堆积在一个程序中,这样的好处是代码唾手可得。
然而,根据二八法则,实际运行中的代码其实往往只占到我们代码量的 20%。
所以,切忌将大而全的工程作为我们的目标,相反,我们应该将功能设计成为实用且简洁的小程序(微服务)

在作者的职业生涯中,经历过很多设计复杂、规模庞大的单体系统。
有的是基于 C++ 编程语言的后端大型系统,有的是使用 J2EEXML 编程风格、交织着庞大功能模块的巨石型应用,这些项目通常由 10 人以上的研发团队负责,动辄百人/月的开发计划。
这种大型单体系统在上线后,时常发生系统内存溢出、系统宕机等问题,让开发人员心惊胆战。
往往一个小问题会影响整个系统的正常运行,排查解决过程需要检查整个工程的代码逻辑,开发人员疲于解决生产上线后的各种问题和 Bug 修复

事实证明,我们需要将庞大、复杂的系统分解成小程序。正如 UNIX 系统中强调的 KISS(Keep it Simple and Stupid)原则,可以说整个 UNIX 系统都是由数目众多的小程序组合完成各种复杂的操作系统功能的。
每个小程序只完成其中一小部分功能。表面上,这些小程序都很低效,但是正是通过像搭积木一样将不同功能的小程序通过变换顺序和组合的方式,完成了意想不到的丰富而强大的功能。

小程序之美体现在响应变化上。通过小程序可以将这种改变控制在足够小的范围,保证不会给整个系统带来巨大的影响。

小程序之美体现在工程结构的简化的完备上。当你的程序充满了个性化的反射调用和程序员独特的编程思维痕迹,那么应该反省你的代码是不是英脱离了小程序应有的代码简洁和易于理解

小程序之美体现在性能优势上。小程序对外部的依赖少,从而可以快速启动,基于资源收敛式的反应式编程模型可以提升性能并减少资源浪费。

小程序之美体现在团队合作和独立演进上。清晰的边界划分时团队协作的有效手段,而体现在微服务架构上就是服务提供者和消费者可以预先订立契约,可以根据契约独立、并行开发各个服务,这样就实现了服务之间的解耦,使其功能能够独立进化

做好一件事

小程序应该只做好一件事,应该保持对一件事的专注力。
在 UNIX 设计哲学中,解决一个问题并将问题解决导完美,比同时解决多个问题更为重要。
然而在我们的工程中,随着项目的进展,我们很难将庞大的代码库进行清晰的模块划分,更糟糕的是我们很多时候并不知道这些模块之间的服务界限。

在 UNIX 操作系统中,我们可以发现很多命令的强大之处正是只有单一的功能,并将这件事干好,也就是所谓的 "Do one thing, do it well"。
而且 UNIX 首创的管道可以把这些命令任意地组合,以完成一个更为强大的功能。

微服务的目标是独立地完成一件事并做到最好。微服务可以根据业务的边界来确定应用的行为,这样它不仅可以很容易地确定某个功能逻辑对应的代码,而且由于该服务专注在某个边界之内,因此可以更好地避免由于代码库过大衍生出的复杂性问题。

快速建立原型

只做好一件事的小程序可以让我们的项目轻装前行,我们不必再担心系统会成为一个庞然大物而无法控制。
对于单体巨石应用,需要指定周密的计划去编写设计文档,以及为聚合不同模块之间功能而准备脚本和代码,相比之下,自治的小程序更加清楚自己的业务职责和业务范围。
开发人员可以对小程序快速建立原型,缩短服务的交付周期,迅速构建可以供用户使用的程序和服务,并从结果中得到反馈,向最终的目标前进

在 UNIX 设计哲学中,软件发生变化时不可避免的,这种变化来源于沟通的失败、需求理解的差异、知识经验的限制等,所以软件工程相比任何其他工程都更加容易返工,需要软件从业者不断试错、总结、验证,并根据期望重新建立共识。
尽快建立原型是一个重要的步骤,有了具体的原型,可以降低项目的风险,可以给客户展示和进行可视化跟踪。往往这个过程时伴随着系统的迭代和演进的

回到微服务,我们说微服务架构在建立原型上由天然的优势,微服务架构的很多特性能让我们快速落地和逐步地独立完善和迭代项目

  • 微服务架构强调细粒度的服务,在服务规模上尽量精简和务实
  • 微服务架构只做好一件事,没有过多的其他因素干扰,我们可以将注意力集中在完成这件事情上,尽量排除干扰因素
  • 微服务架构提供轻量级的通信集成方式,有利于集成测试和验证结果
  • 微服务架构建立在已有的技术基础之上,能够更加快速地构建、发布和体验应用

软件的复利效应

在软件工程实践中,在项目的启动或者早期阶段,经常会面临技术方案的选择问题。
一些软件工程师会陷入自我保护的状态,认为别人的方案存在缺陷,自己如果重新做会比现有方案做的更好,但其实可能是因为他不了解 "每个软件可能都是在某种约束条件下工作的,而且适用于某些特定的场景"。
我们把这种现象称为 "NIH(Not Invented Here)综合症"(一种文化现象,人们不愿使用某种产品、成果或者知识,不是出于技术等因素,而只是因为它源自其他地方)。
NIH 综合症的结果就是重新发明轮子,患有 NIH 综合症的人相信内部开发更安全、更高效、速度更快、维护成本更低,然而,其实他们并不一定能够使用创新的方案来解决实际的问题。

目前,软件正朝着规模化和标准化的方向发展,标准驱动下的软件开发和集成方式,要求你的工作能够集成到标准中,而不是另起炉灶。
NIH 综合症的危险之处就是,你的软件、你的服务无法与标准对接,你的系统可能成为一个孤岛系统。
例如,在你提供了私有协议的 RPC 方式暴露服务的情况下,你的服务职能生存在自己的闭环体系中,而且基于标准的 HTTP 接口 API 的调用方式很难与你的服务集成。

项目的声明周期受很多因素的影响,预算、人员成本、推广等都会给项目的持续发展带来风险,NIH 综合症会给项目的持续发展带来额外的成本问题,因为你缺少相关人员、资料、生态的支持,而采用标准化的技术可以从互联网中得到更多的技术、社区和人员的支持。

下面让我们看看 UNIX 的实践,所谓"前人种树、后人乘凉"。
查阅 UNIX 坎坷的发展历程,我们就会发现,UNIX 操作系统是在数千名工程师的辛勤努力的基础上发展起来的。
UNIX 中复杂的逻辑都是由小的程序累计而成的,聪明的程序员总是可以借用前人写的优秀的代码实现自己的功能,这样才能更快、更好地扩大软件的影响力和威力,放大自己的工作成果。

在微服务领域,我们已经看到非常多优秀的微服务技术框架

可移植性优先

可移植性在软件工程中的重要性无论怎么强调都不过分,因为这个哲学正是 UNIX 操作系统能够成为 "常青树" 的秘诀。
在 UNIX 环境下,Shell 脚本具备更好的可移植性,Shell 脚本通常由多个 UNIX 命令组成,Shell 可执行文件间接由 UNIX 命令解释器解释执行。
如果你对效率要求不高,可以尽量使用 Shell 脚本执行,Shell 脚本语法简单、使用方便、运行之前不需要编译、具备很强的文本处理能力,开发效率也比较高。

在微服务时代,容器的隔离性和可移植性可以说微软件开发带来了革命性的颠覆,Docker 技术通过采用 LXC(Linux Container的简写,它可以提供轻量级的虚拟化,以便隔离进程和资源)虚拟化手段,利用 Linux 系统的 Namespace 和 Cgroup 技术确保了应用程序与资源的隔离。
Docker 通过和各大厂商联合发起 OCI(开发容器标准),规范了应用运行时的容器镜像标准。
镜像的打包、构建、部署、命名过程都按照统一规范进行,进而标准化了底层运行时支撑环境,这样你就可以在统一的容器环境下灵活地交付、部署和移植代码。
通过采用 Docker 容器技术,我们将不需要再关注操作系统的特殊性和差异性,可以更关注应用程序本身,底层多余的环境因素可以通过容易提供的虚拟环境来屏蔽。
也就是说,我么你的微服务具备了更好的可移植性,真正做到了 "一次构建,随处运行"