转载

Chris Richardson 微服务系列-阅读笔记

本文为阅读《Chris Richardson 微服务系列》的阅读笔记,具体原文参考: http://blog.daocloud.io/microservices-4/ , 里面有另外四篇的链接,当前daocloud已经更新到第5篇事件驱动架构。

第一篇 微服务架构的优势和不足

文中强调的单体应用的场景,我在前面很多谈组件化和服务化的文章里面已经都谈到过了,即一个应用系统里面的模块没有办法做到彻底解耦,如果要实现按组件单独部署是不可能的,相互之间仍然有大量内部不可见依赖而导致了模块间无法拆分。

那么单体应用本身带来的问题主要有哪些?

1.系统复杂: 内部多个模块紧耦合,关联依赖复杂,牵一发而动全身。

2.运维困难: 变更或升级的影响分析困难,任何一个小修改都可能导致单体应用整体运行出现故障。

3.无法扩展: 无法拆分部署,出现性能瓶颈后往往只能够增加服务器或增加集群节点,但是DB问题难解决

正是由于这些原因需要考虑引入微服务架构(实质仍然是单个应用本身的组件化和服务化),对于微服务文章里面有一个详细说明如下: 一个微服务一般完成某个特定的功能,比如订单管理、客户管理等。每个微服务都是一个微型应用,有着自己六边形架构,包括商业逻辑和各种接口。有的微服务通过暴露 API 被别的微服务或者应用客户端所用;有的微服务则通过网页 UI 实现。在运行时,每个实例通常是一个云虚拟机或者 Docker 容器。

从这个定义和说明仍然需要做一些关键理解,即在我前面谈微服务的文章里面谈到过的,即核心的几点包括了, 其一足够构成一个独立小应用(从DB到UI),其二微服务应用之间只能通过Service API进行交互,其三一般运行在云虚拟机或更轻的Docker容器上。

API Gateway,这实际上微服务架构里面的很重要的内容,其作用类似于传统企业内部的ESB服务总线,只是更加轻量和高性能来解决微服务的管控和治理问题。 而对于负载均衡,缓存,路由,访问控制,服务代理,监控,日志等都属于基本的服务管控内容 ,也是API Gateway需要考虑的核心能力。

Scale Cube的3D模型,描述的相当好,即通过微服务架构实施后扩展性的变化。

1. Y轴: 本质是应用的分解,即将传统的单体应用分解为多个微服务应用。

2. X轴: 水平弹性扩展能力,即通过负载均衡来实现水平弹性扩展,但是DB问题无法解决,引入3

3. Z轴: 当单个微服务应用引入了DB弹性扩展能力要解决的时候,我们引入了对数据库进行拆分和DaaS

对于微服务架构的好处前面在讲单体应用的问题的时候已经谈到了,微服务架构正好是解决这些问题。而对于微服务架构的不足,简单总结如下:

1. CAP原则: 由于服务无状态和引入了分布式,较难解决事务一致性问题。

2. 集成复杂: 任何彻底的分解都将带来集成的复杂度,即模块在集成时候需要外部微服务模块更多的配合。

3. 部署问题: 稍大项目都涉及到上100个服务节点部署,还涉及到部署后的配置,扩展和监控问题。


第二篇 使用API网关构建微服务

首先说下这篇文章的引入场景,以一个亚马逊购物网站的手机APP订单查看界面来举例,如果是一个单体应用,那么所有的界面需要获取信息都通过单体应用统一一个地址提供的多个Service API获取。但是转变为微服务架构后可以看到对于会员管理,商品管理,订单管理,财务结算管理等都已经拆分为了不同的微服务模块,需要从不同的服务提供地址调用不同的微服务模块提供的Service API来返回数据。

在原文里面我们看到对于客户端和微服务模块间点对点直接通讯提了三个问题,如下:

1. 问题一: 客户端需求和每个微服务暴露的细粒度 API 不匹配

2. 问题二: 部分服务使用的协议对 web 并不友好,如二进制RPC或AMQP消息等。

3. 问题三: 会使得微服务难以重构,如服务拆分或服务组合的场景。

那么我们从传统的ESB能力来对上面三个问题进行一个说明,第一个问题即可能涉及到细粒度的API组合,类似组合服务无法做;其二是可能存在协议转换的问题要解决;其三即服务透明的问题,即需要对客户端提供一个统一的服务目录以使底层服务透明。由于以上问题,引入了API服务网关的概念,再次强调, 对于API服务网关即使微服务架构里面的轻量服务总线,解决服务管控和治理相关问题。 文中对API Gateway给出如下说明:

API 网关是一个服务器,也可以说是进入系统的唯一节点。这与面向对象设计模式中的 Facade 模式很像。API 网关封装内部系统的架构,并且提供 API 给各个客户端。它还可能还具备授权、监控、负载均衡、缓存、请求分片和管理、静态响应处理等功能。

API 网关负责服务请求路由、组合及协议转换。客户端的所有请求都首先经过 API 网关,然后由它将请求路由到合适的微服务。API 网关经常会通过调用多个微服务并合并结果来处理一个请求。它可以在 web 协议(如 HTTP 与 WebSocket)与内部使用的非 web 友好协议之间转换。

API 网关还能为每个客户端提供一个定制的 API。通常,它会向移动客户端暴露一个粗粒度的 API。以产品详情的场景为例,API 网关可以提供一个端点(/productdetails?productid=xxx),使移动客户端可以通过一个请求获取所有的产品详情。API 网关通过调用各个服务(产品信息、推荐、评论等等)并合并结果来处理请求。

API网关的优点和缺点

对于API网关的优点,其实是类似传统ESB企业服务总线的优点,即实现服务透明,同时对于服务运行过程中的日志,安全,路由,缓存等问题进行统一配置和处理,而不需要每个微服务API实现时都去考虑。如开源的Dubbo服务总线即可以看作是一个API网关的实现。

API网关和ESB的一些重要区别点在于API网关更加轻量和高性能,它不需要去考虑太多遗留系统和诸多协议的适配,其次也不需要考虑服务集成过程中的大量数据转换和映射。同时为了提升服务网关的性能,一般API网关在实现过程中不会去记录详细的数据传输日志,或者类似Dubbo架构数据传输根本就不会通过API网关。


。客户端只需要同网关交互,而不必调用特定的服务。API 网关也有一些不足。它增加了一个我们必须开发、部署和维护的高可用组件。还有一个风险是,API 网关变成了开发瓶颈。

简单来说,在我们期望的去中心化和全分布式架构中,网关又成了一个中心点或瓶颈点,正是由于这个原因我们在网关设计的时候必须考虑即使API Gateway宕机也不要影响到服务的调用和运行。

API网关的设计和实现

对于大多数应用程序而言,API 网关的性能和可扩展性都非常重要。因此, 将 API 网关构建在一个支持异步、I/O 非阻塞的平台上是合理的。有多种不同的技术可以实现一个可扩展的 API 网关 。在 JVM 上,可以使用一种基于 NIO 的框架,比如 Netty、Vertx、Spring Reactor 或 JBoss Undertow 中的一种。一个非常流行的非 JVM 选项是 Node.js,它是一个基于 Chrome JavaScript 引擎构建的平台。

另一个方法是使用 NGINX Plus。NGINX Plus 提供了一个成熟的、可扩展的、高性能 web 服务器和一个易于部署的、可配置可编程的反向代理。NGINX Plus 可以管理身份验证、访问控制、负载均衡请求、缓存响应,并提供应用程序可感知的健康检查和监控。

对于API网关需要实现底层多个细粒度的API组合的场景,文章推荐采用响应式编程模型进行而不是传统的异步回调方法组合代码。其原因除了采用回调方式导致的代码混乱外,还有就是对于API组合本身可能存在并行或先后调用,对于采用回调方式往往很难控制。

基于微服务的应用程序是一个分布式系统,必须使用一种进程间通信机制。有两种类型的进程间通信机制可供选择。一种是使用异步的、基于消息传递的机制。有些实现使用诸如 JMS 或 AMQP 那样的消息代理,而其它的实现(如 Zeromq)则没有代理,服务间直接通信。另一种进程间通信类型是诸如 HTTP 或 Thrift 那样的同步机制。通常,一个系统会同时使用异步和同步两种类型。它甚至还可能使用同一类型的多种实现。总之,API 网关需要支持多种通信机制。

注:如果服务是同步调用可以看到微服务模块之间本身是没有彻底解耦的,即如果A依赖B提供的API,如果B提供的服务不可用将直接影响到A不可用。除非同步服务调用在API网关层或客户端做了相应的缓存。因此为了彻底解耦,在微服务调用上更建议选择异步方式进行。

对于大多数基于微服务的应用程序而言,实现 API 网关,将其作为系统的唯一入口很有必要。API 网关负责服务请求路由、组合及协议转换。它为每个应用程序客户端提供一个定制的 API。API 网关还可以通过返回缓存数据或默认数据屏蔽后端服务失败。

第三篇 微服务架构中的进程间通信

基于微服务的分布式应用是运行在多台机器上的;一般来说,每个服务实例都是一个进程。因此,如下图所示,服务之间的交互必须通过进程间通信(IPC)来实现。

对于微服务架构的交互模式,文章从两个维度进行了描述,即

一对一: 每个客户端请求有一个服务实例来响应。

一对多: 每个客户端请求有多个服务实例来响应。

同步模式: 客户端请求需要服务端即时响应,甚至可能由于等待而阻塞。

异步模式: 客户端请求不会阻塞进程,服务端的响应可以是非即时的。

对于分为这两个维度进行描述意义不太大,对于同步模式往往只能是1对1,而且还需要同步等待容易引起阻塞,而对于异步模块往往采用消息机制来实现,同时配合消息中间件可以进一步实现消息的发布订阅。 而对于EDA事件驱动架构要看到其本质也是伊布消息中间件和消息的发布订阅。

异步消息机制可以做到最大化的解耦,对于数据CUD的场景可以看到是比较容易通过异步消息机制实现的,但是会进一步引入事务一致性问题,即在采用异步消息机制后往往通过BASE事务最终一致性来解决事务层面的问题。而对于查询功能可以看到是比较难通过异步消息API实现的,在引入这个之前可以看到需要考虑两方面的问题并解决。

其一是服务网关需要有数据缓存能力,以解决无法从源端获取数据的场景。其二是前端开发框架本身需要支持异步调用和数据装载模式,特别是对于数据查询功能对于用户来讲,在前端的感受仍然需要时同步的。即通过异步方式返回了查询数据后可以动态刷新前端展示界面。

服务版本的问题 :这是不可避免要遇到的问题,特别是对于RestAPI调用,由于Json格式本身无Schema返回更加容易忽视了对服务版本的管理和控制。要知道对于Json数据格式变化仍然会导致RestAPI调用后处理失败。因此服务版本仍然采用大小版本管理机制比较好,对于小版本变更则直接对原有服务进行覆盖同时对所有受影响的服务消费端进行升级;而对于大版本升级则本质是新增加了一个新服务,而对于旧版本服务逐步迁移和替代。

处理局部失败 :文中提到了Netfilix的服务解决方案,对于失败问题的解决要注意常用的仍然是服务超时设置,断路器机制,流量控制,缓存数据或默认值返回等。不论采用哪种失败处理策略,都需要考虑应该尽量减少服务调用失败或超时对最终用户造成的影响。

基于请求/响应的同步 IPC

使用同步的、基于请求/响应的 IPC 机制的时候,客户端向服务端发送请求,服务端处理请求并返回响应。一些客户端会由于等待服务端响应而被阻塞,而另外一些客户端可能使用异步的、基于事件驱动的客户端代码,这些代码可能通过 Future 或者 Rx Observable 封装。然而,与使用消息机制不同,客户端需要响应及时返回。这个模式中有很多可选的协议, 但最常见的两个协议是 REST 和 Thrift。

Thrift 也能够让你选择传输协议,包括原始 TCP 和 HTTP。原始 TCP 比 HTTP 更高效,然而 HTTP 对于防火墙、浏览器和使用者来说更友好。

文中对于两种实现方式已经描述的相当详细,可以看到当前互联网OpenAPI平台和微服务架构实现中仍然是大量以采用Rest API接口为主。

而对于消息格式的选择,可以看到在使用RestAPI接口的时候,更多的是采用了Json消息格式而非XML,对于SOAP WebService则更多采用了XML消息格式。如果采用Thrift则还可以采用二进制消息格式以提升性能。

第四篇 服务发现的可行方案以及实践案例

首先还是先说场景,看似简单的服务注册和服务目录库管理为何会变复杂,其主要的原因还是在结合了云端PaaS和Docker容器部署后,对于微服务模块部署完成后提供出来的IP地址是动态在变化的,包括模块在进行动态集群扩展的时候也需要动态接入新的服务提供IP地址。正是由于这个原因引入了服务发现和管理的困难度。

在文章中提到了两种服务发现模式,即客户端发现模式和服务端发现模式,分开描述如下:

服务客户端发现模式

使用客户端发现模式时,客户端决定相应服务实例的网络位置,并且对请求实现负载均衡。客户端查询服务注册表,后者是一个可用服务实例的数据库;然后使用负载均衡算法从中选择一个实例,并发出请求。客户端从服务注册服务中查询,其中是所有可用服务实例的库。客户端使用负载均衡算法从多个服务实例中选择出一个,然后发出请求。

注:这是类似Dubbo实现机制一样的两阶段模式,即任何一个服务的消费都需要分两个步骤进行, 第一步首先是访问服务注册库(更多是API GateWay提供的一个能力)返回一个已经动态均衡后的服务可用地址,第二步即客户端和该地址直接建立连接进行服务消费和访问。

在这种模式的实现中有两个重点,其一是动态负载均衡算法,其二是服务网关需要能够对原始服务提供点进行实时的心跳检测以确定服务提供的可用性。

Netflix OSS 是客户端发现模式的绝佳范例。Netflix Eureka 是一个服务注册表,为服务实例注册管理和查询可用实例提供了 REST API 接口。Netflix Ribbon 是 IPC 客户端,与 Eureka 一起实现对请求的负载均衡。

缺点:底层的IP虽然动态提供出去了,但是最终仍然暴露给了服务消费方,再需要进一步做安全和防火墙隔离的场景下显然是不能满足要求的。

服务端发现模式

客户端通过负载均衡器向某个服务提出请求,负载均衡器查询服务注册表,并将请求转发到可用的服务实例。如同客户端发现,服务实例在服务注册表中注册或注销。在原文中有图示,基本看图就清楚了,即在服务注册库前新增加了一个Load Balancer节点。 注:这两个节点感觉是可以合并到API GateWay的能力中去的。

服务端发现模式兼具优缺点。它最大的优点是客户端无需关注发现的细节,只需要简单地向负载均衡器发送请求,这减少了编程语言框架需要完成的发现逻辑。并且如上文所述,某些部署环境免费提供这一功能。这种模式也有缺点。除非负载均衡器由部署环境提供,否则会成为一个需要配置和管理的高可用系统组件。


服务注册表需要高可用而且随时更新。客户端能够缓存从服务注册表中获取的网络地址,然而,这些信息最终会过时,客户端也就无法发现服务实例。因此,服务注册表会包含若干服务端,使用复制协议保持一致性。

首先可以看到服务注册表本身不能是单点,否则存在单点故障,当服务注册表有多台服务器的时候同时需要考虑服务注册库信息在多台机器上的实时同步和一致。我们操作和配置服务注册信息的时候往往只会在一个统一的服务管控端完成。

其次如果服务注册服务器宕机是否一定影响到服务本身的消费和调用,如果考虑更高的整体架构可用性,还可以设计对于服务注册库信息在客户端本地进行缓存,当服务注册表无法访问的时候可以临时读取本地缓存的服务注册库信息并发起服务访问请求。

对于服务注册表,文章提供了三种选择,感觉最常用的实现仍然是基于ZooKeeper进行的。

Etcd – 高可用、分布式、一致性的键值存储,用于共享配置和服务发现。

Consul – 发现和配置的服务,提供 API 实现客户端注册和发现服务。

Apache ZooKeeper – 被分布式应用广泛使用的高性能协调服务。

如前所述,服务实例必须在注册表中注册和注销。注册和注销有两种不同的方法。方法一是服务实例自己注册,也叫 自注册模式 (self-registration pattern);另一种是采用管理服务实例注册的其它系统组件,即 第三方注册模式。(原文有详细机制描述,不再累述)

虽然方法一把服务实例和服务注册表耦合,必须在每个编程语言和框架内实现注册代码。但是在自己实现完整微服务架构中,考虑到PaaS平台下微服务模块的动态部署和扩展,采用方法1相当来说更加容易实现。但是方法1仍然不能代替服务注册库本身应该具备的服务节点的心跳检测能力。

原文  http://blog.sina.com.cn/s/blog_493a84550102wbt0.html
正文到此结束
Loading...