《 Reactive Microservices Architecture 》 响应式微服务架构 摘记

第三部分未完结,挖坑先。。。

第一章 介绍

我们只在我们没有其他选择时改变单体 (monolithic) 系统。与其说是敏捷地抓住机会,我们考虑它是否确实值得倾覆我们称为纸牌屋的企业系统的微妙平衡。通常,机会很快就消失了,被快速的公司抓住。

Flaus Schwab: 在新世界,不是大鱼吃小鱼,是快鱼吃慢鱼。

基于微服务的架构是个简单的概念:它提倡用一组小的、隔离的服务来创建系统。这些服务拥有它自己的数据,各自是独立的、可扩展且对失败有弹性。服务集成其他服务来构建一个内聚的系统,它比我们当前构建的典型企业系统更灵活。

传统企业系统设计为单体应用,难以扩展,难以理解且难以维护。单体应用可能很快就变成噩梦,扼杀创新、进度和乐趣。单体应用导致的负面作用对一个公司可能是灾难性的,从底层的士气到高层雇员的周转,从阻止公司雇佣顶尖工程天才到失去市场机会,在极端情况下,甚至是公司的失败。

战争故事通常听起来是这样的:“我们终于做了决定来改变我们的 Java EE 应用,在得到管理层批准后。然后我们经过长达数月的大设计,在我们最终开始构建点东西之前。但我们构建期间的大多数时间都花在尝试弄清楚单体应用真正做了什么。我们因害怕而瘫痪,担心一个小的错误可能导致非预期的、未知的副作用。最终,在数月的担忧、害怕和艰难工作后,改变实现了,但地狱也打开了。”

这样的经历会加强害怕,使我们瘫痪更严重。这就是系统、公司如何停滞的。如果有更好的方式呢?

Steve Jobs:You’ve got to start with the customer experience and work back towards the technology 。

微服务的客户是投资于系统的组织,所以让我们从客户开始:开发人员、架构师和关键利益相关者。

需要拯救的服务

Helen Keller:Although the world is full of suffering, it is also full of the overcomming of it .

因为技术原因,微服务是软件里不纯粹的下一代设计演进。微服务这个词汇呈现的思想在我们进入面向服务的架构(SOA, Service Oriented Architecture)之前已经遍布各处。阻止我们采用微服务内嵌的概念的特定技术约束已经进入下一水平:单台机器运行着单个内核处理器,慢速的网络,昂贵的磁盘,昂贵的 RAM,组织结构是个单体。像把系统组织成一个定义良好的、有单一职责的系统的想法不是新的。

快进到 2016 年,阻止我们采用微服务的技术限制已经消失了。网络是快速的,磁盘是便宜的(且更快速),RAM 是便宜的,多核处理器是便宜的,云架构正在彻底改革我们如何设计、部署系统。现在,我们终于可以考虑着客户来构建我们的系统。

设计和编写软件是有趣的,这是大多数的我们进入软件行业的初衷。微服务不仅仅是一序列的原则和技术。它们以一种更专业的方式来达成复杂问题的系统设计。

微服务允许我们以组织我们团队的方式来构建系统,在团队成员之间划分职责,保证他们自由处理自己的工作。当我们理顺了我们的系统,我们把权利从中央治理主体移交到小团队里,小团队可以快速抓住机会并保持敏捷,因为他们明白 他们控制的、有定义良好的边界的软件。

切割单体应用(Slicing the Monolith)

处理一个单体应用意味着仔细观察你的传统 Java EE 系统。以单片集成方式编写,这些系统倾向于在服务内和服务之间的组件之间强耦合。系统的服务是紊乱、非独立的将(导致)难以编写、理解、测试、演进、升级和独立操作。更坏的是,强耦合会导致级联失败,也就是一个服务失败将拖累整个系统,而不是允许你隔离地处理失败。

一个问题是应用服务器处理这种单片集成模型。他们假设你打包你的服务 JAR 到单个 EAR 文件里来分组你的服务,然后进行部署它(伴随着你所有其他的应用和服务)到单个应用服务器的运行实例里,应用服务器通过类加载器技巧来管理服务“隔离”。总而言之,一种非常脆弱的模型。

传统JEE架构
图 1- 2. 典型的 Java EE 架构

今天,我们有更好的基础来隔离服务,使用虚拟化、Linxu 容器,Docker 和 Unikernels 。 这使得可以把隔离作为头等关注–这对于弹性、伸缩性、持续交付和高效操作是必须的。它也为提升基于微服务的架构的兴趣做好铺垫,允许你切割单体应用,独立地开发、部署、运行、扩展和管理服务。

SOA 穿着新衣服?

一个有效的问题是微服务是否穿着新衣服的 SOA?答案既是是也是否。 “是” 因为初始目标–解耦、隔离、组合、集成、离散并自治的服务–是相同的。“否”是因为 SOA 的基本理念是经常被误解和误用的,导致复杂的系统:ESB 被用于连接(hook up)多个单体应用,在复杂、不高效且不灵活的协议上通信。

Anne Thomas 在她的文章“SOA is Dead; Long Live Serivces”里很好地抓住了这点:

虽然 “SOA”这个词已经死,对面向服务的架构的需求比以前更强了。但很困难那是改变:首字母缩写的阻碍。人们忘掉了 SOA 短语。它们被包装进两个愚蠢的技术辩论(例如, “ESB的最佳实践是什么?” 或者 “WS-* VS. REST”),他们错过了最重要的材料:架构和服务。
成功的 SOA 要求破坏现状。SOA 不仅是采用新技术和给已存在应用构建服务接口的问题,它要求重新设计应用的选集(portfolio)。而且它要求大量的 IT 操作方式的变迁。

当今的软件架构比 SOA 刚出现时的 10-15 年前有显著的不同。今天,多核处理器、云计算、移动设备和 IoT 迅速出现,这意味着所有的系统从一开始就是分布式的,这是个极大不同、更有挑战的世界。

一如既往,新的挑战要求用新的方式去思考,我们已经看到新系统被设计用来解决这些新挑战,这些系统构建在响应式宣言里定义的 响应式原则上。

响应式原则没有新的东西,它们已经被证明、淬炼了达 40 多年,追溯到 Carl Hewitt 有创造力的工作和他的 Actor 模型的发明,, Jim Gray and Pat Helland at Tandem Systems, and Joe Armstrong and Robert Virding and their work on Erlang。这些人超前于他们的时代,但现在世界已经跟上他们的革新性的思考,我们比以前更依赖于他们的工作。

让微服务变得有趣的是这个架构已经从 SOA 的成功和失败中学习的,保持好的理念,用响应式原则和现代基础设施从头重新构建它们。总之,微服务是近年来响应式原则的一个最有趣的应用。

第二章 什么是响应式微服务

使用基于微服务的架构的一个核心原则是 分而治之(Divide and Conquer):分解系统成 离散的、隔离的子系统,通过定义良好的协议通信。

隔离是 恢复能力(resilience)和弹性(elasticity)的先决条件,且要求在服务边界 异步通信 来达到在时间、空间解耦:

  • 时间: 允许并发;
  • 空间: 允许分布式和移动性–移动服务的能力。

当采用微服务时,另一个必不可少的是消除共享的、可变的状态,从而最小化协调、竞争和一致性的开销,如 拥抱 Share-Nonthing Architecture 里定义的 Universal Scalability Law 。

在我们旅程的此刻,是时候讨论定义一个响应式微服务的最重要部分了。

隔离所有事情,Isolate All the Things

Pablo Picasso: Without great solitude, no serious work is possible .

隔离是最重要的特性。它是微服务很多高层次收益的基础。但它也是对你的设计和架构有最大影响的特性。它将、且应该把整个架构切成片,因此它需要从第一天就开始考虑。它甚至将影响 你打散、组织团队和他们职责 的方式,正如 Melvyn Conway 发现的,后来在 1967 年变成 康卫定律(Conway’s Law)的:

Any organization that designs a system(defined broadly) will produce a design whose structure is a copy of the orgazation’s communication structure .

失败隔离:以容纳和管理失败而不是让它级联穿透参与工作流的服务,这种模式有时称为 隔板(Bulkheading)。

隔板已经用于船体结构达100多年,作为一种“创建防水舱以便在船体破裂或其他渗透时容纳水”的方法。船被分为不同的、完全隔离的防水舱,这样,即使防水舱充满了水,渗透也不会扩散,船可以继续运作并到达它的目的地。

%e4%bd%bf%e7%94%a8%e9%98%b2%e6%b0%b4%e8%88%b1%e7%9a%84%e8%88%b9%e7%bb%93%e6%9e%84
图 2-1:使用防水舱的船结构

失败恢复(Resilience):从失败中治愈的能力,取决于分区化和失败容忍性,只能通过打碎同步通信的强耦合来达到。微服务在进程边界上使用异步消息传递来通信,使间接层成为可能并解耦了捕获、管理失败的必要性,与常规流程是正交的,通过使用服务监督(service supervision)

服务之间的隔离使它自然地采纳持续交付。这允许你安全地部署应用并铺展开,增量地恢复修改,一个服务接一个服务。

隔离也让它更容易地缩放服务,也允许他们监控、调试和独立地测试,这些对于单体应用是非常困难的。

自治地行动,Act Autonomously

隔离是自治的先决条件。只有当服务是隔离的,它们才可能完全自治,独立地做出决定,独立地行动,与其他服务协作来解决问题。

一个自治的服务只能通过发布它的的协议或 API 来承诺它自己的行为。拥抱这个简单但基本的事实已经对我们如何理解和用自治服务建模协作式系统产生深远的影响。

自治是另一个方面是,如果一个服务只能做出关于它自身行为的承诺,那么解决一个冲突或修复底层失败场景需要的所有信息都在服务自身内可得,移除了通信和协调的需要。

与自治服务工作打开了如下的灵活性:服务编制、工作流管理、协调行为,还有扩展性、可用性和运行时管理,代价是把更多的东西打包进定义良好的、可组合的 API,它们可以进行通信、得到共识。

做一件事,做好它。Do One Thing, and Do It Well

Unix 哲学和设计已获得高度的成功,在它被提出后的数十年仍然成立。它的一个核心原则是开发者应写的程序应当有单一目的、定义良好的职责,与其他的小程序能很好组合。

这个理念后来被 Robert C.Martin 引入面向对象编程社区,命名为 单一职责原则(Single Responsibility Priciple, SRP),它声明一个类或组件应当“只有一个理由去改变”。

围绕微服务的真正大小已经有很多的讨论。怎样的可以被认为是“微”?多少行代码仍然可以被认为是微服务?这些都是错误的问题。反而,“微”应该是指职责的范围,这里的指导原则是 Unix 哲学,SRP :做一件事,并做好它。

如果一个服务只有一个理由来存在,提供了一个单一可组合的功能点,那么业务领域和职责就不是紊乱的。每个服务可以做得更有用,且系统作为一个整体更容易伸缩、做恢复、理解、扩展和维护。

拥有你自己的状态,独占地

Jonathan Franzen:Without privacy there was no point in being an individual .

到目前为止,我们把微服务表征为一组隔离的服务,每个服务有单一的职责领域。这构成了把每个服务看作孤立地存活和死亡的单一单元的基础,这是恢复力的前提条件,也可以孤立地移动,这是弹性的前提条件。

虽然所有这些听起来很好,我们忘记了房间里的大象:状态。

微服务通常是有状态的实体:他们封装状态和行为,类似于一个 Object 或一个 Actor 的形式,隔离通常应用到状态上,要求你把状态和行为当作单一单元看待。

不幸的是,大多数的 web 框架通过所谓的“无状态”架构来忽略问题,通过“无状态”控制器形式的服务,他们把状态下推到一个大的、共享数据库里,这不如你预期的那么有帮助,只是把问题委托给第三方,使问题更难以控制,在数据完整性保证和伸缩性、可用性保证两方面都是(见图 2-3)。

%e5%8d%95%e4%bd%93%e5%ba%94%e7%94%a8%e4%bc%aa%e8%a3%85%e6%88%90%e5%be%ae%e6%9c%8d%e5%8a%a1
单体应用伪装成微服务,仍然是个微服务

%e5%be%ae%e6%9c%8d%e5%8a%a1%e6%9c%89%e8%87%aa%e5%b7%b1%e7%9a%84%e7%8a%b6%e6%80%81
微服务拥有它自己的状态

真正需要的是,每个微服务对他们的状态有单一职责且因此而存在。把每个服务建模为有界的上下文是有帮助,因为每个服务通常定义了它自己的领域,每个有它自己的语言。这些技术来自于领域驱动设计(Domain-Driven Design, DDD)的建模工具。这里引入的所有新概念,把 DDD 看作是一个学习的好的开始。微服务受 DDD 重度影响,这里你听到的很多微服务上下文的术语来自 DDD。

当与其他微服务通信时,跨越有界的上下文,你只能友好地请求它的状态,你不能强制它显示状态。每个服务按它自己的意愿响应请求,用派生自它当前状态的不可变数据(事实),绝不会直接曝露它的可变状态。

这个每个服务自由地、以它希望的方式展示它的状态,以最合适的格式和媒介存储它。有些服务可能选择传统的关系数据库,有的可能选择 NoSQL 数据库,有的可能选择时序数据库,有的通过事件溯源(Event Sourcing)和命令查询职责分离(Command Query Responsibility Segregation, CQRS)的技术使用事件日志。

从去中心化的数据管理和持久化(有时称为 多语言持久化)可以获得好处。概念上,使用哪种存储媒介不是真正重要的,真正重要的是服务可以被看作是单一单元–包括了自己状态和行为的,为了做到这个,每个服务需要拥有它自己的状态、独占地。这包括不允许某个服务直接访问其他服务的持久化存储,只能通过它的 API,这可能难以通过编程来强制,因此需要用惯例、策略和代码审查来完成。

事件日志是消息的一种持久存储。我们可以选择在消息从外部进入服务时进行存储,

// TODO

拥抱异步消息传递

Alan Kay: Smalltalk is not only NOT its syntax or the class library, it is not even about classes. I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is “message” .

微服务之间的通信需要基于异步消息传递(而每个微服务内部的逻辑是以同步发方式执行的)。为了解耦,微服务之间的异步边界是必须的,它们的通信流,在实践上允许并发,在空间上允许分布式和移动性。没有解耦,不可能达到去中心化的水平和包含隔离、恢复的需要。

异步和非阻塞执行和 IO 通常是更合算的,通过更高效地使用资源。它帮助减小系统里共享资源的竞争(或拥塞),这个通常是可升缩性、低延迟和高吞吐的最大障碍。

为什么阻塞如此差?

举个例子:如果一个服务通过阻塞方式调用另一个服务,它持有着底层的线程。这意味着在这段期间这个线程不能做有用的工作。线程是稀有的资源,需要尽可能高效地利用。如果服务执行的是一个异步、非阻塞形式的调用,它将释放底层的线程以便其他人可以在等待结果期间使用。这导致更高效的使用,从底层资源的开销、能量和性能的角度。

值得指出的是,拥抱异步化在一个服务内的不同资源之间的通信与不同服务之间的通信是一样重要的。为了获得最完整的好处,一个请求链里参与的各方都需要是非阻塞执行的,从请求分发、服务实现、下到数据库及返回。

异步消息传递有助于做出约束,特别是网络编程里失败的场景,而不是把它们隐藏在有漏洞的抽象背后并假装它们不存在,这在远程过程调用(Remote Procedure Calls, RPC)中可以看到。

异步消息传递的另一个好处是,它倾向于聚焦在工作流和应用的通信模式上,帮助你从协同的角度去思考:数据如何在不同的服务之间流动、他们的协议和交互模式。

对异步消息传递的需求局限于响应单个消息或请求,还包括连续流形式的消息,潜在地无边界的流。

基础的变迁是我们从 “数据是静止的,data at rest” 到 “数据是运动的,data in motion”。数据过去是离线的,现在是在线的。应用需要近乎实时地响应数据的变化,当变化发生时执行连续的查询或聚合已有数据,然后实时地反馈回应用程序来影响它的操作方式。

大数据的第一波浪潮是数据是静止的(data at rest),我们存储海量的数据在 HDFS 或类似设施里,然后用离线进程在晚上处理数据,通常有几个小时的延迟。

在第二波浪潮里,我们看到实时响应 “数据是运动的, data in motion” 的需要:捕获实时数据,处理它,在几秒内、甚至小于一秒内把结果反馈到运行中的系统,这个已经越来越重要。
这催生了混合式架构,例如 Lambda 架构,它有两个层次:“speed layer” 用于实时在线处理,“batch layer”用于更深入的离线处理,在 “speed layer” 实时处理的结果稍后会合并到 “batch layer”。这个模型解决了一些需要立即响应(至少是一部分)数据的需要。但它添加了不必要的复杂性:维护两个独立的模型和数据处理管线,还有最后的数据合并。

第三波浪潮是完全拥抱“数据是运动的,data in motion”,对于大多数的场景和数据大小,从传统的面向批处理的架构完全迁移到纯粹的流失处理架构。

这个模型对于基于微服务的架构是最感兴趣的,因为它给我能力把流的能力和“data in motion” 带到服务本身,从通信协议和持久化方案(通过事件日志)都是,包括 client-to-service 和 service-to-service 通信。

保持移动性,但可定位, Stay Mobile, but Addressable

第三章 系统里的微服务

Carl Hewitt: One actior is no actor. Actors come in systems.

单个微服务是相对容易设计和实现的,难的是围绕着基于微服务架构的所有事情:发现、协调、安全、复制、数据一致性、失败、部署和与其他系统集成,仅仅列举了一些。

:discovery, coordination, security, replication, data consistency, failover, deployment

系统需要利用现实,Systems need to exploit reality

Klang’s Conjecture by Viktor Klang: if you cannot solve a problem without programming. You cannot solve a problem with programming .

基于微服务架构的一个主要好处是它给你一组工具来利用现实,让创建的系统尽可能模拟世界是如何工作的,包括它的约束和机会。

另一个固有、需要拥抱的更重要的事实 是现实是不一致的,没有单一绝对的形态,所有的事情都是相对的,包括时间和我们现有的经验。

信息的通信是有延迟的。你思考的信息总是来自过去,这对我们观察到的一切都成立。当我们观察或学习 一个效果时,它是已经发生的,我们总是看到过去。“现在” 是眼睛的主观感受。

微服务是现实的一条脱险通道。在微服务内部,我们居住在一个 确定且强一致性的安全岛上,在这个岛上,我们愉快地生活在 时间和现状是绝对的 幻觉下。

%e9%87%87%e7%94%a8%e5%be%ae%e6%9c%8d%e5%8a%a1%e6%84%8f%e5%91%b3%e7%9d%80%e6%8b%a5%e6%8a%b1%e6%9c%80%e7%bb%88%e4%b8%80%e8%87%b4%e6%80%a7
图3-1 采用微服务意味着拥抱最终一致性

然而,一旦我们离开微服务的边界,我们进入一个广阔的不确定的空间,分布式系统的世界是个完全不一样的世界。

Pat Helland 讨论这个为 “内部数据(data on the inside)” versus “外部数据(data on the outside)”:内部数据是”我们当前、本地的状态”,外部数据,事件 是来自过去的冲击,服务之间的命令是 未来的希望。

服务发现, Service Discovery

欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

《 Reactive Microservices Architecture 》 响应式微服务架构 摘记》上有3个想法

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据