精华内容
下载资源
问答
  • 响应式微服务架构

    千次阅读 2019-08-15 18:07:26
    响应式系统 响应式系统和传统方法的对比 1.处理过程 传统开发方式下,使用的是拉取数据的变化,这是一种间歇性、互不相关的处理过程。前端组件不关心数据库中的数据是否发生变化。 在响应式开发方式下,处理过程只有...

    响应式系统

    响应式系统和传统方法的对比
    1.处理过程
    传统开发方式下,使用的是拉取数据的变化,这是一种间歇性、互不相关的处理过程。前端组件不关心数据库中的数据是否发生变化。
    在响应式开发方式下,处理过程只有在数据变化才会被触发,类似于推送数据的工作方式。
    理解拉取数据和推送数据的区别,可以借助获取推荐博客50条的业务场景。拉取数据可以减少大量的无意义的数据交互,推送数据能够缓解高并发、高访问下的服务器并行压力。
    2.线程管理
    响应式系统相对于传统的开发方式来说,线程周期很短,所有资源之间存在竞争的关系更少。
    3.伸缩性
    响应式开发方式支持传统开发方式下的各种伸缩性实现机制,并提供了更多的分布式实现选择。

    响应式系统的特性

    • 即时响应性:只要有可能,系统就会及时地做出响应。
    • 回弹性:指的是系统出现失败时依然保持即时响应性。
    • 弹性:指的是在系统在不断变化的工作负载之依然保持即时响应性。
    • 消息驱动:指的是响应式系统依赖异步的消息传递,从而确保松耦合、隔离、位置透明的组件之间有明确边界。
    • 基于数据流:一旦数据库中的数据有更新,系统的前端组件上就能有相应的变化。

    微服务框架

    微服务框架有以下的特点:服务组件化、按业务逻辑组织服务、去中心化、基础设施自动化。

    服务拆分与集成
    任何业务都需要使用某个数据容器作为持久化的机制或者数据处理的媒介,这里的数据容器不仅指关系型数据库,还泛指包括消息队列、搜索引擎索引以及各种NoSql在内的数据媒介。
    所以微服务的拆分是围绕业务代码和数据库进行拆分的。通常的方法有绞杀者模式(逐步替换老系统)和修缮者模式(新老系统共存)。

    服务集成(服务之间相互通讯)
    接口集成:通过RPC、REST消息传递和服务总线的服务接口调用。
    数据集成:通过共享数据库或者数据复制的方式实现数据集成。
    客户端集成:由于部分微服务包含一些UI界面,通过这些UI界面就能调用其背后的服务。客户端集成的方式包括直接集成、使用FrontEnd服务器和使用API网关。
    外部集成:很多服务之间的集成包括了与外界服务的依赖和整合,这种集合方式可能包含以上三种方式。

    微服务的核心组件
    事件驱动:事件可传输于松散耦合的组件和服务之间。
    负载均衡:服务器端负载均衡(nginx)+客户端负载均衡(ribbon)
    API网关:使所有的客户端和消费端都通过统一的网关接入微服务。
    配置中心:集中式配置管理,确保同一个服务组件使用的配置文件,能够同步地被更新。
    服务治理:实现自动化的服务注册和发现,并对不可用的服务进行周期性心跳检测,并踢出
    服务可靠:服务熔断机制,防止由于单个服务组件宕机,导致服务整体的雪崩。
    服务监控:通过日志聚合和服务跟踪技术实现 管理服务之间的调用关系、跟踪业务流的处理顺序个结果。

    注:
    服务器端负载均衡和客户端负载均衡区别在于服务清单所存储的位置;
    服务器端负载均衡的清单配置在 负载均衡服务器中;
    而客户端负载均衡 会从治理服务器中获取服务器地址列表,每一个客户端都存有服务器地址列表。

    响应式微服务架构

    响应式微服务架构设计原则

    • 隔离一切事物:系统的隔离性能够保证服务之间不会因为逻辑上错误捕获、错误处理,而导致服务的雪崩现象。
    • 自主行动:隔离是自主的前提,少的依赖关系,能够保证在面对冲突和失败状况时,能够很快地锁定错误。
    • 单一职责:提高了系统整体的可扩展、弹性、易维护等。
    • 拥护自己的私有状态:确保服务有独立的数据库。
    • 拥抱异步消息传递:解决技术耦合、空间耦合、时间耦合的问题,打破技术、空间、时间上的限制。
    • 保证移动,但可寻址:保证服务的地址的稳定性(负载均衡)

    在这里插入图片描述

    展开全文
  • 微服务是响应式原则的一个架构设计,其借鉴了SOA架构中好的理念,并使用了现代的基础服务设施(云服务、自动化工具等)。 响应式微服务定义 使用微服务架构最关键的一个原则就是将系统划分成一个个相互隔离、无依赖...
        

    O’Reilly的电子书《Reactive Microservices Architecture》讲述了微服务/分布式系统的一些设计原则,本文是笔者阅读完此书后的理解。书籍地址:https://info.lightbend.com/COLL-20XX-Reactive-Microservices-Architecture-RES-LP.html


    微服务相比传统的单体应用能够带来快速的响应,以小的系统产生大的影响。而随着网络加速、磁盘成本降低、RAM成本降低、多核技术的发展、云架构技术的爆发,微服务不再受这些客观条件的限制,已经开始大规模的应用。


    与SOA架构,微服务和它都具有相同的初衷:解耦、隔离、组合、集成、分散以及自主,但是SOA经常被误解和误用,尤其是使用ESB来支持对多个单体系统的协议(复杂、低效、不稳定)调用,使得系统变得非常复杂。而随着这些年硬件以及软件架构理念的发展,所有的系统基本都已经变成分布式架构,也带来了很多新的挑战。也就需要新的思路和理念来面对这些问题,其中本书所讲述的响应式原则(Reactive principles)即一种解决分布式系统的思路。响应式原则也并非一个新的东西,Erlang中的Actor模型即一种响应式设计。微服务是响应式原则的一个架构设计,其借鉴了SOA架构中好的理念,并使用了现代的基础服务设施(云服务、自动化工具等)。


    响应式微服务定义


    使用微服务架构最关键的一个原则就是将系统划分成一个个相互隔离、无依赖的子系统,这些子系统通过定义良好的协议进行通信。其中隔离是实现弹性、可伸缩系统的前提,并且需要在服务间建立异步通信边界,因此要在以下两方面进行解耦:


    • 时间:允许并发。

    • 空间:允许分布式和移动性,即服务能够随时移动。


    此外,微服务还需要消除共享状态从而最小化相互协作、联结的成本,要尽量达到“不共享任何东西”。


    隔离任何东西


    隔离是微服务架构中最重要的特性。不仅仅是微服务带来的很多优势的基础,也是对设计和架构影响最大的方面。如康威定律所说,它还对组织架构有非常大的影响。


    系统的结构是对团队组织架构的反映。


    失败隔离是一种与“舱壁”(船舱的隔板)相关的设计模式:隔离错误、失败以防止其蔓延至所有服务,导致更大面积的失败。


    “舱壁”这种模式已经在轮船上使用了几个世纪:创建一个个密封不漏水的空间以防止船的外壳破损或者其他泄漏。这些空间是完全互相隔离的,这样即使一个隔离区充满了水,也不会蔓延流到其他隔离空间中,从而使得船整体仍然能够运作。


    弹性(从失败中恢复的能力)即依赖于这种舱壁和失败隔离的设计,并且需要打破同步通信机制。由此,微服务一般是在边界之间使用异步消息传输,从而使得正常的业务逻辑避免对捕获错误、错误处理的依赖。


    进一步的,服务之间的隔离使得“持续交付”变得很容易,能够随时地部署服务而无需担心影响正常的业务。而且隔离的单个服务很容易监控、调试、测试和部署,非常便于扩展。


    自主地行动


    上面所讲的隔离是自主性的前提。只有当服务之间是完全隔离的,那么才可能实现完全的自主,包括独立的决策、独立的行动以及与其他服务协调合作来解决问题。


    一个自主的服务仅仅保证其对外公布的协议/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”的理念的改变。之前的数据是离线被使用的,而现在的数据是被在线处理的。应用对数据变更的响应需要达到实时级别:当变动发生,需要实时进行持续的查询、聚合并反馈给最终的应用。这个理念的形成经历了三个主要阶段:


    1. “data at rest”: 将大量数据存储在HDFS类似的数据存储媒介中,然后使用离线批处理技术去处理这些数据,一般会有数个小时的延迟。


    2. 意识到了“data in motion”正变得越来越重要:在数秒内捕获数据、处理数据并反馈给运行中的系统。Lambda即此时出现的一种架构: 加速层用来做实时在线计算;批处理层用来做复杂的离线处理。加速层实时处理的结果后续被批处理层的结果合并。这个模型解决了某些场景需要数据即时响应的问题,但其架构的复杂使得不容易维护。


    3. “data in motion”: 全面拥抱移动数据的概念。传统的面向批处理的架构都在逐渐向纯流处理的架构转变。这种模型作为通信协议和持久化方案(通过Event Logging)也能够给微服务带来“data in motion”和流处理的能力。


    保持移动,但可寻址


    如上述所讲,异步消息传输带来了对时间和空间的解耦。其中,对于空间的解耦也被称为“位置透明”:在多核或者多结点上的微服务在运行时无须改变结点即可以动态扩展的能力。这也决定了系统的弹性和移动性。要实现这些需要依赖云计算带来的一些特性和其“按需使用”模型。


    而可寻址则是说服务的地址需要是稳定的,从而可以无限地引用此服务,而无论服务目前是否可以被定位到。当服务在运行中、已经停止、被挂起、升级中、已经崩溃等等情形下,地址都应该是可用的,任意客户端能够随时发送消息给一个地址。实际中,这些消息有可能进入队列排队、重提交、代理、日志记录或者进入死信队列。此外,地址需要是虚拟的,可以代表一组实例提供的服务。


    • 在无状态的服务间做负载均衡:如果服务是无状态的,那么请求被哪一个服务实例处理都是没任何问题的。也有很多种的路由算法供使用,如:轮训、广播或者基于度量信息。


    • 在有状态的服务之间构建Active-Passive的冗余设计:如果一个服务是有状态的,那么可以使用sticky路由算法(同一个客户端的请求都会发送给同一个服务实例)。冗余一个passive实例是为了在主实例挂的时候接管上面的请求。因此,服务的每一个状态变动都需要同步到passive实例上。


    • 有状态的服务的重定位:将一个服务实例从一个位置移动到另一个位置可以提高引用的本地性(让数据和计算靠近)和资源利用率。


    使用虚拟地址能够让服务消费方无须关心服务目前是如何配置操作的,只要知道地址即可。


    微服务系统实现


    一个微服务并非真正的“微服务”,一系列微服务通过通信、合作才能够解决问题,才能组成一个完整的业务系统。实现一个服务是相对简单的,困难的是其他基础设施的实现:服务发现、协作、安全、冗余、数据一致性、容错、部署以及与其他系统的集成。


    系统需要利用现实


    微服务架构带来的一个很大优势就在于它提供了一套工具,能够利用现实,模仿真实的世界来创建系统,包括真实世界的限制和机会。


    首先根据“康威定律”,微服务的部署是和现实中工程组织/部门如何工作是相适应的。此外,还需要注意的是现实不是一致的,任何事情都是相对的,即使是时间和“现在”这个概念。


    信息的传播速度不可能比光快,甚至大部分是很慢的,这也意味着信息通信是有延迟的。信息都是来自过去的,我们稍微思考一下可以知道信息承载的都是我们观察到的东西。而我们观察/学习到的事实至少都是很短时间之前发生的,也就是说我们总是在看过去,“现在”只是旁观者的视角。


    每一个微服务都可以看做一个安全的小岛,提供了确定性和强一致性,上面的时间和“目前”都是绝对的。但是当离开一个微服务的边界时,就进入了一片充满非确定性的大海-分布式系统的世界。如很多人所说,构建分布式系统是困难的。但现实世界同时也提供了如何解决诸如弹性、可伸缩、隔离性等分布式问题的解决思路。因此,即使构建分布式系统是困难的,但是我们也不应该退化为单体应用,而是学习如何使用一系列的设计原则、抽象概念和工具来管理它。


    正如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管理解决的问题在于如何将服务的协议和API统一管理起来,以方便服务的调用。包括协议和数据版本的升级和后退等。解决此问题可以通过引入一个负责序列化编码、协议维护以及数据传输的层,甚至直接将服务版本化。这在DDD中被称作”Anti-Corruption”层,可以加入到服务本身或者在API网关中实现。


    假如一个客户端需要调用10个服务(每一个都有不同的API)来完成一个任务,那么对于这个客户端来说是非常繁琐的。相比起让客户端直接去调用服务,更好的方式是让客户端通过API网关服务来调用。API网关负责接受客户端的请求,然后路由请求到相应的服务(如果有必要需要转换协议),组装响应并将其返回给客户端。这样,做为客户端和服务之间的一层其就能够简化client-to-service协议。但这里如果是中心化的则很难达到高可用和可扩展性,所以使用去中心化技术(比如服务发现)实现API网关则是更好的选择。


    640?wx_fmt=png


    但需要注意的是API网关,包括所有的核心出服务并不是一定要自建的,理想地它应该是底层平台的一部分。


    管理通信模式


    在一个由数个微服务组成的系统中,使用点对点的通信就能完成服务间的通信工作。但是当服务数目越来越多,如果还是让他们之间直接调用,那么很快整个系统会变得混乱不堪。解决此问题需要一个机制能够解耦发送者和接受者,并且能够按照某种定义好的原则路由数据。


    发布订阅机制是一种解决方案:发布者发布信息到某个topic中,订阅者监听此topic以监听消息。可以使用可扩展消息系统(Apache Kafka、Amazon Kinesis)或者NoSQL数据库(AP特性数据库,如Cassendra和Riak)来实现。


    在SOA架构中,ESB承担的即这种角色。微服务中我们肯定不会使用它来桥接单体应用,但是可以将它做为一个发布系统用来广播任务和数据或者做为系统间的通信总线(通过Spark Streaming收集数据到Spark中)。


    发布订阅协议有时候也是有不足的。比如无法提供允许程序员自定义路由规则的高级路由特性或者数据的转化、丰富、分隔以及合并等功能(可以使用Akka Streams或者Apache Camel)。


    集成


    系统与外界或者系统之间的通信都是必需的。当与一个外部系统通信时,尤其当外部系统无法把控时,那么就会有很大的失败风险(系统超载、业务失败)。因此即使协议协商得再好,也不能信赖外部服务,需要做好各种预防措施以保证自身服务的安全。


    首先要达成一个良好的协议从而可以最小化一个系统突发超载造成服务不可用的风险,比如要避免发起的请求超过服务提供方的承载能力。也要尽量避免使用同步通信机制,否则就把自身服务的可用性放在了依赖的第三方服务的控制中。


    避免级联失败需要服务足够解耦和隔离。使用异步通信机制是一个最佳的方案。此外,还需要通过背压(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只允许某些服务访问。


    • TLS Client Certificates也被称为相互验证、双路验证。它给每一个service都分配一个单独的私钥和证书,从而能够很好地保证服务间的认证访问。不仅仅服务要验证客户端的身份,客户端也要验证服务的身份。因此,其不仅能防止数据被窃听,而且即使在不安全的网络中也能防止对数据的拦截和转发。基于SSL之上的通信不仅安全,其也是一个公开、易于理解的标准。但是其非常复杂,无法得到底层平台的足够支持。同样的,HTTPS Basic Authentication也是双路验证,但其对SSL证书的管理也很复杂,请求也不能被反向代理缓存。


    • Asymmetric Request Signing:每一个服务都需要使用自己的私钥给自己发送的请求进行签名,同时每一个服务的公钥都要上报给“服务发现”服务。此方案的缺点在于一旦网络不可靠,那么则很难防止数据窃听或者请求重放攻击。


    • Hash Message Authentication Code (HMAC) : 基于共享密钥来对请求进行一定规则的签名。这个方案比较简单,但是由于每一对需要通信的服务都需要唯一的一个共享密钥,整个系统则需要所有服务排列数目的共享密钥数量,实现起来比较麻烦。


    最小化数据耦合


    微服务架构中,完成一个任务需要多个服务的协同,因此最小化服务之间的状态协作成本,有助于提升微服务系统的整体性能。


    需要做的是要从业务的视角去分析数据以理解数据间的关系、担保和完整性约束。然后对数据进行反范式设计并在系统内定义一致性边界。如此,可以在边界内部实现强一致性。接着,需要使用这些边界来驱动微服务的设计和范围。如果设计的服务之间有很多数据依赖和关系,那么就需要去减少甚至是消除这些数据的耦合,从而避免对服务状态的协同。


    最小化协作成本


    如上节所述,已经最小化数据耦合了,但仍然还是会有业务场景需要多个服务协作完成。这个的确是无法避免的,但到了目前这一步,需要做的是可以根据需要逐渐的添加协作,而不是一开始各种耦合再逐渐去消除(比较麻烦和困难)。


    这里提供几种可扩展、弹性伸缩的方式来协同数据改变,以达到Composability(对数据的变动无须停止数据所在的服务,也无须等待某些条件)。


    • Apology-Oriented Programming: 基于请求原谅比请求权限容易的想法。如果你不能响应协作,那么就做出一个合理的猜测,赌一个条件已经满足,后续如果错了,那么就道歉并做补偿。这种做法和现实是非常相似的。比如航班的超售,如果起飞的时候没有座位那么就去做一些补偿措施。


    • 事件驱动架构(Event-Driven Architecture ):基于异步消息传输和事件溯源。需要区分命令和事件,命令表示一个将要产生副作用的操作意图(对未来的希望),而事件表示已经发生的事实。使用CQRS模式进行查询,将写入方(持久化事件日志)与读取方(将数据存入RDBMS或者NoSQL数据库中)分离。这里使用事件日志做状态管理和持久化具有很多好处:简化审计、调试、冗余、容错,并且允许重放过去任意时间点的事件流。


    • ACID2.0:由Pat Helland创造,定义了一组原则,目的是实现可扩展、弹性的协议以及API设计。A是Associative,表示分组消息不会产生影响,可以批量处理。C是Commutative,表示消息的顺序不重要。I是Idempotent,表示消息重复不会产生影响。D是Distributed,没有实质的意义,猜测是为了凑ACID这个首字母缩写。


    CRDTs是一个囊括了上面这些东西的工具,可以实现最终一致性、据有丰富的数据结构(counters、sets、maps、graphs),并且不需要协作就可以收敛聚合。其更新操作的顺序前后也并不影响最终的合并结果,能够自动安全的进行合并。虽然CRDTs最近才出现在业界视野中,但其实它已经在生产环境使用了很多年。已经有一些生产级别的库可以直接使用(Akka、Riak)。


    然而,很多业务场景并不允许最终一致性。这时可以使用因果一致性(causal consistency)。因果关系很容易被大家理解。而且因果一致性模型能够实现可扩展性和可用性。其一般使用逻辑时间,在很多NoSQL数据库、事件日志以及分布式事件流产品中都可用。


    分布式事务是一个经常使用的方式用来协调分布式系统的变动,但其本质需要约束并发执行,保证同一时间只有一个用户在操作。因此其成本非常昂贵,会使得系统变慢、无法扩展。Saga模式是分布式事务之外的一个能够实现可扩展、弹性伸缩的选择。它的理论基础在于一个长时间运行的业务事务大多时候都是由多个事务步骤组成的,而事务步骤的总体一致性能够通过将这些步骤分组成一个总体的分布式事务来实现。该技术将每一个阶段的事务与一个可补偿回滚的事务配对,如果一个阶段的事务失败了,那么总体的分布式事务就可以回滚(反向顺序)。


    640?wx_fmt=png


    总结


    当设计一个响应式微服务时,需要坚持隔离、单一职责、自主、独占状态、异步消息传输和移动等特质。微服务需要协作才能形成一个系统去发挥作用。一个能够提供基础服务和响应式原则模式的复杂微服务平台是有必要的。


    出处:http://www.rowkey.me/blog/2018/06/07/reactive-microservice/


    640?wx_fmt=png

    架构文摘

    ID:ArchDigest

    互联网应用架构丨架构技术丨大型网站丨大数据丨机器学习

    640?wx_fmt=jpeg

    更多精彩文章,请点击下方:阅读原文

    展开全文
  • 随着以Dubbo、Spring Cloud等框架为代表的分布式服务调用和治理工具的大行其道,以及以Docker、Kubernetes等容器技术的日渐成熟,微服务架构(Mi...
        

    随着以Dubbo、Spring Cloud等框架为代表的分布式服务调用和治理工具的大行其道,以及以Docker、Kubernetes等容器技术的日渐成熟,微服务架构(Microservices Architecture)毫无疑问是近年来最热门的一种服务化架构模式。所谓微服务,就是一些具有足够小的粒度、能够相互协作且自治的服务体系。正因为每个微服务都比较简单,仅关注于完成一个业务功能,所以具备技术、业务和组织上的优势[1]


    另一方面,随着Spring 5的正式发布,我们引来了响应式编程(Reactive Programming)的全新发展时期。Spring 5中内嵌了响应式Web框架、响应式数据访问、响应式消息通信等多种响应式组件,从而极大地简化了响应式应用程序的开发过程和难度。


    本章作为全书的开篇,将对微服务架构和响应式系统(Reactive System)的核心概念做简要介绍,同时给出两者之间的整合点,即如何构建响应式微服务架构。在本章最后,我们也会给出全书的组织架构以总领全书。


    响应式系统核心概念


    在本节中,我们将带领大家进入响应式系统的世界。为了让大家更好地理解响应式编程和响应式系统的核心概念,我们将先从传统编程方法出发逐步引出响应式编程方法。同时,我们还将通过响应式宣言(Reactive Manifesto)了解响应式系统的基本特性和设计理念。


    从传统编程方法到响应式编程方法


    在电商系统中,订单查询是一个典型的业务场景。用户可以通过多种维度获取自己已下订单的列表信息和各个订单的明细信息。我们就通过订单查询这一特定场景来分析传统编程方法和响应式编程方法之间的区别。


    1.订单查询场景的传统方法

    在典型的三层架构中,图1-1展示了基于传统实现方法的订单查询场景时序图。一般用户会使用前端组件所提供的操作入口进行订单查询,然后该操作入口会调用后台系统的服务层,服务层再调用数据访问层,进而访问数据库,数据从数据库中获取之后逐层返回,最后显示在包括前端服务或用户操作界面在内的前端组件上。

     

    640?wx_fmt=jpeg

    图1-1订单查询场景的传统实现方法时序图

    显然,在图1-1所展示的整个过程中,前端组件通过主动拉取的方式从数据库中获取数据。如果用户不触发前端操作,那么就无法获取数据库中的数据状态。也就是说,前端组件对数据库中的任何数据变更一无所知。


    2.订单查询场景的响应式方法

    主动拉取数据的方式在某些场景下可以运作得很好,但如果我们希望数据库中的数据一有变化就通知到前端组件,这种方式就不是很合理。这种场景下,我们希望前端组件通过注册机制获取数据变更的事件,图1-2展示了这一过程。


    在图1-2中,我们并不是直接访问数据库来获取数据,而是订阅了OrderChangedEvent事件。当订单数据发生任何变化时,系统就会生成这一事件,然后通过一定的方式传播出来。而订阅了该事件的服务就会捕获该事件,从而通过前端组件响应该事件。事件处理的基本步骤涉及对某个特定事件进行订阅,然后等待事件的发生。如果不需要再对该事件做出响应,我们就可以取消对事件的订阅。

     

    640?wx_fmt=jpeg

    图1-2订单查询场景的响应式实现方法时序图

    图1-2体现的是响应式系统中一种变化传递(Propagation Of Change)思想,即当数据变化之后,会像多米诺骨牌一样,导致直接和间接引用它的其他数据均发生相应变化。一般而言,生产者只负责生成并发出事件,然后消费者来监听并负责定义如何处理事件的变化传递方式。


    显然,这些事件连起来会形成一串数据流(Data Stream),如果我们能够及时对数据流的每一个事件做出响应,就会有效提高系统的响应能力。基于数据流是响应式系统的另一个核心特点。


    我们再次回到图1-1,如果从底层数据库驱动,经过数据访问层到服务层,最后到前端组件的这个服务访问链路全部都采用响应式的编程方式,从而搭建一条能够传递变化的管道,这样一旦数据库中的数据有更新,系统的前端组件上就能相应地发生变化。而且,当这种变化发生时,我们不需要通过各种传统调用方式来传递这种变化,而是由搭建好的数据流自动进行传递。


    3.传统方法与响应式方法的对比

    图1-1展示的传统方法和图1-2展示的响应式方法具有明显的差异性,我们分别从处理过程、线程管理和伸缩性角度做简要对比。


    (1)处理过程

    传统开发方式下,我们拉取(Pull)数据的变化,这意味着整个过程是一种间歇性、互不相关的处理过程。前端组件不关心数据库中的数据是否有变化。

    在响应式开发方式下,一旦对事件进行注册,处理过程只有在数据变化时才会被触发,类似一种推(Push)的工作方式。


    (2)线程管理

    在传统开发方式下,线程的生命周期比较长。在线程存活的状态下,该线程所使用的资源都会被锁住。当服务器在同时处理多个线程时,就会存在资源的竞争问题。


    在响应式开发方式下,生成事件和消费事件的线程的存活时间都很短,所以资源之间存在较少的竞争关系。


    (3)伸缩性

    传统开发方式下,系统伸缩性涉及数据库和应用服务器的伸缩,一般我们需要专门采用一些服务器架构和资源来应对伸缩性需求。


    在响应式开发方式下,因为线程的生命周期很短,同样的基础设施可以处理更多的用户请求。同时,响应式开发方式同样支持传统开发方式下的各种伸缩性实现机制,并提供了更多的分布式实现选择。图1-3展示了事件处理与系统伸缩性之间的关系。

     

    640?wx_fmt=jpeg

    图1-3  事件处理与系统伸缩性示意图

    在图1-3中,显然Web应用程序和事件处理程序可以分别进行伸缩,这为伸缩性实现机制提供更多的选型余地。


    响应式宣言与响应式系统


    如同业界的其他宣言一样,响应式宣言是一组设计原则,符合这些原则的系统可以认为是响应式系统。同时,响应式宣言也是一种架构风格,是一种关于分布式环境下系统设计的思考方式,响应式系统也是具备这一架构风格的系统。


    1.响应式系统特性

    响应式宣言给出了响应式系统所应该具备的特性,包括即时响应性(Responsive)、回弹性(Resilient)、弹性(Elastic)以及消息驱动(Message Driven)。具备这些特性的系统可以称为响应式系统。图1-4给出了响应式宣言的图形化描述。

     

    640?wx_fmt=jpeg

    图1-4响应式宣言(来自响应式宣言官网)

    在图1-4中,响应式宣言认为,响应式系统的价值在于提供了即时响应性、可维护(Maintainable)和扩展性(E意味着可以快速地检测到问题并且有效地对其进行处理。即时响应的系统专注于提供快速而一致的响应时间,确立可靠的反馈上限,以提供一致的服务质量。这种一致的行为转而将简化错误处理、建立最终用户的信任,并促使用户与系统做进一步互动。


    (2)回弹性

    回弹性指的是系统在出现失败时依然保持即时响应性。这不仅适用于高可用的、任务关键型系统——任何不具备回弹性的系统都将会在发生失败之后丢失即时响应性。回弹性是通过复制、遏制、隔离以及委托来实现的。失败的扩散被遏制在了每个组件内部,与其他组件相互隔离,从而确保系统某部分的失败不会危及整个系统,并能独立恢复。每个组件的恢复都被委托给了另一个内部或外部组件。此外,在必要时可以通过复制来保证高可用性。因此,组件的客户端不再承担组件失败的处理。


    (3)弹性

    弹性指的是系统在不断变化的工作负载之下依然保持即时响应性。响应式系统可以对输入的速率变化做出反应,比如,通过增加或者减少被分配用于服务这些输入的资源。这意味着设计上并没有竞争点和中央瓶颈,系统得以进行组件的分片或者复制,并在它们之间分布输入。通过提供相关的实时性能指标,响应式系统能支持预测式以及响应式的伸缩算法。这些系统可以在常规的硬件以及软件平台上实现高效的弹性。


    (4)消息驱动

    消息驱动指的是响应式系统依赖异步的消息传递,从而确保松耦合、隔离、位置透明的组件之间有着明确边界。这一边界还提供了将失败作为消息委托出去的手段。使用显式的消息传递,可以通过在系统中塑造并监视消息流队列,并在必要时应用背压,从而实现负载管理、弹性以及流量控制。使用位置透明的消息传递作为通信的手段,使得跨集群或者在单个主机中使用相同的结构成分和语义来管理失败成为了可能。非阻塞的通信使得接收者可以只在活动时才消耗资源,从而减少系统开销。


    2.响应式的维度

    响应式的概念还体现在不同维度上,包含响应事件、响应压力、响应错误和响应用户。


    (1)响应事件

    基于消息驱动机制,响应式系统可以对事件做出快速响应。


    (2)响应压力

    响应式系统可以在不同的系统压力下进行灵活响应。当压力较大时,使用更多的资源;而当压力变小时,则释放不需要的资源。


    (3)响应错误

    响应式系统可以优雅地处理错误,监控组件的可用性,并在必要时冗余组件。


    (4)响应用户

    响应式系统一方面能够积极响应用户请求,但当消费者没有订阅事件时,就不会浪费资源进行不必要的处理。


    剖析微服务架构


    目前,微服务架构已经成为一种主流的软件开发方法论,它把一种特定的软件应用设计方法描述为能够独立部署的服务套件。本节将对微服务设计原理与架构做精简而全面的介绍。


    分布式系统与微服务架构

    微服务架构首先表现为一种分布式系统(Distributed System),而分布式系统是传统单块系统(Monolith System)的一种演进。


    1.单块系统

    在软件技术发展过程的很长一段时间内,软件系统都表现为一种单块系统。时至今日,很多单块系统仍然在一些行业和组织中得到开发和维护。所谓单块系统,简单讲就是把一个系统所涉及的各个组件都打包成一个一体化结构并进行部署和运行。在Java EE领域,这种一体化结构很多时候就体现为一个WAR包,而部署和运行的环境就是以Tomcat为代表的各种应用服务器。


    单块系统有其存在和发展的固有优势。当团队规模并不是太大的时候,一个单块应用可以由一个开发者团队进行独立维护。该团队的成员能够对单块应用进行快速学习、理解和修改,因为其结构非常简单。同时,因为单块系统的表现形式就是一个独立的WAR包,想要对它进行集成、部署以及实现无状态集群相对也比较简单,通常只要采用负载均衡机制并运行该单块系统的多个实例,就能达到系统伸缩性要求。


    但在另一方面,随着公司或者组织业务的不断扩张、业务结构的不断变化以及用户量的不断增加,单块系统的优势已无法适应互联网时代的快速发展,面临着越来越多的挑战,例如,如何处理业务复杂度、如何防止代码腐化、如何处理团队协作问题以及如何应对系统伸缩性问题[1]。针对以上集中式单块系统所普遍存在的问题,基本的解决方案就要依赖于分布式系统的合理构建。


    2.分布式系统

    所谓分布式系统,是指硬件或软件组件分布在不同的网络计算机上,彼此通过一定的通信机制进行交互和协调的系统。我们从这个定义中可以看出分布式系统包含两个区别于单块系统的本质性特征:一个是网络,分布式系统的所有组件都位于网络之中,对互联网应用而言,则位于更为复杂的互联网环境中;另一个是通信和协调,与单块系统不同,位于分布式系统中的各个组件只有通过约定、高效且可靠的通信机制进行相关协作,才能完成某项业务功能。这是我们在设计和实现分布式系统时首先需要考虑的两个方面。


    分布式系统相较于集中式系统而言具备优势的同时,也存在一些我们不得不考虑的特性,包括但不限于网络传输的三态性、系统之间的异构性、数据一致性、服务的可用性等[1]。以上问题是分布式系统的基本特性,我们无法避免,只能想办法进行利用和管理,这就给我们设计和实现分布式系统提出了挑战。微服务架构本质上也是一种分布式系统,但在遵循通用分布式特性的基础上,微服务架构还表现出一定的特殊性。接下来我们将围绕微服务架构的这些特殊性展开讨论。


    3.微服务架构

    Martin Fowler指出[2],微服务架构具有以下特点。


    (1)服务组件化

    组件(Component)是一种可独立替换和升级的软件单元。在日常开发过程中,我们可能会设计和使用很多组件,这些组件可能服务于系统内部,也可能存在于系统所运行的进程之外。而服务就是一种进程外组件,服务之间利用诸如RPC(Remote Procedure Call,远程过程调用)的通信机制完成交互。服务组件化的主要目的是服务可以独立部署。如果你的应用程序由一个运行在独立进程中的很多组件组成,那么对任何一个组件的改变都将导致必须重新部署整个应用程序。但是如果你把应用程序拆分成很多服务,显然,通常情况下,你只需要重新部署那个改变的服务。在微服务架构中,每个服务运行在其独立的进程中,服务与服务之间采用轻量级通信机制互相沟通。


    (2)按业务能力组织服务

    当寻找把一个大的应用程序进行拆分的方法时,研发过程通常都会围绕产品团队、UED团队、APP前端团队和服务器端团队而展开,这些团队也就是通常所说的职能团队(Function Team)。当使用这种标准对团队进行划分时,任何一个需求变更,无论大小,都将导致跨团队协作,从而增加沟通和协作成本。而微服务架构下的划分方法有所不同,它倾向围绕业务功能的组织来分割服务。这些服务面向具体业务结构,而不是面向某项技术能力。因此,团队是跨职能的(Cross-Functional)的特征团队(Feature Team),包含用户体验、项目管理和技术研发等开发过程中要求的所有技能。每个服务都围绕着业务进行构建,并且能够被独立部署到生产/类生产环境。


    (3)去中心化

    服务集中治理的一种好处是在单一平台上进行标准化,但采用微服务的团队更喜欢不同的标准。把集中式系统中的组件拆分成不同的服务,我们在构建这些服务时就会有更多的选择。对具体的某一个服务而言,应该根据业务上下文选择合适的语言和工具进行构建。


    另一方面,微服务架构也崇尚于对数据进行分散管理。当集中式的应用使用单一逻辑数据库进行数据持久化时,通常选择在应用的范围内使用一个数据库。然而,微服务让每个服务管理自己的数据库,无论是相同数据库的不同实例,还是不同的数据库系统。


    (4)基础设施自动化

    许多使用微服务架构产品或者系统的团队拥有丰富的持续集成(Continue Integration)和持续交付(Continuous Delivery)经验。团队使用微服务架构构建软件需要更广泛地依赖基础设施自动化技术。


    在微服务中同样需要考虑服务容错性设计等分布式系统所需要考虑的问题,我们对以上特点进行总结和提炼,认为微服务具备业务独立、进程隔离、团队自主、技术无关轻量级通信和交付独立性等“微”特性。


    服务拆分与集成

    本节在微服务架构基本概念的基础上,简要分析服务拆分的策略和手段,同时也给出对拆分之后的服务进行集成的各种实现方法和技术体系。


    1.服务拆分

    在微服务架构中,我们认为服务是业务能力的代表,需要围绕业务进行组织。服务拆分的关键在于正确理解业务,识别单个服务内部的业务领域及其边界,并按边界进行拆分。所以微服务的拆分模式本质上是基于不同的业务进行拆分。业务体现在各种功能代码中,通过确定业务的边界,并使用领域与界限上下文(Boundary Context)、领域事件(Domain Event)等技术手段可以实现拆分。


    数据对微服务架构而言同样可以认为是一种依赖关系,因为任务业务都需要使用某个数据容器作为持久化的机制或者数据处理的媒介,这里的数据容器不仅指关系型数据库,还泛指包括消息队列、搜索引擎索引以及各种Nosql在内的数据媒介。微服务架构中存在一种说法,即我们需要将微服务用到的所有资源全部嵌入到该服务中,从而确保微服务的独立性。而数据的拆分则体现在如何将集中式的中心化数据转变为各微服务各自拥有的独立数据,这部分工作同样十分具有挑战性。


    关于业务和数据应该先拆分谁的问题,可以是先数据库后业务代码,也可以是先业务代码后数据库。然而在拆分中遇到的最大挑战可能会是数据层的拆分,因为在数据库中,可能会存在各种跨表连接查询、跨库连接查询以及不同业务模块的代码与数据耦合得非常紧密的场景,这会导致服务的拆分非常困难。因此,在拆分步骤上我们更多地推荐数据库先行。数据模型能否彻底分开,很大程度上决定了微服务的边界功能是否彻底划清。


    服务拆分的方法根据系统自身的特点和运行状态,通常可分为绞杀者与修缮者两种模式。绞杀者模式(Strangler Pattern)[3]指的是在现有系统外围将新功能用新的方式构建为新的服务的策略,通过将新功能做成微服务方式,而不是直接修改原有系统,逐步实现对老系统替换。采用这种策略,随着时间的推移,新的服务就会逐渐“绞杀”老的系统。对于那些规模很大又难以对现有架构进行修改的遗留系统,推荐采用绞杀者模式。而修缮者模式就如修房或修路一样,将老旧待修缮的部分进行隔离,用新的方式对其进行单独修复。修复的同时,需保证与其他部分仍能协同功能。从这种思路出发,修缮者模式更多地表现为一种重构技术,具体实现上可以参考Martine Fowler的BranchByAbstraction重构方法[4]


    2.服务集成

    服务之间势必需要集成,而这种集成关系远比简单的API调用要复杂。对于微服务架构而言,我们的思路是尽量采用标准化的数据结构和通信机制并降低系统集成的耦合度。我们把微服务架构中服务之间的集成模式分为以下四大类[1],同时还会引入其他一些手段来达到服务与服务之间的有效集成。


    (1)接口集成

    接口集成是服务之间集成的最常见手段,通常基于业务逻辑的需要进行集成。RPC、REST、消息传递和服务总线都可以归为这种集成方式。


    RPC架构是服务之间进行集成的最基本方式。在RPC架构实现思路上,远程服务提供者以某种形式提供服务调用相关信息,远程代理对象通过动态代理拦截机制生成远程服务的本地代理,让远程调用在使用上如同本地调用一样。而网络通信应该与具体协议无关,通过序列化和反序列化方式对网络数据进行有效传输。


    REST(Representational State Transfer,表述性状态转移)从技术上讲也可以认为是RPC架构的一种具体表现形式,因为RPC架构中最基本的网络通信、序列化/反序列化、传输协议和服务调用等组件都能在REST中有所体现。但REST代表的并不是一种技术,也不是一种标准和规范,而是一种设计风格。要理解RESTful架构,最好的方法就是去理解它的全称Representational State Transfer这个词组,直译过来就是“表述性状态转移”,针对的是网络上的各种资源(Resource)。所以REST通俗地讲就是:资源在网络中以某种表现形式进行状态转移。


    消息通信(Messaging)机制(或者称为消息传递机制)可以认为是一种系统集成组件,是在分布式系统中完成消息的发送和接收的基础软件,用于消除服务交互过程中的耦合度。关于耦合度的具体表现形式,我们在下一节中还会具体展开,消息通信机制实现系统解耦的做法是在服务与服务之间添加一个中间层,这样紧耦合的单阶段方法调用就转变成松耦合的两阶段过程,可以通过中间层进行消息的存储和处理,这个中间层就是以各种消息中间件为代表的消息通信系统(Messaging System)。


    企业服务总线(Enterprise Service Bus,ESB)本质上也是一种系统集成组件,用于解决分布式环境下的异步协作问题,可以看作是对消息通信系统的扩展和延伸。ESB提供了一批核心组件,包括路由器、转换器和端点。路由器(Router)在一个位置上维护消息目标地址并基于消息本身或上下文进行路由;转换器(Transformer)用于异构系统之间进行数据适配,数据结构、类型、表现形式、传输方式都是潜在的需要转换的对象;端点(Endpoint)封装了应用系统与服务总线系统的交互。


    (2)数据集成

    数据集成同样可以用于微服务之间的交互,常见的共享数据库(Shared Database)是一个选择,但也可以通过数据复制(Data Replication)的方式实现数据集成。


    在微服务架构中,我们追求数据的独立性。但对于一些遗留系统而言,我们无法重新打造数据体系,数据复制就成为一种折中的集成方法。所谓数据复制,就是在不同的数据容器中保存同一份业务数据。这里的同一份业务数据的概念不在于数据内容的完全一致性,而在于这些数据背后的业务逻辑的一致性。


    (3)客户端集成

    由于微服务是一个能够独立运行的整体,有些微服务会包含一些UI界面,这也意味着微服务之间也可以通过UI界面进行集成。从某一个微服务的角度讲,调用它的服务就是该服务的客户端。关于客户端与微服务之间的集成可以分为三种方式,即直接集成、使用FrontEnd服务器和使用API网关。


    直接集成方式比较简单,就是客户端通过微服务提供的访问入口直接对微服务进行集成。这种方式适合于微服务数量不是太多的场景。如果采用直接集成的方式,服务按照业务模块进行边界划分和命名是一项最佳实践。


    FrontEnd服务器有时候也可以认为是一种Portal(门户)机制,即把客户端所需要的各种CSS、Javascript等公共资源统一放在FrontEnd服务器,然后每个微服务包含自身特有的HTML等客户端代码片段以及业务逻辑,通过集成FrontEnd服务器上的公共资源完成独立服务的运行。


    当微服务数量较多且客户端集成场景比较复杂时,通常就需要单独抽取一层作为客户端访问的统一入口,这一层在微服务架构里称为API网关(Gateway)。API网关的主要作用是对后端的各个微服务进行整合,从而为不同的客户端提供定制化的内容。


    (4)外部集成

    这里把外部集成单独剥离出来的原因在于现实中很多服务之间的集成需求来自于与外部服务的依赖和整合,而在集成方式上也可以综合采用接口集成、数据集成和客户端集成。


    以上集成方式各有其应用场景和特点,现实中的很多系统包含的集成方式并不限于其中一种。关于服务拆分和服务集成的方法论与工程实践不是本书的重点,读者可参看笔者的《微服务设计原理与架构》[1]一书做进一步了解。在本书中,我们重点介绍的接口集成,并试图通过响应式编程的方式实现基于RESTful风格以及消息通信的微服务集成需求。


    微服务架构的核心组件


    微服务架构的实现首先需要提供一系列基础组件,包括事件驱动、集群与负载均衡、服务路由等分布式环境下的通用组件,也包括API网关和配置管理等微服务架构所特有的功能组件。同时,基于服务注册中心的服务发布和订阅机制是微服务体系下实现服务治理的基本手段。而关于如何保证服务的可靠性,我们也需要考虑服务容错、服务隔离、服务限流和服务降级等需求和实现方案。最后,我们也需要使用服务监控手段来管理服务质量和运行时状态。


    1.事件驱动

    事件驱动架构(Event-Driven Architecture,EDA)定义了一个设计和实现应用系统的架构风格,在这个架构风格里事件可传输于松散耦合的组件和服务之间。事件处理架构的优势就在于当系统中需要添加另一个业务逻辑来完成整个流程时,只需要对处于该流程中的事件添加一个订阅者即可,不需要对原有系统做大量修改。考虑到在微服务架构中服务数量较多且不可避免地需要对服务进行重构,事件处理在系统扩展性上的优势就尤为明显。而在技术实现上,通过消息通信机制,我们不必花费太大代价就能实现事件驱动架构。响应式编程从一定程度上也是事件驱动架构的一种表现形式。


    2.负载均衡

    集群(Cluster)指的是将几台服务器集中在一起实现同一业务。而负载均衡(Load Balance)就是将请求分摊到位于集群中的多个服务器上进行执行。负载均衡根据服务器地址列表所存放的位置可以分成两大类,一类是服务器端负载均衡,另一类是客户端负载均衡。另一方面,以各种负载均衡算法为基础的分发策略决定了负载均衡的效果。在集群化环境中,当客户端请求到达集群,如何确定由某一台服务器进行请求响应就是服务路由(Routing)问题。从这个角度讲,负载均衡也是一种路由方案,但是负载均衡的出发点是提供服务分发而不是解决路由问题,常见的静态、动态负载均衡算法也无法实现精细化的路由管理。服务路由的管理可以归为几个大类,包括直接路由、间接路由和路由规则[1]


    3.API网关

    API网关本质上就是一种外观模式(FaçadePattren)的具体实现,它是一种服务器端应用程序并作为系统访问的唯一入口。API网关封装了系统内部架构,为每个客户端提供一个定制的API。同时,它可能还具有身份验证、监控、缓存、请求管理、静态响应处理等功能。在微服务架构中,API网关的核心要点是所有的客户端和消费端都通过统一的网关接入微服务,在网关层处理通用的非业务功能。


    4.配置中心

    在微服务架构中,一般都需要引入配置中心(Configuration Center)的相关工具。采用配置中心也就意味着采用集中式配置管理的设计思想。对于集中式配置中心而言,开发、测试和生产等不同的环境配置信息保存在统一存储媒介中,这是一个维度。而另一个维度就是分布式集群环境,需要确保集群中同一类服务的所有服务器保存同一份配置文件,并且能够同步更新。


    5.服务治理

    在微服务架构中,服务治理(Service Governance)可以说是最关键的一个要素,因为各个微服务需要通过服务治理实现自动化的服务注册(Registration)和发现(Discovery)。服务治理的需求来自服务的数量。如果在服务数量并不是太多的场景下,服务消费者获取服务提供者地址的基本思路是通过配置中心,当服务的消费者需要调用某个服务时,基于配置中心中存储的目标服务的具体地址构建链路完成调用。但当服务数量较多时,为了实现微服务架构中的服务注册和发现,通常都需要构建一个独立的媒介来管理服务的实例,这个媒介一般被称为服务注册中心(Service Registration Center)。


    另一方面,服务提供者和服务消费者都相当于服务注册中心的客户端应用程序。在系统运行时,服务提供者的注册中心客户端程序会向注册中心注册自身提供的服务,而服务消费者的注册中心客户端程序则从注册中心查询当前订阅的服务信息并周期性地刷新服务状态。同时,为了提高服务路由的效率和容错性,服务消费者可以配备缓存机制以加速服务路由。更重要的是,当服务注册中心不可用时,服务消费者可以利用本地缓存路由实现对现有服务的可靠调用。


    6.服务可靠

    在微服务架构中,各个服务独立部署且服务与服务之间存在相互依赖关系。和单块系统相比,微服务架构中出现服务访问失败的原因和场景非常复杂,这就需要我们从服务可靠性的角度出发对服务自身以及服务与服务之间的交互过程进行设计。


    针对服务失败,常见的应对策略包括超时(Timeout)和重试(Retry)机制。超时机制指的是调用服务的操作可以配置为执行超时,如果服务未能在这个时间内响应,将回复一个失败消息。同时,为了降低网络瞬态异常所造成的网络通信问题,可以使用重试机制。这两种方式都会产生同步等待,因此合理限制超时时间和重试次数是一般的做法。


    当服务运行在一个集群中,出现通信链路故障、服务端超时以及业务异常等场景都会导致服务调用失败。容错(Fault Tolerance)机制的基本思想是冗余和重试,即当一个服务器出现问题时不妨试试其他服务器。集群的建立已经满足冗余的条件,而围绕如何进行重试就产生了Failover、Failback等几种常见的集群容错策略。


    服务隔离(Isolation)包括一些常见的隔离思路以及特定的隔离实现技术框架。所谓隔离,本质上是对系统或资源进行分割,从而实现当系统发生故障时能限定传播范围和影响范围,即发生故障后只有出问题的服务不可用,保证其他服务仍然可用。常见的隔离措施包括线程隔离、进程隔离、集群隔离、机房隔离和读写隔离等[5]


    关于服务可靠性,还有一个重要的概念称为服务熔断(Circuit Breaker)。服务熔断类似现实世界中的“保险丝”,当某个异常条件被触发时,就直接熔断整个服务,并不是一直等到此服务超时。而服务降级就是当某个服务熔断之后,服务端准备一个本地的回退(Fallback)方法,该方法返回一个默认值。


    7.服务监控

    我们知道在传统的单块系统中,所有的代码都在同一台服务器上,如果服务运行时出现错误和异常,我们只要关注一台服务器就可以快速定位和处理问题。但在微服务架构中,事情显然没有那么简单。微服务架构的本质也是一种分布式架构,微服务架构的特点决定了各个服务部署在分布式环境中。各个微服务独立部署和运行,彼此通过网络交互,而且都是无状态的服务,一个客户端请求可能需要经过很多个微服务的处理和传递才能完成业务逻辑。在这种场景下,我们首先面临的一个核心问题是如何管理服务之间的调用关系;另一方面,如何跟踪业务流的处理顺序和结果也是服务监控的核心问题。通常,我们需要借助于日志聚合和服务跟踪技术来解决这两个核心问题。


    微服务架构技术体系


    本书的定位是讨论响应式微服务架构构建过程中的工程实践。无论是实现响应式微服务架构还是传统的微服务架构,都需要借助于某一种具体的技术体系。


    为了实现微服务架构,首先需要选择一种主流的工具来构建单个微服务。当系统中存在多个微服务时,我们就应该提供服务治理、负载均衡、服务容错、API网关、配置中心、事件驱动等实现方案以完成服务之间的交互和集成。同时,微服务架构的技术体系也包括如何对微服务进行测试,以及基于日志聚合和服务跟踪的服务监控管理。


    1.微服务核心组件的实现技术

    微服务之间首先需要进行通信。对于服务通信,微服务架构明确要求服务之间通过跨进程的远程调用方式进行通信。关于远程调用,有三种风格的解决方案,即RPC、REST和自定义实现。而在服务与服务的交互方式上,也存在两个维度,即按照交互对象的数量分为一对一和一对多,以及按照请求响应的方式分为同步和异步。目前RPC框架可供选型的余地很大,如AlibabaDubbo、Facebook Thrift以及Google gRPC等都是非常主流的实现,而基于REST的实现框架则包括Jersey、Spring MVC以及本书中将要详细介绍的响应式Spring WebFlux等。


    事件驱动架构实现工具的表现形式通常是各种消息中间件,如基于JMS(Java Message Service,Java消息服务)规范的ActiveMQ、基AMQP(Advanced Message Queuing Protocol,高级消息队列协议)规范的RabbitMQ、在大数据流式计算领域中应用非常广泛的Kafka,当然还有像Alibaba自研的RocketMQ。在这些消息中间件中,ActiveMQ一般不考虑,如果是相对比较轻量级的应用,则可以选择RabbitMQ,而Kafka和RocketMQ则适合大型应用的场景。


    负载均衡分为服务器端负载均衡和客户端负载均衡两大类实现方案。在服务器软件中,我们可以选择Nginx、HA proxy、Apache、LVS等工具。而类似Netflix Ribbon的工具则是一种单独可以使用的负载均衡机制。事实上,所有的分布式服务框架几乎都内置了负载均衡的实现,所以负载均衡本身并不需要做太多的选择。


    API网关是微服务架构的核心组件之一。Netflix OSS(Open Source Software)中有一个Zuul提供了一套过滤器机制,可以很好地支持签名校验、登录校验等前置过滤功能处理,同时它也维护了路由规则和服务实例,以便完成服务路由功能。其他可供参考的API网关还有开源的Spring Cloud Gateway和Kong等。


    配置管理的作用是完成集中式的配置信息管理。目前比较主流的包括Spring旗下的Spring Cloud Config、淘宝的Diamond和百度的Disconf等。在实现上,Spring Cloud Config支持将配置信息存放在配置服务本地的内存中,也支持放在远程Git仓库中,这点与其他工具在设计上有较大不同。Diamond和Disconf都是基于Mysql作为存储媒介,Diamond采用拉模型,即每隔15s拉一次全量数据;而Disconf基于Zookeeper的推模型,实时推送。在配置数据模型上,Diamond只支持Key-Value结构的数据,采用的是非配置文件模式;而Disconf支持传统的配置文件模式,也支持Key-Value结构数据。


    关于服务注册和服务发现,比较常见的分布式一致性协议是Paxos协议[6]和Raft协议[7]。相比Paxos协议,Raft 协议易于理解和实现,因此,最新的分布式一致性方案大都选择Raft协议。我们知道Zookeeper 采用的是基于Paxos协议改进的ZAB(Zookeeper Atomic Broadcast,Zookeeper原子消息广播)协议,而 Raft 协议的实现工具主要是Consul 和Etcd。注册中心是任何一个微服务框架所必不可少的组件,很多框架都内建了对服务注册中心的支持。例如,Alibaba的Dubbo框架支持包括Zookeeper、Redis等在内的多种注册中心实现,默认采用的是Zookeeper;新浪的Motan支持Zookeeper,也支持Consul;Spring Cloud也同时提供了Spring Cloud Consul和Spring Cloud Zookeeper两种实现方案;而Netflix OSS中使用的是Eureka。


    服务可靠性相关的功能主要包括服务容错、服务隔离、服务限流和服务降级,其中大多数机制都偏向于实现策略而不是实现工具。我们需要明确的是如何实现服务隔离和服务熔断机制的框架。服务熔断器可选的开源方案包括NetflixHystrix和Resilience4j。


    2.Spring Cloud

    在本书中,我们将采用Spring Cloud作为微服务架构的实现框架。组件完备性是我们选择该框架的主要原因。Spring Cloud是Spring家族中新的一员,重点打造面向服务化的功能组件,在功能上服务注册中心、API网关、服务熔断器、分布式配置中心和服务监控等组件都能在Spring Cloud中找到对应的实现。


    另一个选择Spring Cloud的原因在于服务之间的交互方式。我们知道微服务架构中推崇基于HTTP协议的RESTful风格实现服务间通信,而诸如Dubbo等框架的服务调用基于长连接的RPC实现。采用RPC方式会导致服务提供方与调用方接口产生较强依赖,而且服务对技术敏感,无法做到完全通用。Spring Cloud采用的就是RESTful风格,这方面更加符合微服务架构的设计理念。


    Spring Cloud还具备一个天生的优势,因为它是 Spring 家族中的一员,而Spring在开发领域的强大地位给Spring Cloud起到了很好的推动作用。同时,Spring Cloud基于Spring Boot,而Spring Boot目前已经在越来越多的公司得到应用和推广,用来简化Spring应用的框架搭建以及开发过程。Spring Cloud也继承了Spring Boot 简单配置、快速开发、轻松部署的特点,让原本复杂的开发工作变得相对容易上手。


    在本书后续章节中,我们将看到如何使用Spring Cloud实现微服务架构中的各个核心组件。


    构建响应式微服务架构


    使用微服务架构最关键的一个原则就是将系统划分成一个个相互隔离、无依赖的微服务,这些微服务通过定义良好的协议进行通信。本节将讨论构建响应式微服务架构的一些设计原则和理念,并探讨整合响应式编程和微服务架构的方法。


    响应式微服务架构设计原则

    Reactive Microservices Architecture[8]一书讲述了响应式微服务架构的核心概念以及实施过程中的一些最佳实践。本节将介绍这些核心概念和最佳实践,以便读者能够更好地理解响应式微服务架构。


    1.隔离一切事物

    在微服务架构中,我们经常会提到雪崩效应(Avalanche Effect)这一概念。服务雪崩的产生是一种扩散效应。当系统中存在两个服务A和B,如果A服务出现问题,而B服务会通过用户不断提交服务请求等手工重试或代码逻辑自动重试等手段进一步加大对A服务的访问流量。因为B服务使用同步调用,会产生大量的等待线程占用系统资源。一旦线程资源被耗尽,B服务提供的服务本身也将处于不可用状态,整个过程的演变可参考图1-5。而这一效果在整个服务访问链路上进行扩散,就形成了雪崩效应。


    雪崩效应的预防需要依赖于架构设计中的一种称为舱壁隔离(Bulkhead Isolation)的架构模式。所谓舱壁隔离,顾名思义就是像舱壁一样对资源或失败单元进行隔离,如果一个船舱破了进水,只损失一个船舱,其他船舱可以不受影响。舱壁隔离模式在微服务架构中的应用就是各种服务隔离思想。

     

    640?wx_fmt=jpeg

    图1-5雪崩效应示意图

    隔离是微服务架构中最重要的特性,也是实现响应式宣言中所提倡的弹性、可伸缩系统的前提。所谓弹性,就是从失败中恢复的能力,依赖于这种舱壁和失败隔离的设计,并且需要打破同步通信机制。由此,微服务一般是在边界之间使用异步消息传输,从而使得正常的业务逻辑避免对捕获错误、错误处理的依赖。


    2.自主行动

    上面所讲的隔离性是自主性的前提。只有当服务之间是完全隔离的,才可能实现完全的自主,包括独立的决策、独立的行动以及与其他服务协调合作来解决问题。


    从实现上讲,服务自主性仅仅需要确保其对外公布协议的正确性即可。自主性不仅能够让我们更好地了解微服务系统以及各个服务的领域模型,也能够在面对冲突和失败状况时,确保快速定位到问题出在具体的哪一个微服务中。


    3.只做一件事,并且尽量做好

    大家都知道面向对象设计中有一条单一职责原则(Single Responsibility Principle,SRP),而在微服务架构中一个很大的问题是如何正确地划分各个服务的大小。多大的粒度才能被称之为“微”服务,显然,这个“微”是和服务本身的职责有直接关系,我们希望一个服务只做一件事,而且在服务内部要把相关的功能做到尽量好。


    每一个服务都应该只有一个存在的原因,业务和职责不应该糅杂在一起。如果满足这个要求,所有的服务组织在一起从整体上就能够便于扩展、具有弹性、易理解和维护。


    4.拥有自己的私有状态

    在软件设计领域经常会提到状态(State)这个词,而服务之间的状态本质上体现的还是一种数据关系。如果一个数据需要在多个服务之间共享才能完成一项业务功能,那么这项业务功能就被称为有状态。基于这项业务功能所设计和实现的一系列服务之间就形成了一种状态性,这一系列服务就是有状态服务。


    很多服务都会把自己的状态下沉到一个庞大的共享数据库中,这也是一些传统Web框架的做法。这种做法就会造成在扩展性、可用性以及数据集成上很难做好把控。而在本质上,一个使用共享数据库的微服务架构本质还是一个单体应用。一个服务既然具有单一职责,那么合理的方式就应该是该服务拥有自己的状态和持久化机制,建模成一个边界上下文。这里就需要充分应用领域驱动设计(Domain Driven Design,DDD)中的相关策略设计和技术设计方面的方法和工程实践。关于领域驱动设计以及背后的Event Sourcing(事件溯源)和CQRS(Command Query Responsibility Segreation,命令查询职责分离)等概念,读者可参考《实现领域驱动设计》[9],这里不做具体展开。


    5.拥抱异步消息传递

    从软件设计上讲存在三种不同层级的耦合度,即技术耦合、空间耦合和时间耦合。技术耦合度表现在服务提供者与服务消费者之间需要使用同一种技术实现方式。如图1-6a中服务提供者与服务消费者都使用RMI(Remote Method Invocation)作为通信的基本技术,而RMI是Java领域特有的技术,也就意味着其他服务消费者想要使用该服务也只能采用Java作为它的基本开发语言;空间耦合度指的是服务提供者与服务消费者都需要使用统一的方法签名才能相互协作,图1-6b中的getUserById(id)方法名称和参数的定义就是这种耦合的具体体现;而时间耦合度则表现在服务提供者与服务消费者只有同时在线才能完成一个完整的服务调用过程,如果出现图1-6c中所示的服务提供者不可用的情况,显然,服务消费者调用该服务就会发生失败。

     

    640?wx_fmt=jpeg

    图1-6耦合度的三种表现形式

    微服务之间通信的最佳机制就是异步消息传递,消息传递能够从技术、空间和时间等多个维度上缓解甚至消除图1-6中的三种耦合度。我们在第5章中会进一步对该话题展开讨论。

    同时,异步非阻塞执行是对资源的高效操作,能够最小化访问共享资源时的阻塞消耗,从而提升整体系统的性能。


    6.保持移动,但可寻址

    异步消息传递带来了服务的位置透明性。所谓位置透明,指的是在多核或者多结点上的微服务在运行时无须改变结点,即可动态扩展的能力。这也决定了系统的弹性和移动性,要实现这些需要依赖云计算带来的一些特性和按需使用的模型。


    另一方面,可寻址则是指服务的地址需要稳定,从而可以无限地引用此服务,无论服务目前是否可以被定位到。当服务在运行中、已停止、被挂起、升级中、已崩溃等情形下,地址都应该是可用的,任意客户端能够随时发送消息给一个地址。从这个角度讲,地址应该是虚拟的,可以代表一组实例提供的服务。使用虚拟地址能够让服务消费方无须关心服务目前是如何配置操作的,只要知道地址即可。


    整合响应式编程与微服务架构

    构建一个分布式系统是复杂而困难的一项工作,微服务架构基于分布式,同时又需要考虑弹性、可伸缩性、隔离性等一系列问题。作为一个微服务架构,服务与服务之间、服务与外部系统之间的通信都是必需的。当我们对被依赖的服务和外部系统无法把控时,就会有很大的失败风险。因此,即使双方之间的通信协议定义得再好,也不能信赖外部服务或系统,需要做好各种措施以保证自身服务的安全。这里我们就可以充分整合响应式编程和微服务架构来实现这一目标。


    响应式编程和微服务架构的一个整合点在于我们可以采用响应式编程中的背压(Backpressure)机制来实现数据流处理速度的一致性。在背压机制下,接收方根据自己的接受状况调节接受速率,通过反向的响应来控制发送方的发送速率,以防止一个系统中快速生成数据的部分压垮处理数据较慢的部分。目前,越来越多的工具和框架都在开始拥抱响应式流(Reactive Streams)规范,这些技术使用异步背压实时流来桥接系统,从而在总体上提高系统的可靠性、性能以及互操作性。关于背压和响应式流的具体概念和实现方法,将在下一章具体展开讨论。


    在微服务架构的通信模式上,要尽量避免使用同步通信机制,否则就把自身服务的可用性放在了所依赖的第三方服务的控制范围中。上一节中对雪崩效应的产生原因分析已经非常明确地说明了这一点。避免级联失败需要服务足够解耦和隔离,使用异步通信机制是一个最佳的方案。当然,传统的RESTful风格的服务调用仍然适用于可控的服务调用上。本书也会分别介绍响应式编程环境下RESTful风格和异步通信风格的服务通信模式及实现方法。


    另一方面,整个微服务架构需要的是一种全栈式的响应式环境,即响应式微服务开发方式的有效性取决于在整个请求链路中采用了全栈的响应式编程模型。如果某一个环节或步骤不是响应式的,就会出现同步阻塞,从而导致背压机制无法生效。常见的同步阻塞产生的环节除了服务与服务之间的同步通信,还有基于关系型数据库的数据访问,因为传统的关系型数据库都是采用非响应式的数据访问机制。本书也会详细介绍如何使用响应式的数据访问组件实现全栈的响应式编程模型。

    本文节选自电子工业出版社《Spring响应式微服务:Spring Boot 2+Spring 5+Spring Cloud实战》第一章,由电子工业出版社博文视点授权。本书主要包含构建响应式微服务架构过程中所应具备的技术体系和工程实践。围绕响应式编程和微服务 架构的整合,讨论如何使用 Reactor 响应式编程框架、如何构建响应式 RESTful 服务、如何构建响应式数据访问组件、如何构建响应式消息通信组件、如何构建响应式微服务架构,以及如何测试响应式微服务 架构等核心主题,并基于这些核心主题给出具体的案例分析。

    640?wx_fmt=jpeg

    基于篇幅的考虑,部分内容进行了简化,想了解本书全部详细内容,可以点击阅读原文直接购买。

    本次活动我们采取文章留言送书的活动。在本周末前,留言点赞数最高的前 5 名我们将免费赠送本书!图书由电子工业出版社博文视点提供。

    参考阅读:

    技术原创及架构实践文章,欢迎通过公众号菜单「联系我们」进行投稿。转载请注明来自高可用架构「ArchNotes」微信公众号及包含以下二维码。

    高可用架构

    改变互联网的构建方式

    640?wx_fmt=png

    长按二维码 关注「高可用架构」公众号

    展开全文
  • Airbnb开源框架,真响应式架构——MvRx Airbnb开源框架,真响应式架构——MvRx- https://mp.weixin.qq.com/s/pttfFzkAO5MnwscXDX-dDw Airbnb开源框架,真响应式架构——MvRx: https://github.com/airbnb/MvRx ...

    > Airbnb开源框架,真响应式架构——MvRx

    Airbnb开源框架,真响应式架构——MvRx- https://mp.weixin.qq.com/s/pttfFzkAO5MnwscXDX-dDw
    Airbnb开源框架,真响应式架构——MvRx: https://github.com/airbnb/MvRx
      MvRx 有四个重要的概念,分别是 State、ViewModel、View 和 Async。
    有了 Epoxy 的帮助,绝大部分界面都可以放入RecyclerView 中。对于不适宜使用 RecyclerView 的界面,或者 RecyclerView 之外的一些界面元素,MvRx 至少也提供了与 Android Architecture Components 相似的能力,并且其与 RxJava 的结合更加的友好。
      这个库综合运用了以下几种技术:Kotlin (MvRx is Kotlin first and Kotlin only);Android Architecture Components;RxJava;React (概念上的);Epoxy (可选但推荐);

     MVP 模式在 Android 界一直很流行,因为它比较好理解。其核心思想是,通过接口隔离数据与显示,数据的变动通过接口回调的方式去通知界面更新。这正是典型的命令式 M-V(数据-显示)链接。在这种模式下 View 层是完全被动的,完全受控于 Presenter 层的命令。这种模式并没有什么大问题,只是有一些不太方便之处,主要体现在 M-V 的紧密链接,导致复用比较困难,要么 View 层需要定义不必要的接口(这样 Presenter 可以复用),要么就需要为几乎每个 View 都定义一个对应的 Presenter,想想都心累。

    - MvRx 的真响应式 MVVM
    MvRx 构建的 MVVM 模式,完美地解决了上述的问题。MvRx 放弃了LiveData,使用State 来通知 View 层数据的改变(当然仍然是可感知生命周期的)。MvRx 可以方便地把RxJava Observable 的请求过程包装成 Ansyc 类,不仅可以改变 State 来通知 View 层,而且也包含了数据加载的状态(成功、失败、加载中等)。如果结合 Airbnb 的另一个开源库 Epoxy,那么几乎可以做到真正的响应式,即View层在数据改变时仅仅描述当前数据状态下界面的样子,Epoxy 可以帮我们实现与之前数据状态的比较,然后找出差别,仅更新那些有差别的 View 部分。这是对 MvRx 的大致描述。下面来看看 MvRx 是如果使用的。

    > Epoxy

    Epoxy——RecyclerView的绝佳助手- https://www.jianshu.com/p/369c08c29593
    Epoxy is an Android library for building complex screens in a RecyclerView- https://github.com/airbnb/epoxy

    展开全文
  • 本系列其他文章见:《响应式Spring的道法术器》。 前情提要:响应式编程 | 响应式流 1.5 响应式系统 1.5.1 响应式宣言 关注“响应式”的朋友不难搜索到关于“响应式宣言”的介绍,先上图: 这张图凝聚...
  • 响应式网站切图实操

    千人学习 2016-05-24 18:03:45
    本课程讲以互动的方式与学员共同打造一个网站,从创意开始,一直到整体页面被呈现出来。从原生的方式到主流的UI开发过程。一切从零开始,只要你有HTML5基础,通过22学时的努力,你将走向前端工程师的行列。
  • 介绍MVP这个架构一直是Android开发社区讨论的焦点,每个人都有自己的分析理解众说纷纭。直到GitHub上Google官方发布用MVP架构搭建的项目。感觉是时候分析了。MVP架构简介这不是本文重点,所以摘抄自李江东的博文MVP...
  • (2)响应式流——响应式Spring的道法术器

    万次阅读 多人点赞 2018-03-07 09:21:32
    本系列其他文章见:《响应式Spring的道法术器》。 前情提要: 什么是响应式编程 1.2 响应式流 上一节留了一个坑——为啥不用Java Stream来进行数据流的操作? 原因在于,若将其用于响应式编程中,是有局限性...
  • 什么叫响应式网站

    千次阅读 2014-06-07 21:52:54
    1、什么叫响应式网页设计  响应式Web设计(Responsive Web design)的理念是:页面的设计与开发应当根据用户行为以及设备环境(系统平台、屏幕尺寸、屏幕定向等)进行相应的响应和调整。具体的实践方式由多方面组成,...
  • 响应式编程规范

    千次阅读 2019-02-01 14:40:51
    文章目录响应式宣言反应式系统的特质reactive-streams 响应式宣言 响应式编程的概念已经很早就提出来了,业内很多大牛共同构建了 响应式宣言(中文版)。如果您认可该宣言,可以在其中签下自己的大名。 内容不多,...
  • Bootstrap介绍和Bootstrap各个版本之间的差异对比。...重点讲解栅格布局这一核心内容对实现响应式布局的支持,配合其他全局样式和组件及插件,让用户在实现响应式布局的同时,得到更加优雅的页面效果。
  • 响应式布局

    千次阅读 2016-09-19 14:34:30
    响应式布局是Ethan Marcotte在2010年5月份提出的一个概念,简而言之,就是一个网站能够兼容多个终端——而不是为每个终端做一个特定的版本。这个概念是为解决移动互联网浏览而诞生的。 响应式布局可以为不同终端的...
  • -(RACSignal *)urlResults { return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSError *error; ... encoding:NSUTF8StringEncoding error:&error];...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 144,024
精华内容 57,609
关键字:

响应式网站架构