O’Reilly的电子书《Reactive Microservices Architecture》讲述了微服务/分布式系统的一些设计原则,本文是笔者阅读完此书后的理解。程序员
微服务相比传统的单体应用可以带来快速的响应,以小的系统产生大的影响。而随着网络加速、磁盘成本下降、RAM成本下降、多核技术的发展、云架构技术的爆发,微服务再也不受这些客观条件的限制,已经开始大规模的应用。算法
与SOA架构,微服务和它都具备相同的初衷:解耦、隔离、组合、集成、分散以及自主,可是SOA常常被误解和误用,尤为是使用ESB来支持对多个单体系统的协议(复杂、低效、不稳定)调用,使得系统变得很是复杂。而随着这些年硬件以及软件架构理念的发展,全部的系统基本都已经变成分布式架构,也带来了不少新的挑战。也就须要新的思路和理念来面对这些问题,其中本书所讲述的响应式原则(Reactive principles)即一种解决分布式系统的思路。响应式原则也并不是一个新的东西,Erlang中的Actor模型即一种响应式设计。微服务是响应式原则的一个架构设计,其借鉴了SOA架构中好的理念,并使用了现代的基础服务设施(云服务、自动化工具等)。数据库
使用微服务架构最关键的一个原则就是将系统划分红一个个相互隔离、无依赖的子系统,这些子系统经过定义良好的协议进行通讯。其中隔离是实现弹性、可伸缩系统的前提,而且须要在服务间创建异步通讯边界,所以要在如下两方面进行解耦:编程
此外,微服务还须要消除共享状态从而最小化相互协做、联结的成本,要尽可能达到“不共享任何东西”。设计模式
隔离是微服务架构中最重要的特性。不只仅是微服务带来的不少优点的基础,也是对设计和架构影响最大的方面。如康威定律所说,它还对组织架构有很是大的影响,浏览器
1
复制代码 |
系统的结构是对团队组织架构的反映。复制代码 |
失败隔离是一种与“舱壁”(船舱的隔板)相关的设计模式:隔离错误、失败以防止其蔓延至全部服务,致使更大面积的失败。缓存
“舱壁”这种模式已经在轮船上使用了几个世纪:建立一个个密封不漏水的空间以防止船的外壳破损或者其余泄漏。这些空间是彻底互相隔离的,这样即便一个隔离区充满了水,也不会蔓延流到其余隔离空间中,从而使得船总体仍然可以运做。安全
弹性(从失败中恢复的能力)即依赖于这种舱壁和失败隔离的设计,而且须要打破同步通讯机制。由此,微服务通常是在边界之间使用异步消息传输,从而使得正常的业务逻辑避免对捕获错误、错误处理的依赖。性能优化
进一步的,服务之间的隔离使得“持续交付”变得很容易,可以随时地部署服务而无需担忧影响正常的业务。并且隔离的单个服务很容易监控、调试、测试和部署,很是便于扩展。bash
上面所讲的隔离是自主性的前提。只有当服务之间是彻底隔离的,那么才可能实现彻底的自主,包括独立的决策、独立的行动以及与其余服务协调合做来解决问题。
一个自主的服务仅仅保证其对外公布的协议/API的正确性便可。如此,不只可以让咱们更好地了解协做的这些系统以及对他们的建模,也可以在面对冲突、失败情况时,只在一个服务内进行排查、修复便可。
使用自主服务可以给服务编排、工做流管理以及服务合做上带来很大灵活性,同时也带来可扩展性、可用性、运行时管理等优点。但其付出的代价就是须要花心思在定义良好的可组合的API设计上,这个是有必定挑战性的。
如Unix编程哲学所说:程序应该只作一件事,而且作好它。而后让他们一块儿工做完成任务。这也相似于面向对象编程语言中软件开发单一职责原则(SRP)的描述。
而在微服务中一个很大的问题就是如何正确地肯定服务的大小。好比怎样的粒度才能被认为是“微”(micro)?多少行代码还能被认为是微服务。但这里“micro”实际上是和职责范围有关的,就如Unix的SRP原则:只作一件事而且作好。
每个服务都应该只有一个存在的缘由,提供了一组相关的功能,业务和职责不会糅杂在一块儿。全部服务组织在一块儿总体上可以便于扩展、具备弹性、易理解和维护。
微服务中有一个很关键的部分就是状态(state),不少微服务也都是有状态的实体,包括对状态和行为的封装。而在“无状态”的设计理念下,不少服务都把本身的状态下沉到一个大的共享数据库中,这也是不少传统的Web框架的作法。如此就形成了在扩展性、可用性以及数据集成上很难作好把控。而本质上,一个有着共享数据库的微服务架构本质仍是一个单体应用。
合理的方式是一个服务既然具备单一职责,那么就应该拥有本身的状态和持久化机制,建模成一个边界上下文,有本身的域名和语言。这些也都是DDD(Domain-Drivern Design)里面的技术。微服务受DDD影响很大,其中不少微服务的上下文的概念都来自于DDD。
当访问一个服务时,也只能是客气的请求其状态而并不能强制其必定具备状态。如此,每一个服务都可以经过事件溯源(Event Sourcing)和CQRS(Command Query Responsibility Segreation)自定义本身的状态表示和实现(RDBMS、NoSQL、Time-Series、EventLog)。
去中心化的数据管理和持久化(多语言持久化)可以带来不少优点。数据的存储媒介能够根据服务本身的须要选择,服务包括其数据均可以看作一个单独的单元。同时并不容许一个服务直接去访问另外一个服务的数据库,若是要访问只能经过API(经过指定规范、策略和Code Review来保证)。
Event Log是一种消息的存储方式。咱们能够以消息进入服务的形式存储(发送到服务的Commnds),即命令溯源(Command Sourcing)。咱们也能够忽略命令,让命令先执行对服务产生一些做用,若是触发了状态变动,那么咱们捕获这次变更并用事件溯源(Event Sourcing)将这次Event存储到EventLog中。
消息有序存储,可以提供服务全部的交互历史。同时消息也保存了服务的事务,也就可以对这些事务日志进行查询、审计、重放从而用于弹性伸缩、调试以及冗余等。
命令溯源和事件溯源是不一样的语义。重放命令意味着会重放其带来的反作用。而重放事件则是执行状态的改变。须要根据具体场景的不一样选择使用哪一种溯源技术。
使用EventLog能够避免”对象关系不匹配”的问题(ORM中常常出现)。而因为其自身自然适合异步消息传输,所以绝大多数状况下,Event Log是微服务中最佳的持久化模型。
微服务之间的通讯的最佳机制就是消息传输。如上文所说,服务之间的异步边界可以在时间和空间两方面进行解耦,可以提高总体系统的性能。
异步非阻塞执行以及IO都是对资源的高效操做,可以最小化访问共享资源时的阻塞消耗(扩展性、低延迟以及高吞吐的最大障碍)。简单的例子:若是要发起对10个服务的访问,其中每个请求须要耗时100ms,那么若是使用同步模式,则完成全部请求则须要10*100=1000ms。而若是使用异步模式,同时发起10个线程,则一共就须要100ms。
异步消息传输还可以让咱们注重网络编程的限制,而不是伪装这些限制不存在,尤为是在失败场景下。还可以让咱们更关注工做流以及服务间的数据流、协议、交互是怎样进行的。
然而目前微服务的默认通讯协议以REST为主,其本质是同步通讯机制,比较适用于可控的服务调用或者紧耦合的服务调用上。
此外,使用异步消息传输的另外一个需求在于对消息的持续流处理(多是无界的)。也是咱们从“data at rest”到”data in motion”的理念的改变。以前的数据是离线被使用的,而如今的数据是被在线处理的。应用对数据变动的响应须要达到实时级别:当变更发生,须要实时进行持续的查询、聚合并反馈给最终的应用。这个理念的造成经历了三个主要阶段:
“data at rest”: 将大量数据存储在HDFS相似的数据存储媒介中,而后使用离线批处理技术去处理这些数据,通常会有数个小时的延迟。
意识到了“data in motion”正变得愈来愈重要:在数秒内捕获数据、处理数据并反馈给运行中的系统。Lambda即此时出现的一种架构: 加速层用来作实时在线计算;批处理层用来作复杂的离线处理。加速层实时处理的结果后续被批处理层的结果合并。这个模型解决了某些场景须要数据即时响应的问题,但其架构的复杂使得不容易维护。
“data in motion”: 全面拥抱移动数据的概念。传统的面向批处理的架构都在逐渐向纯流处理的架构转变。这种模型做为通讯协议和持久化方案(经过Event Logging)也可以给微服务带来“data in motion”和流处理的能力。
如上述所讲,异步消息传输带来了对时间和空间的解耦。其中,对于空间的解耦也被称为“位置透明”:在多核或者多结点上的微服务在运行时无须改变结点便可以动态扩展的能力。这也决定了系统的弹性和移动性。要实现这些须要依赖云计算带来的一些特性和其“按需使用”模型。
而可寻址则是说服务的地址须要是稳定的,从而能够无限地引用此服务,而不管服务目前是否能够被定位到。当服务在运行中、已经中止、被挂起、升级中、已经崩溃等等情形下,地址都应该是可用的,任意客户端可以随时发送消息给一个地址。实际中,这些消息有可能进入队列排队、重提交、代理、日志记录或者进入死信队列。此外,地址须要是虚拟的,能够表明一组实例提供的服务。
使用虚拟地址可以让服务消费方无须关心服务目前是如何配置操做的,只要知道地址便可。
一个微服务并不是真正的“微服务”,一系列微服务经过通讯、合做才可以解决问题,才能组成一个完整的业务系统。实现一个服务是相对简单的,困难的是其余基础设施的实现:服务发现、协做、安全、冗余、数据一致性、容错、部署以及与其余系统的集成。
微服务架构带来的一个很大优点就在于它提供了一套工具,可以利用现实,模仿真实的世界来建立系统,包括真实世界的限制和机会。
首先根据“康威定律”,微服务的部署是和现实中工程组织/部门如何工做是相适应的。此外,还须要注意的是现实不是一致的,任何事情都是相对的,即便是时间和“如今”这个概念。
信息的传播速度不可能比光快,甚至大部分是很慢的,这也意味着信息通讯是有延迟的。信息都是来自过去的,咱们稍微思考一下能够知道信息承载的都是咱们观察到的东西。而咱们观察/学习到的事实至少都是很短期以前发生的,也就是说咱们老是在看过去,“如今”只是旁观者的视角。
每个微服务均可以看作一个安全的小岛,提供了肯定性和强一致性,上面的时间和“目前”都是绝对的。可是当离开一个微服务的边界时,就进入了一片充满非肯定性的大海-分布式系统的世界。如不少人所说,构建分布式系统是困难的。但现实世界同时也提供了如何解决诸如弹性、可伸缩、隔离性等分布式问题的解决思路。所以,即便构建分布式系统是困难的,可是咱们也不该该退化为单体应用,而是学习如何使用一系列的设计原则、抽象概念和工具来管理它。
正如Pat Helland在《Data on the Outside versus Data on the Inside.”》对”data on the inside”和“data on the outside”的对比所说:内部的数据就是咱们本地的“目前”,而外部数据-事件便是来自过去的信息,服务之间的命令则是“对将来的但愿”。
服务发现要解决的问题就是如何定位一系列的服务从而可使用地址去调用。其中最简单的手段就是将地址和端口信息硬编码在全部服务中或者外置在服务的配置文件中。这种方式的问题在于其是一种静态部署模型,与微服务的初衷是相矛盾的。
服务须要保持解耦和移动,而系统须要是弹性和动态的。所以能够经过使用控制反转(Inversion of Control)模式引入一个间接层来解决此问题。也就是说每个服务都上报本身的信息(位置、如何访问)给一个统一的平台。这个平台被称做“服务发现”,是微服务平台的一个基础部分。这样,一旦服务的信息被存储了,服务就可使用“服务注册中心”来查找调用服务的信息,这种模式被称做“Client-Side服务发现”。另外一种策略是将信息存储、维护在一个负载均衡器(AWS的ELB)或者直接维护在服务提供方的地址中-“Server-Side服务发现”。
能够选择CP特性的数据库做为服务信息的存储,可以保证信息的一致性。可是这种数据库是牺牲了必定程度的可用性来达到强一致性的,而且依赖一些额外的基础设施,而不少时候强一致性并不是那么须要。所以,更好的选择是使用AP特性的点对点的技术来存储,好比使用CRDTs(Conflict-Free Replicated Data Types )与Epidemic Gossip能够实现信息的最终一致性传播,可以有更好的弹性,也不须要额外的基础设施。
API管理解决的问题在于如何将服务的协议和API统一管理起来,以方便服务的调用。包括协议和数据版本的升级和后退等。解决此问题能够经过引入一个负责序列化编码、协议维护以及数据传输的层,甚至直接将服务版本化。这在DDD中被称做”Anti-Corruption”层,能够加入到服务自己或者在API网关中实现。
假如一个客户端须要调用10个服务(每个都有不一样的API)来完成一个任务,那么对于这个客户端来讲是很是繁琐的。相比起让客户端直接去调用服务,更好的方式是让客户端经过API网关服务来调用。API网关负责接受客户端的请求,而后路由请求到相应的服务(若是有必要须要转换协议),组装响应并将其返回给客户端。这样,作为客户端和服务之间的一层其就可以简化client-to-service协议。但这里若是是中心化的则很难达到高可用和可扩展性,因此使用去中心化技术(好比服务发现)实现API网关则是更好的选择。
但须要注意的是API网关,包括全部的核心出服务并非必定要自建的,理想地它应该是底层平台的一部分。
在一个由数个微服务组成的系统中,使用点对点的通讯就能完成服务间的通讯工做。可是当服务数目愈来愈多,若是仍是让他们之间直接调用,那么很快整个系统会变得混乱不堪。解决此问题须要一个机制可以解耦发送者和接受者,而且可以按照某种定义好的原则路由数据。
发布订阅机制是一种解决方案:发布者发布信息到某个topic中,订阅者监听此topic以监听消息。可使用可扩展消息系统(Apache Kafka、Amazon Kinesis)或者NoSQL数据库(AP特性数据库,如Cassendra和Riak)来实现。
在SOA架构中,ESB承担的即这种角色。微服务中咱们确定不会使用它来桥接单体应用,可是能够将它作为一个发布系统用来广播任务和数据或者作为系统间的通讯总线(经过Spark Streaming收集数据到Spark中)。
发布订阅协议有时候也是有不足的。好比没法提供容许程序员自定义路由规则的高级路由特性或者数据的转化、丰富、分隔以及合并等功能(可使用Akka Streams或者Apache Camel)。
微服务技术是程序员都离不开的话题,特别是对于有想法,有目标的程序员,因此顺便在这里给你们推荐一个交流学习群:650385180,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,如下的知识体系图也是在群里获取。相信对于已经工做和遇到技术瓶颈的码友,在这个群里必定有你须要的内容。
系统与外界或者系统之间的通讯都是必需的。当与一个外部系统通讯时,尤为当外部系统没法把控时,那么就会有很大的失败风险(系统超载、业务失败)。所以即便协议协商得再好,也不能信赖外部服务,须要作好各类预防措施以保证自身服务的安全。
首先要达成一个良好的协议从而能够最小化一个系统突发超载形成服务不可用的风险,好比要避免发起的请求超过服务提供方的承载能力。也要尽可能避免使用同步通讯机制,不然就把自身服务的可用性放在了依赖的第三方服务的控制中。
避免级联失败须要服务足够解耦和隔离。使用异步通讯机制是一个最佳的方案。此外,还须要经过背压(back-pressure,接收方根据本身的接受情况调节接受速率,经过反向的响应来控制发送方的发送速率)来达成数据流速度的一致性,以防止响应快速的系统压垮其中较慢的部分。而愈来愈多的工具和库都在开始拥抱“响应式流”(Reactive Streams)规范(Akka Stream、RxJava、Spark Streaming、Cassandra drivers),这些技术使用异步背压实时流来桥接系统,从而在整体上提升系统的可靠性、性能以及互操做性。
如何管理调用服务时候的失败也是微服务中一个关键的问题。捕获到错误后,先重试,而若是错误一直发生,那么就隔离服务一段时间直到服务恢复-“断路器”模式(Netflix和Akka中都有实现)。
面对可扩展性、高吞吐以及可用性的要求,系统集成的实现从传统的依赖于中心化服务如RDBMS/ESB逐渐变为如今采用去中心化策略(HTTP REST、ZeroMQ)或者订阅发布系统(Kakka、Amazon Kinesis)。而最近事件流平台(Event Streaming Platforms)正成为系统集成选型的趋势,其理念来自于Fast Data和实时数据管理。
如上文所述,服务之间使用异步通讯机制可以获得不少的好处。可是若是是客户端(浏览器、APP)与服务之间的通讯,使用REST常常是更好的选择。可是并不是全部的地方都非得使用同步通讯机制,须要根据不一样的场景作不一样的评估。不少状况下,开发者出于习惯都会倾向于使用同步方案,而不是根据真正的须要做出可以简化操做、提高操做性的选择。这里给出几个一般会使用同步方案建模但其本质是异步行为的事例:
对于上述实例,咱们须要分别进行分析去理解怎样才是符合客户端和服务通讯的最天然的方式。同时也常常须要根据数据的完整性约束来寻找能够弱化一致性保证(有序)的可能,目的就是找到最少的协调约束条件给用户以直观的语义:找到利用现实的最佳策略。
安全管理主要是对服务的认证受权管理,限制某些service只容许某些服务访问。
微服务架构中,完成一个任务须要多个服务的协同,所以最小化服务之间的状态协做成本,有助于提高微服务系统的总体性能。
须要作的是要从业务的视角去分析数据以理解数据间的关系、担保和完整性约束。而后对数据进行反范式设计并在系统内定义一致性边界。如此,能够在边界内部实现强一致性。接着,须要使用这些边界来驱动微服务的设计和范围。若是设计的服务之间有不少数据依赖和关系,那么就须要去减小甚至是消除这些数据的耦合,从而避免对服务状态的协同。
如上节所述,已经最小化数据耦合了,但仍然仍是会有业务场景须要多个服务协做完成。这个的确是没法避免的,但到了目前这一步,须要作的是能够根据须要逐渐的添加协做,而不是一开始各类耦合再逐渐去消除(比较麻烦和困难)。
这里提供几种可扩展、弹性伸缩的方式来协同数据改变,以达到Composability(对数据的变更无须中止数据所在的服务,也无须等待某些条件)。
CRDTs是一个囊括了上面这些东西的工具,能够实现最终一致性、据有丰富的数据结构(counters、sets、maps、graphs),而且不须要协做就能够收敛聚合。其更新操做的顺序先后也并不影响最终的合并结果,可以自动安全的进行合并。虽然CRDTs最近才出如今业界视野中,但其实它已经在生产环境使用了不少年。已经有一些生产级别的库能够直接使用(Akka、Riak)。
然而,不少业务场景并不容许最终一致性。这时可使用因果一致性(causal consistency)。因果关系很容易被你们理解。并且因果一致性模型可以实现可扩展性和可用性。其通常使用逻辑时间,在不少NoSQL数据库、事件日志以及分布式事件流产品中均可用。
分布式事务是一个常用的方式用来协调分布式系统的变更,但其本质须要约束并发执行,保证同一时间只有一个用户在操做。所以其成本很是昂贵,会使得系统变慢、没法扩展。Saga模式是分布式事务以外的一个可以实现可扩展、弹性伸缩的选择。它的理论基础在于一个长时间运行的业务事务大多时候都是由多个事务步骤组成的,而事务步骤的整体一致性可以经过将这些步骤分组成一个整体的分布式事务来实现。该技术将每个阶段的事务与一个可补偿回滚的事务配对,若是一个阶段的事务失败了,那么整体的分布式事务就能够回滚(反向顺序)。
当设计一个响应式微服务时,须要坚持隔离、单一职责、自主、独占状态、异步消息传输和移动等特质。微服务须要协做才能造成一个系统去发挥做用。一个可以提供基础服务和响应式原则模式的复杂微服务平台是有必要的。