02微服务系统与设计(D1_走出微服务误区:避免从单体到分布式单体)

目录

学习前言

一、回顾:从单体到微服务到 Function

二、分布式单体

分布式单体起因之一:通过共享库和网络客户端访问分布式能力

分布式单体起因之二:简单用远程调用替代进程内方法调用

分布式单体起因小结

三、引入非侵入式方案:物理隔离+逻辑抽象

四、引入 Event:解除不必要的强耦合

1. 温故而知新:Event 和 Command

1.1. 什么是 Event?

1.2. 什么是 Command?

1.3. Command 和 Event 总结

2. 从业务视角出发:关系模型决定通讯行为

3. 全程 Command 带来的问题:不必要的强耦合

编排和协调

4. 全程 Event 带来的问题:开发困难和业务边界不清晰

五、Command 和 Event 的选择:实事求是不偏不倚

六、总结与反思

警惕:不要沦为分布式单体

反思:喧闹和谩骂之外的冷静思考

七、参考资料和推荐阅读

八、参考文献


学习前言

最近社区频繁出现的对微服务的各种质疑和反思的声音,甚至放弃微服务回归单体。

本文从“分布式单体”问题出发,介绍通过引入非侵入式方案和引入Event/EDA 来走出微服务实践误区:

从单体到微服务,却最后沦为分布式单体。

一、回顾:从单体到微服务到 Function

在过去几年间,微服务架构成为业界主流,很多公司开始采用微服务,并迁移原有的单体应用迁移到微服务架构。

从架构上,微服务和单体最大的变化在于微服务架构下应用的粒度被“拆小”:将所有业务逻辑都在一起的单体应

用,按照领域模型拆分为多个内聚而自治的“更小”的应用。而 Function 则在拆分上更进一步,拆分粒度变成了

“单个操作”,基于 Function 逐渐演进出现 FaaS 形态和 Serverless 架构。

在微服务和 Serverless 的喧嚣中,也逐渐出现了很多质疑和反对的声音:越来越多的人发现,当他们兴冲冲的迁

移单体应用到微服务和 Serverless 架构之后,得到的收益并没有期望中的那么理想。最近,出现了对微服务的各

种质疑、反思的声音,甚至放弃微服务回归单体。

无论是支持还是反对微服务的声音,大多都是着眼于组织架构(康威定律,对应用和代码的 ownership)、微服

务拆分(粒度大小,如何识别领域模型和业务边界)、分布式事务(跨多个微服务调用时维持一致性),工具(自

动化构建、部署,可测试性,监控,分布式链路跟踪,CI/CD),数据库分离(避免多个微服务尤其是领域模型外

的微服务共享数据库)等方面进行合理性分析和观点阐述,相信大家都对这些问题都有了解。

而我今天的文章,将从另外一个角度来看待微服务(也包括 Serverless)实践中存在的误区——辛辛苦苦从单体

走到微服务,却最后沦为

二、分布式单体

“Distributed Monolith”,分布式单体,这真是一个悲伤的技术术语。而这偏偏是企业采用微服务后通常最容

易踏进去的一个“陷阱”,事实上我看到的很多微服务落地最终都是以"分布式单体"收场,无法获得微服务的完整

收益。

问题源于微服务实施的方式 —— 按照业务逻辑拆解单体,划分为多个微服务,定义 API 接口,然后通过 REST 或

者 RPC 进行远程调用,最终把这些微服务组合起来提供各种业务功能。简单说,就是在业务拆分的基础上,用进

程间的远程调用简单替代原来进程内的方法调用

期间,对于原来使用的各种分布式能力,继续沿用之前的方式,简单说:方式不变,只是粒度变小。

从方法论说这样做无可厚非,这也是微服务采用过程中非常标准的做法。但问题在于,止步于此是不够的 —— 至

少存在两个有待继续努力改进的地方。

分布式单体起因之一:通过共享库和网络客户端访问分布式能力

分布式能力的共享库和网络客户端是造成分布式单体问题的原因之一,关于这一点,来自 verizon 的 Mohamad

Byan 在他的名为 Avoid the Distributed Monolith!! 的演讲中有详细的阐述,我这里援引他的图片和观点:

上图是微服务体系的逻辑架构,由两部分组成:

  • 内层架构(图上浅蓝色部分),是每个微服务的实现架构;
  • 外层架构(图上黄色部分),是构建强大微服务架构所需要的各种能力,这里通常有大家熟悉的各种分布式能

力;

特别提示:这里说的“网络客户端”是各种分布式能力的客户端,如服务注册发现/ MQ 中间件/ Redis 等 key-

value 存储/数据库/监控日志追踪系统/安全体系等,不是服务间通讯如 RPC 的客户端。

而内层的微服务是通过 共享类库网络客户端 来访问外层架构提供的分布式能力:

分布式能力的 共享类库网络客户端 会迫使内层微服务和外层架构的各种分布式能力之间产生强耦合,增加运

维的复杂性(如升级困难造成版本碎片化),多语言受限于类库和网络客户端支持的语言,各种组件(如消息中间

件)往往使用自定义数据格式和通讯协议 —— 所有这些迫使内层微服务不得不实质性受限于外层架构的技术选型。

对于 Function,这个问题就更加明显了:Function 的粒度更小,更专注业务逻辑。某些简短的 Function 可能只

有几百行代码,但是,为了让这几百行代码运转起来而需要引入的共享类库和网络客户端可能相比之下就规模惊人

了。援引一张网上图片作为示意:

分布式单体起因之二:简单用远程调用替代进程内方法调用

在微服务架构改造过程中,熟悉单体系统和架构的开发人员,习惯性的会将这些单体时代的知识和经验重用到新的

微服务架构之中。其中最典型的做法就是:在遵循领域模型将现有单体应用按照业务边界拆分为多个微服务时,往

往选择用 REST 或者 RPC 等远程调用方式简单替代原有的进程内方法调用。

当两个逻辑上的业务模块存在协作需求时:

多增加了 Sidecar 节点,并未改变远程调用的性质:

这导致了前面所说的 “分布式单体”:

  • 在微服务之前:应用程序由多个耦合在一起的模块组成,这些模块通过内存空间进行方法调用…..
  • 在微服务之后:应用程序由多个耦合在一起的微服务组成,这些微服务通过网络进行远程调用…..

抛开调用方式的差异来看采用微服务前后的系统架构,会发现:两者几乎是完全一样的!!

而微服务版本在某些情况下可能表现的更糟糕:因为调用方式更脆弱,因为网络远比内存不可靠。而我们将网络

当成 “胶水” 来使用,试图把分散的业务逻辑模块(已经拆分为微服务)按照单体时代的同样方式简单粘在一起,

这当然比单体在同一个进程内直接方法调用更加的不可靠。

关于这一点,在 "The Eight Fallacies of Distributed Computing/分布式计算的8个谬论" 一文中有详细阐述。

类似的,在采用 Function 时,如果依然沿用上面的方式,以单体或微服务架构的思维方式和设计模式来创建

FaaS/Serverless 架构:

其本质不会发生变化 —— 不过是将微服务变成粒度更小的函数,导致系统中的远程调用数量大为增加:

系统内的耦合并没有发生变化,Serverless 并不能改变微服务中存在的这个内部耦合问题:调用在哪里,则耦合

就在哪里!只是把将组件的粒度从 “微服务“换成了 “Function/函数”。

耦合的存在是源于系统不同组件之间的通讯模式,而不是实现通讯的技术

如果让两个组件通过“调用”(后面再展开讲何为调用)进行远程通信,那么不管调用是如何实现的,这两个组件

都是紧密耦合。因此,当系统从单体到微服务到 Serverless,如果止步于简单的用远程调用替代进程内方法调

用,那么系统依然是高度耦合的,从这个角度来说:单体应用 ≈ 分布式单体 ≈ Serverless 单体

分布式单体起因小结

上面我们列出了微服务和 Serverless 实践中容易形成“分布式单体”的两个主要原因:

  • 通过共享库和网络客户端访问分布式能力;
  • 简单用远程调用替代进程内方法调用;

下面我们针对这两个问题探讨解决的思路和对策。

三、引入非侵入式方案:物理隔离+逻辑抽象

前面谈到分布式单体产生的一个原因是“通过共享库和网络客户端访问分布式能力”,造成微服务和 Lambda 函

数和分布式能力强耦合。以 Service Mesh 为典型代表的非侵入式方案是解决这一问题的有效手段,其他类似方案

有 RSocket / Multiple Runtime Architecture,以及数据库和消息的 Mesh 化产品,其基本思路有两点:

  • 委托:通过 Sidecar 或者 Runtime 来进行对分布式能力的访问,避免应用和提供分布式能力的组件直接通讯

造成强绑定 —— 通过物理隔离进行解耦

  • 抽象:对内层微服务隐藏实现细节,只暴露网络协议和数据契约,将外围架构的各种分布式能力以 API 的方

式暴露出来,而屏蔽提供这些能力的具体实现 —— 通过逻辑抽象进行解耦

以 Service Mesh 的 Sidecar 为例,在植入 Sidecar 之后,业务应用需要直接对接的分布式能力就大为减少

(物理隔离):

最近出现的 Multiple Runtime / Mecha 架构,以及遵循这一架构思想的微软开源产品 Dapr ,则将这个做法推

进到服务间通讯之外更多的分布式能力。

此外在委托之外,还提供对分布式能力的抽象。比如在 Dapr 中,业务应用只需要使用 Dapr 提供的标准 API,就

可以使用这些分布式能力而无法关注提供这些能力的具体产品(逻辑抽象):

以 pub-sub 模型中的发消息为例,这是 Dapr 提供的 Java 客户端 SDK API:

public interface DaprClient {
    Mono<Void> publishEvent(String topic, Object event);
   Mono<Void> publishEvent(String topic, Object event, Map<String, String> metadata);
}

可见在发送事件时,Dapr 完全屏蔽了底层消息机制的具体实现,通过客户端 SDK 为应用提供发送消息的高层抽

象,在 Dapr Runtime 中对接底层 MQ 实现——完全解耦应用和 MQ:

四、引入 Event:解除不必要的强耦合

在解决了微服务/ Serverless 系统和外部分布式能力之间紧耦合的问题之后,我们继续看微服务/Serverless 系统

内部紧耦合的问题。前面讨论到,从单体到微服务到 Function/Serverless,如果只是简单的将直接方法调用替换

为远程调用(REST 或者 RPC),那么两个通讯的模块之间会因为这个紧密耦合的调用而形成依赖,而且依赖关系

会伴随调用链继续传递,导致形成一个树形的依赖关系网络,表现为系统间的高度耦合:

要解决这个问题,基本思路在于审视两个组件之间通讯行为的业务语义,然后据此决定两者之间究竟是应该采用

Command/命令模式还是 Event/事件模式。

1. 温故而知新:Event 和 Command

首先我们来温习一下 Event 和 Command 的概念和差别,借用一张图片,总结的非常到位:

1.1. 什么是 Event?

Event: “A significant change in state” — K. Mani Chandy.

Event 代表领域中已经发生的事情:通常意味着有行为(Action)已经发生,有状态(Status)已经改变。

因为是已经发生的事情,因此:

  • Event 可以被理解为是对已经发生的事实的客观陈述;
  • 这意味着 Event 通常是不可变的:Event 的信息(代表着客观事实)不能被篡改,Event 的产生不能逆转;
  • 命名:Event 通常以动词的完成时态命名,如 UserRegistredEvent;

产生 Event 的目标是为了接下来的 Event 传播:

  • 将已经发生的 Event 通知给对此感兴趣的观察者;
  • 收到 Event 的观察者将根据 Event 的内容进行判断和决策:可能会有接下来的动作(Action),有些动作可能需要和其他模块通讯+ 而触发命令(Command),这些动作执行完毕可能会造成领域状态的改变从而继续触发新的事件(Event);

Event 传播的方式

  • Event 有明确的“源/source”,即 Event 产生(或者说状态改变)的发生地; 但由于生产者并不知道(不

愿意/不关心)会有哪些观察者对 Event 感兴趣,因此 Event 中并不包含“目的地/Destination”信息;

  • Event 通常是通过 MessageQueue 机制,以 pub-sub 的方式传播;
  • Event 通常不需要回复( reply)或者应答(response);
  • Event 通常用发布(publish);

1.2. 什么是 Command?

Command 用于传递一个要求执行某个动作(Action)的请求。

Command 代表将要发生的事情:

  • 通常意味着行为(Action)还未发生但即将发生(如果请求被接受和执行);
  • 存在被拒绝的可能:不愿意执行(参数校验失败,权限不足),不能执行(接收者故障或者资源无法访问);
  • 命名:Command 通常以动词的普通形态命名,如 UserRegisterCommand;

产生 Command 的目标是为了接下来的 Command 执行:

  • 将 Command 发送给期望的执行者;
  • 收到 Command 的执行者将根据 Command 的要求进行执行:在执行的过程中内部可能有多个动作(Action),有些动作可能需要和其他模块通讯而触发命令(Command),这些动作执行完毕可能会造成领域状态的改变从而继续触发新的事件(Event);

Command 的传播方式:

  • Command 有明确的源(Source),即 Command 的发起者;
  • Command 也有非常明确的执行者(而且通常是一个),因此命名通常包含“目的地/Destination”信息;
  • Command 通常是通过 HTTP / RPC 这样的点对点远程通讯机制,通常是同步;
  • Command 通常需要应答(Response):回应 Command 是否被执行(因为可能被拒绝),执行结果(因为可能执行失败);
  • Command 通常用发送(Send);

1.3. Command 和 Event 总结

总结 —— Command 和 Event 的本质区别在于他们的意图:

  • Command 的意图是 告知希望发生的事情
  • Event 的意图是 告知已经发生的事情

意图上的差异最终会在服务间依赖关系上有特别的体现:

  • Command 的发起者必须明确知晓 Command 的接收者并明确指示需要做什么(所谓的命令、指示、操纵、

编排),尤其当发起者连续发出多个 Command 时,通常这些 Command 会有非常明确的顺序和逻辑关

系,以组合为特定的业务逻辑;

Command 的依赖关系简单明确: 发起者 “显式依赖” 接收者;

  • Event 的发起者只需负责发布 Event,而无需关注 Event 的接收者,包括接收者是谁(一个还是多个)以及

会做什么(所谓的通知、驱动、协调)。即使 Event 实际有多个接收者,这些接受者之间往往没有明确的顺

序关系,其处理过程中的业务逻辑也往往是彼此独立的。

Event 的依赖关系稍微复杂一些:发起者明确不依赖接收者,接收者则存在对发起者 “隐式的反向依赖” ——反

向是指和 Command 的依赖关系相比方向调转,是接受者反过来依赖发起者;隐式则是指这种依赖只体现于 “接

受者依赖 Event,而 Event 是由发起者发布” 的间接关系中,接受者和发起者之间并不存在直接依赖关系。

2. 从业务视角出发:关系模型决定通讯行为

在温习完 Command 和 Event 之后,我们再来看我们前面的问题:为什么简单的将直接方法调用替换为远程调用

(REST 或者 RPC)会出问题?主要原因是在这个替换过程中,所谓简单是指不假思索直接选择远程调用,也就是

选择全程 Command 方式:

真实业务场景下各个组件(微服务或者 Function)的业务逻辑关系,通常不会像上图这么夸张,不应该全是

Command (后面会谈到也不应该全是 Event) ,而应该是类似下图描述的两者结合,以微服务为例(Function

类推):

  1. 业务输入:图上微服务A接收到业务请求的输入(可能是 Command 方式,也可能是 Event 方式)
  2. 业务逻辑 “实现” 的执行过程:
  • 微服务 A 在执行 Command(或者被 Event 触发)的过程中,会有很多动作(Action);
  • 有些是微服务 A 内部的动作,比如操作数据库,操作 key-value 存储,内存中的业务逻辑处理等;
  • 有些是和外部微服务进行通讯,如执行查询或要求对方进行某些操作,这些通讯方式是以 Command 的形

式,如图上和微服务 B 的通讯;

  • 在这些内部和外部动作完成之后,执行过程完成;
  • 如果是 Command,则需要以应答的形式给回 Command 操作的结果;
  1. 业务状态变更 触发 的后续行为:
  • 在上面的执行过程完成后,如果涉及到业务状态的变更,则需要为此发布事件;
  • 事件通过 event bus 分发给对该事件感兴趣的其他微服务:注意这个过程是解耦的,微服务A不清楚也不关心哪些微服务对此事件感兴趣,事件也不需要应答;

上面微服务 A 的业务逻辑执行处理过程中,需要以 Command 或者 Event 方式和其他微服务通讯,如图中的微服

务 B/C/D/E。而对于这些微服务 B/C/D/E(视为微服务 A 的下游服务),他们在接受到业务请求后的处理流程和

微服务A的处理流程是类似的。

因此我们可以简单推导一下,当业务处理逻辑从微服务 A 延展到微服务 A 的下游服务(图中的微服务 B/C/D/E)

时的场景:

将图中涉及的微服务 A/B/C/D/E 在处理业务逻辑的行为总结下来,通讯行为大体是一样的:

抽象起来,一个典型的微服务在业务处理流程中的通讯行为可以概括为以下四点:

  • 输入:以一个 Command 请求或者一个 Event 通知为输入,这是业务处理流程的起点;
  • 内部 Action:微服务的内部逻辑,典型如数据库操作,访问 redis 等 key-value 存储(对应于 Multiple Runtime/Mecha 架构中的各种分布式能力)。可选,通常为 0-N 个;
  • 外部访问:以 Command 形式访问外部的其他微服务。可选,通常为 0-N 个;
  • 通告变更:以 Event 形式对外发布事件,通告上述操作产生的业务状态的变更。可选,通常为 0-1 个;

在这个行为模式中,2 和 3 是没有顺序的,而且可能交错执行,而 4 通常都是在流程的最后:只有当各种内部

Action 和外部 Command 都完成,业务逻辑实现结束,状态变更完成,“木已成舟”才能以 Event 的方式对外

发布:“操作已完成,状态已变更,望周知”。

这里我们回顾一下前面的总结 —— Event 和 Command 的本质区别在于他们的意图:

  • Event 的意图是告知已经发生的事情;
  • Command 的意图是告知希望发生的事情;

从业务逻辑处理的角度来看,外部访问的 Command 和内部操作的 Action 是业务逻辑的 “实现” 部分:这些操

作组成了完整的业务逻辑——如果这些操作失败,则业务处理将会直接影响(失败或者部分失败)。而发布事件

则是业务逻辑完成之后的后续 “通知” 部分:当业务逻辑处理完毕,状态变更完成后,以事件的方式驱动后续的

进一步处理。注意是驱动,而不是直接操纵。 从时间线的角度来看整个业务处理流程如下图所示:

3. 全程 Command 带来的问题:不必要的强耦合

全程 Command 的微服务系统,存在的问题就是在上述最后阶段的“状态变更通知”环节,没有采用 Event 和

pub-sub 模型,而是继续使用 Command 逐个调用下游相关的其他微服务:

Event 可以解耦生产者和消费者,因此图中的微服务 A 和微服务 C/D/E 之间没有强烈的依赖关系,彼此无需锁定

对方的存在。

但是 Command 不同,在采用 Command 方式后微服务 A 和下游相关微服务 C/D/E 会形成强依赖,而且这种依

赖关系会蔓延,最终导致形成一颗巨大而深层次的依赖树,而 Function 由于粒度更细,问题往往更严重:

而如果在“状态变更通知”环节引入 Event,则可以解耦微服务和下游被通知的微服务,从而将依赖关系解除,避

免无限制的蔓延。如下图所示,左边图形是使用 Event 代替 Command 来进行状态变更通知之后的依赖关系,考

虑到 Event 对生产者和消费者的解耦作用,我们“斩断”绿色的 Event 箭头,这样就得到了右边这样一个被分解

为多个小范围依赖树的系统依赖关系图:

对 Event 和 Command 使用的建议:

  • 在单体应用拆分为微服务时,不应该简单的将原有的方法调用替换为 Command;
  • 应该审视每个调用在业务逻辑上的语义:是业务逻辑执行的组成部分?还是执行完成之后的状态通知?
  • 然后据此决定采用 Command 还是 Event;

编排和协调

在 Command 和 Event 的使用上,还有两个概念:编排和协调。

这里强烈推荐一篇博客文章, Microservices Choreography vs Orchestration: The Benefits of Choreography,作者 Jonathan Schabowsky ,Solace 的 CTO。他在这边博客中总结了让微服务协同工作的两

种模式,并做了一个生动的比喻:

  • 编排(Orchestration):需要主动控制所有的元素和交互,就像指挥家指挥乐团的乐手一样——对应

Command;

  • 协调(Choreography):需要建立一个模式,微服务会跟随音乐起舞,不需要监督和指令——对应

Event;

也曾看到很多持类似观点的文章,其中有一张图片印象深刻,我摘录过来:

左边是期望通过编排(Orchestration)方式得到的整齐划一的理想目标,右边是实际得到的大型翻车现场。

4. 全程 Event 带来的问题:开发困难和业务边界不清晰

在 Command 和 Event 的使用上,除了全程使用 Command 之外,还有一个极端是全程使用 Event,这一点在

Lambda(FaaS)中更常见一些:

这个方式首当其冲的问题就是在适用 Command 语义的地方采用了 Event 来替代,而由于 Command 和 Event

在使用语义上的差异,这个替代会显得别扭:

  • Command 是一对一的,替代他的 Event 也不得不从 “1:N” 退化为 “1:1”,pub-sub 模型不再存在;
  • Command 是需要返回结果的,尤其是 Query 类的 Command 必须要有查询结果,使用 Event 替代之后,

就不得不实现 “支持 Response 的 Event”,典型如在消息机制中实现 Request-Reply 模型的;

  • 或者引入另外一个 Event 来反向通知结果,即用两个异步 Event 来替代一个同步的 Command —— 这需要

让发起者进行额外的订阅和处理,开发复杂性远远超过使用简单的 Command;

  • 而且还引入了一个非常麻烦的状态问题:即服务间通讯的上下文中通常是有状态的,Reply Event 必须准确的

发送给 Request Event

  • 的发起者的实例,而不能任意选择一个。这使得 Reply Event 不仅仅要 1:1 的绑定订阅者服务,还必须绑定

这个服务的特定实例 —— 这样的 Reply Event 已经没法称为 Event 了;

  • 绕开这个状态问题的常见方案是选择无状态的场景,如果处理 Reply Event 时无需考虑状态,那么 Event

Reply 才能简单的发送给任意的实例;

对于粒度较大的微服务系统,通常很难实现无状态,所以在微服务中全程采用 Event 通常会比较别扭的,事实上

也很少有人这样做。而在粒度非常小的 Function/FaaS 系统中,全程采用 Event 方式比较常见。

关于全程使用 Event,我个人持保留态度,我倾向于即使是在 FaaS 中,也适当保留 Command 的用法:如果某

个操作是“业务逻辑”执行中不可或缺的一部分,那么 Command 方式的紧耦合反而更能体现出这个“业务逻

辑”的存在:

如果完全采用 Event 方式,“彻底”解耦,则产生新的问题(且不论在编码方面额外带来的复杂度) —— 在海量

细粒度的 Event 调用下,业务逻辑已经很难体现,领域模型(Domain Modeling)和有界上下文(Bounded

Context)则淹没在这些 Event 调用下,难于识别:

备注:

这个问题被称为“Lambda Pinball”,这里不深入展开,后续计划会有一篇文章单独详细探讨“Lambda

Pinball”的由来和解决的思路。

五、Command 和 Event 的选择:实事求是不偏不倚

总结一下 Command 和 Event 的选择,我个人的建议是不要一刀切:全程 Command 方式的缺点容易理解,但

简单替换为全程 Event 也未必合适。

我的个人观点是倾向于从实际“业务逻辑”处理的语义出发,判断:

  • 如果是业务逻辑的 “实现” 部分:倾向于选择使用 Command;
  • 如果是业务逻辑完成之后的后续 “通知” 部分:强烈建议选择使用 Event;

六、总结与反思

警惕:不要沦为分布式单体

上面我们列出了微服务和 Serverless 实践中容易形成 “分布式单体” 的两个主要原因和对策:

  • 通过共享库和网络客户端访问分布式能力:引入非侵入方案解耦应用和各种分布式能力;
  • 简单用远程调用替代进程内方法调用:区分 Command 和 Event,引入 Event 来解除微服务间不必要的强耦

合;

前者在技术上目前还不太成熟,典型如 Istio/Dapr 项目都还有待加强,暂时在落地上阻力比较大。但后者已经是

业界多年的成熟实践,甚至在微服务和 Serverless 兴起之前就广泛使用,因此建议可以立即着手改进。

反思:喧闹和谩骂之外的冷静思考

如果我们在微服务和 Serverless 实践中,始终停留在“用远程调用简单替代进程内方法调用”的程度,并固守单

体时代的习惯引入各种 SDK,那么分布式单体问题就必然不可避免。我们的微服务转型、Serverless 实践最后得

到的往往是:

把单体变成…… 更糟糕的分布式单体

当然,微服务可能成为分布式单体,但这并不意味着微服务架构是个谎言,也不意味着比单体架构更差。

Serverless 可能同样遭遇分布式单体(还有后续要深入探讨的 Lambda Pinball),但这也不意味着 Serverless

不可取 —— 微服务和 Serverless 都是解决特定问题的工具,和所有的工具一样,在使用工具之前,我们需要先

研究和了解它们,学习如何正确的使用它们:

  • 需要为微服务创建正确的架构,和单体架构必然会有很大的不同:一定不是“原封不动”的将方法调替换为远

程调用,最好不要用共享类库和网络客户端的方式直接使用各种分布式能力;

  • Serverless 更是需要我们对架构进行彻底的反思,需要改变思维方式,才能保证收益大于弊端;

七、参考资料和推荐阅读

  • Avoid the Distributed Monolith!!:来自 verizon 的 Mohamad Byan 在2018年9月的一个演讲,描述微服务实践中的分布式单体陷阱和解决的方式。
  • “Mecha:将Mesh进行到底” :我前段时间的文章,详细介绍 Multiple Runtime / Macha 架构,将更多的分布式能力进行Mesh化。
  • The Eight Fallacies of Distributed Computing: 分布式计算领域的经典文章,中文翻译请见 分布式计算的八大谬论
  • Opportunities and Pitfalls of Event-driven Utopia: Bernd Rücker 在QCon上的一个演讲,讲述“事件驱动乌托邦的机遇与陷阱”,本文的部分图片来自这份PPT。
  • Practical DDD: Bounded Contexts + Events => Microservices: Indu Alagarsamy的一个演讲,介绍领域驱动开发(DDD)和 Messaging 的交集。推荐使用消息技术在干净、定义良好的有界上下文之间进行通信,以去除时空耦合。
  • Building Event-Driven Cloud Applications and Services: 讨论构建事件驱动的应用和服务的通用实践和技术,是一个序列教程。中文翻译看 构建事件驱动的云应用和服务
  • The Architect’s Guide to Event-Driven Microservices: 来自Solace公司网站上的一份PDF格式的小册子,副标题为 “The Architect’s Guide to Building a Responsive, Elastic and Resilient Microservices Architecture / 架构师指南,用于建立响应式的,灵活而弹性的微服务架构。” 中文翻译见 事件驱动微服务架构师指南
  • 致传统企业朋友:不够痛就别微服务,有坑:网易云刘超刘老师的超级好文章,极其实在而全面的讲述微服务落地需要考虑的方方面面以及各种问题,强烈推荐阅读。

八、参考文献

  • 作者:蚂蚁金服分布式架构
  • 链接:https://juejin.im/post/6844904202565599240
  • 来源:掘金

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/920977.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

WEB攻防-通用漏洞文件上传js验证mimeuser.ini语言特性

知识点&#xff1a; 1、文件上传-前端验证 2、文件上传-黑白名单 3、文件上传-user.ini妙用 4、文件上传-php语言特性 详细点&#xff1a; 1、检测层面&#xff1a;前端&#xff0c;后端等 2、检测内容&#xff1a;文件头&#xff0c;完整型&#xff0c;二次渲染等 3、检…

鸿蒙学习高效开发与测试-集成开发环境(4)

文章目录 1、工程管理2、代码编辑3、界面预览4、编译构建5、代码调试6、性能调优7、设备模拟8、命令行工具9、端云一体化开发 HUAWEI DevEco Studio 是面向鸿蒙生态的集成开发环境&#xff0c;提供了一站式的鸿蒙生态应用、元服务开发能力&#xff0c;详细能力如图所示。 1、工…

基于yolov8、yolov5的茶叶等级检测识别系统(含UI界面、训练好的模型、Python代码、数据集)

摘要&#xff1a;茶叶等级检测在茶叶生产、质量控制和市场销售中起着至关重要的作用&#xff0c;不仅能帮助相关部门实时监测茶叶质量&#xff0c;还为智能化检测系统提供了可靠的数据支撑。本文介绍了一款基于YOLOv8、YOLOv5等深度学习框架的茶叶等级检测模型&#xff0c;该模…

Spire.PDF for .NET【页面设置】演示:打开 PDF 时自动显示书签或缩略图

用户打开 PDF 文档时&#xff0c;他们会看到 PDF 的初始视图。默认情况下&#xff0c;打开 PDF 时不会显示书签面板或缩略图面板。在本文中&#xff0c;我们将演示如何设置文档属性&#xff0c;以便每次启动文件时都会打开书签面板或缩略图面板。 Spire.PDF for .NET 是一款独…

从 HTML 到 CSS:开启网页样式之旅(开篇之一)——CSS 初体验与网页样式新征程

从 HTML 到 CSS&#xff1a;开启网页样式之旅&#xff08;一&#xff09;——CSS 初体验与网页样式新征程 前言一、为什么需要 CSS&#xff1f;二、CSS的引用&#xff08;一&#xff09;行内样式&#xff08;二&#xff09;内部样式&#xff08;三&#xff09;外部样式&#xf…

Android音频采集

在 Android 开发领域&#xff0c;音频采集是一项非常重要且有趣的功能。它为各种应用程序&#xff0c;如语音聊天、音频录制、多媒体内容创作等提供了基础支持。今天我们就来深入探讨一下 Android 音频采集的两大类型&#xff1a;Mic 音频采集和系统音频采集。 1. Mic音频采集…

数据结构C语言描述4(图文结合)--栈的实现,中序转后序表达式的实现

前言 这个专栏将会用纯C实现常用的数据结构和简单的算法&#xff1b;有C基础即可跟着学习&#xff0c;代码均可运行&#xff1b;准备考研的也可跟着写&#xff0c;个人感觉&#xff0c;如果时间充裕&#xff0c;手写一遍比看书、刷题管用很多&#xff0c;这也是本人采用纯C语言…

数据结构之一:复杂度

相关代码&#xff1a;SData/test_22/main.c Hera_Yc/bit_C_学习 - 码云 - 开源中国 数据结构&#xff1a;在内存当中存储、组织数据的方式。&#xff08;顺序表、链表、栈、队列、树等&#xff09;。 算法&#xff1a;与数据结构配合使用&#xff0c;是对数据的处理。&#…

【鸿蒙技术分享:探索 HarmonyOS 开发之旅】

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Houdini和Blender如何使用CPU云渲染

近期&#xff0c;渲染101云渲染农场在产品和服务方面进行了重要更新&#xff0c;进一步提升了我们平台的渲染能力和兼容性&#xff0c;助力各位用户高效完成创作。云渲码6666 渲染101云渲码6666 1. Houdini和Blender支持CPU云渲染 我们不断拓展云渲染的工具和平台支持&#x…

02 —— Webpack 修改入口和出口

概念 | webpack 中文文档 | webpack中文文档 | webpack中文网 修改入口 webpack.config.js &#xff08;放在项目根目录下&#xff09; module.exports {//entry设置入口起点的文件路径entry: ./path/to/my/entry/file.js, }; 修改出口 webpack.config.js const path r…

网络编程 作业1

1.c #include <myhead.h> #define IP "192.168.60.45" #define PORT 6666 #define BACKLOG 100 void fun(int sss) {if(sssSIGCHLD){while(waitpid(-1,NULL,0)>0);}} int main(int argc, const char *argv[]) {//1.捕获子进程退出时的信号if(signal(SIGCHL…

【2024亚太杯亚太赛APMCM C题】数学建模竞赛|宠物行业及相关产业的发展分析与策略|建模过程+完整代码论文全解全析

第一个问题是&#xff1a;请基于附件 1 中的数据以及你的团队收集的额外数据&#xff0c;分析过去五年中国宠物行业按宠物类型的发展情况。并分析中国宠物行业发展的因素&#xff0c;预测未来三年中国宠物行业的发展。 第一个问题&#xff1a;分析中国宠物行业按宠物类型的发展…

外排序中的归并排序

外排序中的归并排序 7.11 外排序中的归并排序相关基础知识原理最终参考程序 7.11 外排序中的归并排序 外部排序&#xff1a;数据元素太多不能同时放在内存中&#xff0c;根据排序过程的要求不能在内外存之间移动数据的排序。&#xff08;下文也称外排序&#xff09; 例如 1 G…

wsl2中kali linux下的docker使用教程(教程总结)

一、前言 上一篇关于kali linux的文章是图形界面的配置&#xff0c;这里作者准备补充两点&#xff0c;一点是在使用VNC时&#xff0c;如果F8不能用的话&#xff0c;可以试试AltF8&#xff1b;然后就是VNC在初始化设置时的三个设置选项依次是密码、再输一次以及设置仅观看密码。…

Linux系统使用valgrind分析C++程序内存资源使用情况

内存占用是我们开发的时候需要重点关注的一个问题&#xff0c;我们可以人工根据代码推理出一个消耗内存较大的函数&#xff0c;也可以推理出大概会消耗多少内存&#xff0c;但是这种方法不仅麻烦&#xff0c;而且得到的只是推理的数据&#xff0c;而不是实际的数据。 我们可以…

【通俗理解】ELBO(证据下界)——机器学习中的“情感纽带”

【通俗理解】ELBO&#xff08;证据下界&#xff09;——机器学习中的“情感纽带” 关键词提炼 #ELBO #证据下界 #变分推断 #机器学习 #潜变量模型 #KL散度 #期望 #对数似然 第一节&#xff1a;ELBO的类比与核心概念【尽可能通俗】 ELBO&#xff0c;即证据下界&#xff0c;在…

【pyspark学习从入门到精通14】MLlib_1

目录 包的概览 加载和转换数据 在前文中&#xff0c;我们学习了如何为建模准备数据。在本文中&#xff0c;我们将实际使用这些知识&#xff0c;使用 PySpark 的 MLlib 包构建一个分类模型。 MLlib 代表机器学习库。尽管 MLlib 现在处于维护模式&#xff0c;即它不再积极开发…

业务架构、数据架构、应用架构和技术架构

TOGAF(The Open Group Architecture Framework)是一个广泛应用的企业架构框架&#xff0c;旨在帮助组织高效地进行架构设计和管理。 TOGAF 的核心就是由我们熟知的四大架构领域组成:业务架构、数据架构、应用架构和技术架构。 企业数字化架构设计中的最常见要素是4A 架构。 4…

阿里巴巴官方「SpringCloudAlibaba全彩学习手册」限时开源!

最近我在知乎上看过的一个热门回答&#xff1a; 初级 Java 开发面临的最大瓶颈在于&#xff0c;脱离不出自身业务带来的局限。日常工作中大部分时间在增删改查、写写接口、改改 bug&#xff0c;久而久之就会发现&#xff0c;自己的技术水平跟刚工作时相比没什么进步。 所以我们…