微服务:Why | What | How

image.png

客户为「本质需求」买单。

我们有一系列的思维工具,对于微服务的分析,我选择用 what how why 的工具进行分析,不过值得注意的是我略微调整了顺序,这绝非是开创性的的纲领,而是对于这些年对于微服务的再次反思。

Why:为什么是微服务

背景

本章大多数借鉴 参考1

远在 80 年代初,第一种重要的系统分发技术 “远程过程调用 (RPC)” 诞生的时候,微服务的历史就开始了。基本思路都是让远程调用对开发人员保持透明

随着处理器改进和本地地址空间的扩大(当时最常用的处理器是具有 64K 地址空间的 16 位处理器),这个问题变得不太重要。此外,DCE 和 CORBA 的第一组大型实现告诉了架构师一个有关分布式计算的重要观察结论:

某个功能能够分散化,并不代表着它就应该分散化

随着 RPC 的盛行,慢慢的社区构建出了面向服务的架构 SOA,在 SOAP 的核心中,SOAP 只不过是一种通过 HTTP 调用对象方法的方式。它利用了 2000 年代初的计算领域的两个特征: 企业网络中对 HTTP 的支持越来越多,以及事实上此支持包含登录和调试基于文本的网络调用的机制。但 SOA 在整体上的败笔是,它脱离了简单的初衷,开始添加一层又一层脱离了简单方法调用的一些附加概念:添加了异常处理、 事务支持、安全性和数字签名,人们感觉 SOA 已经变成了一个复杂协议。正如 命运给予她的全部馈赠,早已在暗中标好了价格

尝试让分布式调用的行为像本地调用,这最终带来一些苦果。

在推出 JEE 时,许多企业已经转而采用使用应用服务器托管许多不同应用程序的概念,因为它类似于来自大型机领域的现有 IT 模型。一个操作小组控制、监视和维护来自 Oracle 或 IBM 的相同应用服务器的 “农场”,并在这个 “农场” 上部署不同的部门级应用程序。这种标准化和一致性对操作团队很有用,而且降低了总体操作成本。但这与应用程序开发 人员产生了冲突,因为开发和测试环境很大,很难创建,而且需要操作团队干预。这通常意味着新环境可能需要花几个月才能完成创建,这延缓了项目进度并增加了开发成本。此外,因为这些环境不受该团队的控制,所以不同环境的应用服务器版本、补丁级别、应用程序数据和 软件安装常常不一致。开发人员更喜欢更小的、轻量级的应用程序平台 — 通常是一些开源的应用服务器,比如 Tomcat 或 Glassfish。同时,随着控制反转和依赖注入等技术得到普及,人们避开了 JEE 的复杂性,而喜欢上了 Spring 平台所提供的简单性。这么做的收获是,开发团队发现,他们能够自行在彼此尽可能接近的开发、测试和生产环境中 一致地构建和部署应用程序,不仅速度更快,而且出错率更低,因为源于环境不一致性的所有错误都被消除了。

您的程序和它们的运行时环境应尽可能完全独立。


根据上面的3个观察结论,Fowler 的微服务设计原则之一是,微服务是 “围绕业务能力进行组织的”。该原则直接源于一种发现:您能够分散某项功能,并不意味着您就应该分散它。
Façade 模式在其各种表现形式中的整体概念是,为系统或子系统定义一个特定的外部 API。言外之意是,这个 API 是业务驱动的 [作者注:这也就诞生了 API Gateway 这样的 Pattern] 。使用 EJB、SOAP 和其他复杂分发技术的团队最终发现,尝试让分布式系统看起来像本地系统最终会带来苦果。最后,Fowler 围绕分散化治理和分散化数据管理的规定源于一项来之不易的发现:您的程序和运行时环境应自给自足。

定义

目的

还记得我在 为何有些软件知识自学如此的困难#什么是技术 中给大家分享技术的定义吗?我们来解构一下 微服务 的第一要素:

微服务的目的:构建关注于单一目标自然的程序,如果有人和你说微服务是为了解决高并发,那完全就是扯淡,回忆一下 martinfowler 在 微服务 的定义中所言

We do not claim that the microservice style is novel or innovative, its roots go back at least to the design principles of Unix.

试图回忆起来那些 Unix 最基本的哲学

程序应该只关注一个目标,并尽可能把它做好。让程序能够互相协同工作。应该让程序处理文本数据流,因为这是一个通用的接口。

特征

本章大多数借鉴 参考3

服务组件化

当谈到组件时,我们遭遇困难的定义:组件是什么。我们的定义是:组件是一个可独立替换和独立升级的软件单元。微服务架构将使用库,但组件化软件的主要方式是分解成服务。我们把库定义为链接到程序并使用内存函数调用来调用的组件,而服务是一种进程外的组件,它通过web服务请求或rpc(远程过程调用)机制通信。

围绕业务能力组织

当想要把大型应用程序拆分成部件时,通常管理层聚焦在技术层面,导致UI团队、服务侧逻辑团队、数据库团队的划分。当团队按这些技术线路划分时,即使是简单的更改也会导致跨团队的时间和预算审批。一个聪明的团队将围绕这些优化,两害取其轻 - 只把业务逻辑强制放在它们会访问的应用程序中。换句话说,逻辑无处不在。

任何设计系统(广泛定义的)的组织将产生一种设计,他的结构就是该组织的通信结构。
– Melvyn Conway1967

UIeZf.png

对于一个微服务项目应该有多大的规模?无论是亚马逊两匹萨团队还是重构二周论,都算是一种实践的体现,对于项目有多大,应该是在最终的实践过程中摸索出一套对企业适用的结果。

交付产品不是项目

我们看到大多数应用程序开发工作使用一个项目模式:目标是交付将要完成的一些软件。完成后的软件被交接给维护组织,然后它的构建团队就解散了。
微服务支持者倾向于避免这种模式,而是认为一个团队应该负责产品的整个生命周期。对此一个共同的启示是亚马逊的理念 “you build, you run it” ,开发团队负责软件的整个产品周期。

这样的体系需要警惕: 团队的无限拓展导致的重复建设与疲于奔命为什么我厌倦了产品研发,you build, you run it 而不是 you build everything,基础设施的完善是必须的。

简单通讯

微服务社区主张另一种方法:智能端点和哑管道。基于微服务构建的应用程序的目标是尽可能的解耦和尽可能的内聚 - 他们拥有自己的领域逻辑,他们的行为更像经典UNIX理念中的过滤器 - 接收请求,应用适当的逻辑并产生响应。使用简单的REST风格的协议来编排他们,而不是使用像WS-Choreography或者BPEL或者通过中心工具编制(orchestration)等复杂的协议。

第二种常用方法是在轻量级消息总线上传递消息。选择的基础设施是典型的哑的(哑在这里只充当消息路由器) - 像RabbitMQ或ZeroMQ这样简单的实现仅仅提供一个可靠的异步交换结构 - 在服务里,智能仍旧存活于端点中,生产和消费消息。

去中心化治理

团队在构建微服务时也更喜欢用不同的方法来达标。他们更喜欢生产有用的工具这种想法,而不是写在纸上的标准,这样其他开发者可以用这些工具解决他们所面临的相似的问题。有时,这些工具通常在实施中收获并与更广泛的群体共享,但不完全使用一个内部开源模型。现在git和github已经成为事实上的版本控制系统的选择,在内部开放源代码的实践也正变得越来越常见。

去中心化数据管理

数据管理的去中心化有许多不同的呈现方式。在最抽象的层面上,这意味着使系统间存在差异的世界概念模型。在整合一个大型企业时,客户的销售视图将不同于支持视图,这是一个常见的问题。客户的销售视图中的一些事情可能不会出现在支持视图中。它们确实可能有不同的属性和(更坏的)共同属性,这些共同属性在语义上有微妙的不同。

这个问题常见于应用程序之间,但也可能发生在应用程序内部,尤其当应用程序被划分成分离的组件时。一个有用的思维方式是有界上下文(Bounded Context)内的领域驱动设计(Domain-Driven Design, DDD)理念。DDD把一个复杂域划分成多个有界的上下文,并且映射出它们之间的关系。这个过程对单体架构和微服务架构都是有用的,但在服务和上下文边界间有天然的相关性,边界有助于澄清和加强分离,就像业务能力部分描述的那样。

像这样使用事务有助于一致性,但会产生显著地临时耦合,这在横跨多个服务时是有问题的。分布式事务是出了名的难以实现,因此微服务架构强调服务间的无事务协作,对一致性可能只是最后一致性和通过补偿操作处理问题有明确的认知。

对很多开发团队来说,选择用这样的方式管理不一致性是一个新的挑战,但这通常与业务实践相匹配。通常业务处理一定程度的不一致,以快速响应需求,同时有某些类型的逆转过程来处理错误。这种权衡是值得的,只要修复错误的代价小于更大一致性下损失业务的代价。

UI8aw.png

基础设施自动化

我们这里将之光住几个关键特性。我们希望有尽可能多的信心,我们的软件正在工作,所以我们运行大量的自动化测试。促进科工作软件沿管道线“向上”意味着我们自动化部署到每个新的环境中。

一个单体应用程序可以十分愉快地通过这些环境被构建、测试和推送。事实证明,一旦你为单体投入了自动化生产之路,那么部署更多的应用程序似乎也不会更可怕。请记住,持续部署的目标之一是使部署枯燥,所以无论是一个或三个应用程序,只要它的部署仍然枯燥就没关系。

UIcri.png

为失效设计

使用服务作为组件的一个结果是,应用程序需要被设计成能够容忍服务失效。任何服务调用都可能因为供应者不可用而失败,客户端必须尽可能优雅的应对这种失败。与单体应用设计相比这是一个劣势,因为它引入额外的复杂性来处理它。结果是,微服务团队不断反思服务失效如何影响用户体验。Netflix的Simian Army在工作日诱导服务甚至是数据中心故障来测试应用程序的弹性和监测。

在生产环境中的这种自动化测试足够给大多数运营团队那种不寒而栗,通常在结束一周的工作之前。这不是说单体风格不能够进行完善的监测设置,只是在我们的经验中比较少见。

我们并非是保证系统永不宕机,我们是需要保证某节点故障的时候不会影响到全局系统,并且可以快速恢复。

进化式设计

微服务从业者,通常有进化式设计背景并且把服务分解看做是进一步的工具,使应用程序开发者能够控制他们应用程序中的变更而不减缓变更。变更控制并不一定意味着变更的减少 - 用正确的态度和工具,你可以频繁、快速且控制良好的改变软件。

明显的缺陷

  • 很难弄清楚组件边界在哪里,进化式的设计需要大量的迭代
  • 重构微服务系统困难重重,跨服务边界移动代码是很困难的,任何接口变更都需要在参与者之间进行协调,需要添加向后兼容层,并且测试也变得更加复杂
  • 需要基础化的自动化/基础设施
  • 如果组件不组成的干净利索,那么所有你做的是将复杂度从组件内部转移到组件之间的连接。不仅仅是把复杂性移到周围,它将复杂性移动到一个不太明确、难以控制的地方
  • 一个差的团队总是创建一个差的系统,(有一个合理的说法是,你不应该从微服务架构开始。相反,从单体开始,使它保持模块化,一旦单体成为问题时把它分解成微服务)

还有一些关于不太明显的缺陷可以阅读参考9


上面的几个特性可以看作是微服务的实践,而关于微服务的元器件我们在后续的内容中继续探讨。在而这过程之中,我和大家聊一聊,我们跳出技术的层面看,微服务给我们带来什么。看看 Devops

微服务解决的问题

UIUKk.png
我们还记得Devops的内容吧。

DevOps(开发 Development 与运维 Operations 的组合词)是一种文化、一场运动或实践,强调在自动化软件交付流程及基础设施变更过程中,软件开发人员与其他信息技术(IT)专业人员彼此之间的协作与沟通。它旨在建立一种文化与环境,使构建、测试、软件发布得以快速、频繁以及更加稳定地进行。

在我的眼中,我认为 微服务Devops 的环中的一些细分理念/实践。

Devops 的自动化也是 微服务 的基石,而 Devops 所提出来的快速/频繁更新就要求系统能够在较小的范围内进行迭代,而这恰好与 微服务 小组件化的理念不谋而合,而微服务的系统因为是自然的也方便 Deploy,而监控这些也作为微服务必要的基石。


因为其实对于我们来说,最终的价值要体现在 业务侧,因此 Devops 通过提高效率 参考4 体现价值,而 Microservices 通过支撑 Devops 而产生价值,因此 Microservices 本身就是一个非显性的价值,作为售卖的对象来说,可能并不是一个良好的对象。

我认为,Devops是一套企业的流程优化,华为40亿师从IBM,也是因为IBM有着壮士断腕的气魄且成功了,如果隔壁老王给你讲Devops,你可能就不会相信。毕竟是非经过不知难,想要给别人讲Devops,没实践是不行的。

为何需要微服务

从技术层面上想微服务,那就是锤子的眼里都是钉子。我们先来看看现代的应用特性。

现代应用

在 2020 年,人类迈入信息科技时代多年之后,我们对于应用的需求依然保持着朴质的需求,健壮性,安全性,高效性。但是变化的是经过这么多年的信息化熏陶,我们对于系统的易用性提出了更多的要求,可以理解成,我们对于软件性本身的要求没有本质上的变化,但是由于业务系统复杂化,现代应用所需要面临的是在如何在规模化的业务诉求下依然保持着较高软件特性 参考5

可能上面的话有些抽象,说起来举一些例子

  • 淘宝商城因为功能变的越来越多导致代码激增,单一仓库连提交代码都是困难
  • 因为用户规模的激增我们需要更大的吞吐量
  • 因为定时秒杀的场景,我们需要更加适合的弹性扩容的基础设施等

现代软件的应对之策

现代应用因为需要解决上面所列出的问题,所以不得不在最终的软件架构上做出妥协。

  • 单体应用太大不好改 -> 拆分服务化
  • 用户规模太大 -> 分布式
  • 流量不均衡 -> 易于弹性伸缩

但是显然一步不能走完所有的路。新的解决之道又往往带来新的问题。分布式 架构带来的高可用的问题,弹性伸缩 又依赖完善的 基础设施,但是如同 Martin 所言,这一切都是向着更正向的反馈,我们努力攻克下这些问题,诞生了容器化,所以就促成了 The Twelve-Factor App Modern App 这一些范式。

从上面我们可以看出整个技术的进步脉络:

业 务 复 杂 度 上 升 --> 技 术 尝 试 解 决 --> 形 成 最 佳 实 践

微服务

从朴质的逻辑中可以简单的定义出,微服务 即是当前在特定规模下的软件形态的最佳实践。

到底多大的规模应该微服务化,这并没有标准答案,但是我认为有两点是值得肯定的,1. 如果现有的软件架构不能够完全满足业务的需求,就可以尝试微服务。2. 如果现在体系还不可以,也可以试试微服务架构也许会有一些新收获。

至于微服务的优势,烦请大家Google一下,就不做展开。

What:什么是微服务的形态

ULPIr.png

《涅槃经》卷三○载:“其触牙者,即言象形如莱茯根(萝卜);其触耳者,言象如箕;其触头者,言象如石;其触鼻者,言象如杵;其触脚者,言象如木臼;其触脊者,言象如床;其触腹者,言象如瓮;其触尾者,言象如绳。”

微服务是什么,我们将微服务的特征已经在 微服务:Why 定义清楚,我们下一步看看更为具型的形态。

微服务的形态

其实对于微服务的形态,Chris Richardson微服务架构 中已经罗列的非常的清晰了。我在这里将一些比较核心的东西再加一些个人的思考重试一下。

ULtNW.png

服务发现

ULN02.png

因为微服务架构天生会导致部署的实例变多,服务实例的动态变迁这件事情还是需要自动化的。业界的实现模式常见为

  • Spring Cloud Discovery 基于 Eureka 这样的独立的服务发现软件
  • Kubernetes DNS:因为 K8s 本身可以感知 POD 的变迁,因此直接从 Api server 获得也很正常

ULrbL.png

不过因为 DNS 本身就是一个很不错的分布式系统套件,其实从个人角度看,我认为第二个方案还是更适合。并且有大量的新技术也是基于此技术,比如 SericeMesh,可以选的选择此方案更靠谱点。

配置信息外部化

apollo nacos configmap

其实从经验看起来,在小规模的情况下,Kubernetes 自带的 configmap 反而是最好用的。

API 网关

ULsof.png

API 网关是一个非常可以标准化的市场,有成熟产品的 Tyk Kong,有框架性质的 Spring Gateway等等,这块直接选择一个自己看着爽的用就好了,其实也没多大区别。

熔断器

ULEDU.png
熔断器其实蛮适合从 Service Mesh 做的。

链路追踪

ULk6O.png


Wait… 停住,为什么微服务有这么多的 Pattern ,不知道读者朋友们有没有疑问。我在初步接触微服务的时候,我也常常会有这样的疑问,当我搞清楚微服务其实只是一组最佳实践之后我就明白了,这些东西是其他的架构师同行们在多次的实践的过程中帮我们抽象出来的 最佳模式,而我们也并非是要去背上这些模式再去设计微服务架构,我们也是因为在实践的过程中遇见自己的问题之后才对症找药。

过早的优化是一切罪恶的根源。 – by: Donald.Knuth

How:如何落地微服务

ULAUp.png

讲完那么多的理论,我们终于到了落地的阶段,微服务怎么落地,这是大概是我们最有价值的事情了(因为微服务最有价值的恰恰就是最佳实践)。

准则

  • 微服务没有标准答案,阿里的微服务不一定适应所有人
  • 记住微服务的第一特性:面向业务
  • 微服务架构是进步式的

因为本章需要大量的实践经验,因地制宜

实践步骤

ULJ5S.png

Step 1: 业务梳理

将现有的业务进行梳理,业务专家或者测试专家,甚至于使用者都可以,我们需要将整个业务系统完整的展示在大家面前。

输出物: 业务流程报告 用户故事以及用户故事地图

Step 2:业务建模

业务架构师进场,分析客户的业务,我们需要将业务划分为相对独立的业务单元,我们可以利用 实现领域驱动设计(DDD) 的技术进行划分。在此阶段一定要忘记你所拥有的技术,在此阶段我们的目标是明确的将业务划分单元。尽可能的保证 保证单一业务服务高效聚合 降低服务间的相互调用

输出物: 业务划分地图 业务交互图

Step 3:基础设施建设

对于一个微服务系统,我们先看一下最简单的系统构成
ULcxT.png
从这张图中,我们至少可以发现我们需要为这些服务准备好异步通讯的 MQ,和统一对外暴露的 BFF,并且和调用这些系统的 Docker 治理平台,当我们展开内部的微服务的时候

ULb6E.png

我们又看到了不同的数据库。因此我们首先要需要设计出这样的一套架构图。并且将这套系统内非业务属性的单元都作为基础设计进行建设。

常见的基础设施 KV Databse Search Engine Steam Monitoring Tracing Message Queue Container Orchestration CICD 等等

这在企业内部并不是一个很有价值的事情,但是又是耗时最多,并且在后期大部分时候大家都在忙不迭的处理这些系统的HA问题,建议大家在初期阶段就能够做完性能测试。

Step 4:Runtime技术选型

不同的 modules 自然有不同的选型,针对 Go体系 Java体系 在自己的领域选择热门的技术即可。
Java体系 选择 Spring Cloud 就是一个相对比较好的选择。不过也需要参考本企业内的技术专家的技术体系

Step 5: 构建系统

按照各种范式构建系统即可。

Step 6:持续迭代

Go to Step 1

关于咨询

实际上在这一块,无论是国内外都是让参与过微服务改造的工程师对需求方进行量体裁衣。实际上并不会有什么特别的地方,这里的内容非常的像:

王刚也是这么做菜的,但是你做就是不好吃的状态。

此之过程具有价值的乃是经验,而师傅只能教你怎么做菜。

切记方法论千千万,八仙过海,各显神通。

参考

1. 寻根溯源:微服务模式发展简史
2. microservices
3. 微服务架构
4. DevOps是如何实现效率的提升
5. 基于服务的架构对现代软件设计的影响
6. 微服务落地,我们在考虑什么
7. Design a microservice-oriented application
8. 从300万行到50万行代码,遗留系统的微服务改造
9.The seven deceptions of microservices