上一节大概的讲述了一下微服务框架,这一节将讲到它的设计与实现。可能会涉及到一些Dubbo相关的东西,毕竟接触到的其中一种实现就是Dubbo+zookeeper
同样也是转自知乎:
---API网关的设计与实现
注意,我下面提到着这些内容,所涉及的java基础知识可能会是我后面需要补足的,比如NIO
对于大多数的应用程序而言,API网关的性能和可扩展性都非常重要。因此将API网关构建在一个支持异步、IO非阻塞的平台上是合理的。有多种不同的技术可以实现一个可扩展的API网关。在JVM上,可以使用一种基于NIO的框架,比如Netty、Vertx,Spring Reactor或JBoss Undertow中的一种。一个非常流行的非JVM是Node.js。
对于API网关需要实现底层多个细粒度的API组合场景,文章推荐使用响应式编程模型进行而不是传统的异步回调方法组合代码【比如ajax就是一种异步回调,因为它会导致代码混乱】由于API本身存在并行或先后调用,对于回调方法难以控制
基于微服务的应用系统本身是一个分布式的系统,因此必须有一种合理的进程通信机制,有两种可供选择,一种是异步的、基于消息传递机制的,实现诸如:JMS或AMQP;另一种是基于HTTP和Thrift的进程同步机制。通常一个系统会采用异步和同步两种机制,甚至同一种机制下的多种实现。
注意:为了更好地将微服务之间解耦合,希望能够保存一些服务结果的缓存,以免造成信息不可用的情况,另外更建议的是采用异步的通信方式。
总结:API网关作为系统的唯一入口,API网关负责服务请求路由、组合以及协议转换,它为每个应用客户端提供定制的API。API网关还可以通过返回缓存数据或默认数据屏蔽后端服务的失败
---微服务框架中进程间的通信
基于微服务的分布式应用是运行在多台服务器上的,一般来说每个服务实例都是一个进程,因此服务之间必须通过进程通信【IPC】来实现。
对于微服务框架的交互模式,文章从两个维度进行描述:
一对一:每个客户端请求由一个服务实例来响应。
一对多:每个客户端的请求由多个服务实例来响应。
同步模式:客户端请求需要服务器及时响应,甚至由于等待而阻塞
异步模式:客户端请求不会阻塞进程,服务端的响应式非及时的
基于异步模块往往采用消息机制来实现,同时配合消息中间件可以进一步实现消息的发布订阅。异步消息机制可以做到最大化解耦,对于数据CUD的场景可以看到是比较容易通过异步消息机制实现的,但是会进一步引入事务一致性问题,即在采用异步消息机制后往往通过BASE事务最终一致性来解决事务层面问题。
而对于查询功能可以看到的是比较难通过异步消息API实现,在引入这个之前,可能有两方面的问题需要考虑。
其一,服务网关需要有数据缓存能力,以解决无法从源端获取数据的场景。其二是,前端开发框架本身需要支持异步调用和数据装载模式,特别是数据查询功能对于用户来讲,在前端感受仍然是需要同步的,即通过异步的方式返回了查询数据动态刷新前端展示界面
---服务版本问题:
这是不可避免要遇到的问题,特别是对于RestAPI的,由于JSON本身格式无Schema返回,会更加忽视对服务版本的管理和控制。要知道对于JSON数据格式变化,会导致RestAPI调用后处理失败。因此服务版本仍然采用大小版本机制比较好,对于小版本变更,则直接对原有服务进行覆盖同时对所有受影响的服务消费端进行升级;而对大版本升级则本质是新增了一个服务,而对于旧版本服务逐步迁移和代替。
---处理局部失败
文中提到Netfilix的服务解决方案,对于失败的问题的解决要注意常用的仍然是服务超时设置,断路器机制,流量控制,缓存数据或默认值返回等。不论采用哪种失败处理策略,都需要考虑尽量减少服务调用失败或超时对最终用户的影响。
---基于请求/响应的同步IPC
使用同步的、基于请求/响应的IPC机制的时候,客户端向服务端发送请求,服务端处理请求并返回响应,一些客户端会由于等待服务端响应而被阻塞,而另外一些客户端可能使用异步的、基于事件驱动的客户端代码,这些代码可能通过Future或者Rx Observable封装。然而与使用消息机制不同,客户端需要响应及时返回,这个模式中有很多可选的协议,最常见的还是REST和Thrift。
在文章中提到了两种服务发现模式,即客户端发现模式和服务端发现模式,分开描述如下:
---客户端发现模式
使用客户端发现模式时,客户端决定相应服务实例的网络位置,并且对请求实现负载均衡。客户端查询服务注册表,后者是一个可用服务实例的数据库;然后使用负载均衡算法从中选择一个实例,并发出请求。
注:这是类似Dubbo实现机制一样的两阶段模式,任何一个服务的消费都需要分两个步骤进行。第一步,首先是访问服务注册库【更多的是API Gateway提供的一个能力】返回一个已经动态均衡过的服务可用地址;第二步客户端和该地址直接连接进行服务消费和访问。
这种模式的实现两个重点,一是动态负载均衡算法,其二是服务网关需要能够对原始服务提供点进行实时心跳检测以确定服务提供的可用性。
---服务端发现模式
客户端通过负载均衡器向某个服务提出请求,负载均衡器查询服务注册表,并将请求转发到可用的服务实例,如同客户端发现,服务实例在服务注册表中注册或注销。在服务注册器前新增一个Load Balance节点。
服务器发现模式兼具优缺点,它最大的优点是客户端无需注意细节,只需要简单地向负载均衡器发出请求,这就减少了编程语言框架需要的发现逻辑。某些部署环境免费提供这一功能
----服务注册表
服务注册表需要高可用而且随时更新。客户端能够缓存从服务注册表中获取的网络地址,然而,这些信息最终会过时,客户端也就无法发现服务实例,因此,服务注册表会包含若干服务端,使用复制协议保持一致性。
首先可以看到服务注册表本身不能是单点,否则会存在单点故障,当服务注册表有多台服务器的时候同时需要考虑服务注册库信息在多台机器上的实时同步和一致。我们操作和配置服务注册信息的时候,往往只会在一个统一的服务管控端完成。
如果注册服务器宕机是否一定会影响到客户端消费和调用,如果考虑更高的整体架构可用性,还可以设计对于服务注册库信息在客户端本地的缓存,当服务注册表无法访问的时候可以临时读取本地缓存的服务注册库信息并发起请求。
对于服务注册表,文章提供了3个选择,最常用的还是基于Zookeeper的。Dubbo也有集成广播的发现。
Zookeeper--分布式应用广泛使用的高性能协调服务
如前所述,服务实例必须在注册表中注册和销毁,注册和销毁有两种不同的方法。
1. 服务实例自己注册,自注册模式
2. 管理服务实例的其他组件,即第三方注册
虽然方法一把服务实例和服务注册表耦合,必须在每个编程语言和框架内实现注册代码,但是在自己实现完整微服务框架中,考虑到PaaS平台微服务模块的动态部署和扩展,采用方法一相对来说更加容易实现。