zhaoyu@home:~$

微服务设计模式-外部api

外部API设计的相关问题

一个微服务系统需要提供给外部调用的api,这些api可能有如下一些类型:

  • web应用,例如,erp后台管理系统等。
  • 运行在浏览器上的js应用。
  • 手机端:app、小程序等。
  • 第三方应用。

web应用通常运行在防火墙内,所以会在高宽带、低延迟的LAN上访问服务。其他运行在防护墙外,一般通过低宽带、高延迟的因特网或者移动 网络访问服务。

一种API策略是让客户端直接访问服务。是传统的整体应用调用的方式,但是这种在微服务中很少用,有如下缺点:

  • 微服务的api都是细粒度的,这就要求客户端发起多个请求获取他们需要的数据,这样做会降低请求效率,从而影响用户体验。
  • 没有封装导致客户端直接对接服务和API,这让服务的架构和API改变困难。
  • 服务间的通讯协议可能对客户端不友好。

移动端设计问题

  • 频繁的请求导致用户体验变差:因特网相比 LAN有着更高的延迟,通常要比LAN高100倍,移动网络更糟。频繁请求会导致请求时间更长。 此外, 更多的网络请求也会消耗电量, 让移动设备耗电过快。
  • 缺少封装让前端开发者和后端开发者同步修改代码:对于移动端,后台的改变可能会导致前端代码对应地改变,而发布新版本的审核流程 十分不便。
  • 服务间通信可能是客户端不友好的通信协议:如服务间可能使用的gRPC,这对移动端非常不友好。

其他类型客户端设计的问题

  • web应用的设计:web应用也可以使用浏览器不友好的协议访问服务,开发web应用的人员通常和开发后端服务的人员保持密切的协作。 所以web应用可以随着后台的变化而改变,所以web应用直接访问后端服务是可行的。

  • 面向浏览器js的应用:浏览器的js程序需要随着后端服务的改变而改变,并且js程序在调用细粒度的API时,也会存在高延迟的问题。

  • 第三方应用:微服务的组合api模式也会让第三方调用效率低下,但是更关键的是,第三方通常需要一个稳定的API,很难让第三方开发者去 升级到新版本的API。并且让后端开发人员长期保持API向前兼容对一个组织是一个很大的负担。

API网关模式

API网关是外部通向系统的进入点。它的责任是请求路由、API组合、身份认证等。API网关和设计模式中的facade(门面模式)类似。它封装了 应用的内部结构并提供API接口给客户端。它还拥有其他的功能,如鉴权,监控,限速等。其结构如下:

路由请求

api网关主要功能之一就是路由请求。当api网关接收到一个请求,API网关查询路由映射,将请求路由到指定服务。这个功能能和nginx等 web服务器提供的方向代理功能相同。

API组合

api网关也扮演了api组合的功能,如获取订单详情如下:

协议转换

如提供一个RESTful API给外部客户端,而内部使用gRPC。

API 为每个客户端提供特定的客户端特定API。

每种客户端需要的API不一样,如第三方客户端需要订单完整的信息,而移动端只需要返回订单简短信息。这时,最好的办法是为每个客户端 提供它自己的api。

实现边缘功能(edge)

一个应用可能有如下边缘功能:认证、授权、限流、缓存、请求统计、请求记录日志等。这些功能一般会有三个地方可以实现。第一,后端 服务。这些后端也可以做,但是在到达后端服务前处理更加安全。第二,在API网关之前建立一个独立edge 服务,好处是功能分离,缺点是 增加了网络延迟,并增加了应用的复杂性。第三种就是在API网关中实现edge服务。

一个API网关通常由两部分组成,API层和通用层。API层由多个独立的API模块组成,每个API模块映射一个特定的客户端。通用层实现共享 功能,如鉴权。如下图:

一些api直接将请求映射到服务,而另一些则使用api组合从多个服务中获取数据并合并结果。

API网关归属模式

一个重要的问题,是谁负责API网关的开发。一种方案就像SOA的ESB一样,由一个独立的团队去管理,如果一个移动端的开发者想要访问一个 接口,那么他需要向API网关组提交一个申请并等待API网管组提供API。这种中心化的组织拥有瓶颈,不适合微服务松耦合的团队结构。

一个更好的方案是由Netflix提出的,让每个客户端组拥有API模块,并发布API。而API网关组负责开发通用层和网关操作相关功能。如下图:

但是api网关的责任依然是模糊不清的,多个团队向代码库提供代码,API网关组负责它的管理和操作。这种模糊的划分和“谁创建,谁负责” 的原则违背。解决方案是一种Backends for frontends (BFF) 模式。每个客户端团队独立开发和管理API模块和独立通用模块, 如下图所示:

这种方式也有一个缺点,就是独立管理通用模块可能导致代码重复。理想情况下:所有的API网关应该使用共享的库,并由API网关组管理。

API网关的优缺点

  • api网关的优点:封装应用内部结构,减少网络延迟,并简化客户端代码。
  • 缺点:需要维护一个高可用的独立组件,带来开发、部署、管理成本。因此可能成为开发瓶颈。

API网关设计

设计一个api网关时,需要考虑如下几点:

性能和扩展

网关时应用的门户,所有的外部调用都要经过网关,一个影响性能和扩展的重要因素是API网关使用同步还是异步I/O。异步IO可以极大地提高 请求效率,JVM可以使用NIO的某种框架,如Netty或者Spring Reactor等。但是异步IO更加复杂,代码也更加难写,事件处理器必须快速返回 来避免线循环利用的程阻塞。

使用reactive流

api组合模式调用多个服务,等待返回并组装结果。通常每个服务的调用都是顺序执行的,这样的缺点是接口响应的总时间是每个服务响应的 事件总和。为了减少调用事件,服务调用可以并发执行。

传统的并发代码是使用回调,异步,事件驱动IO都是基于回调的扩展。可以通过ExecutorService.submitCallable()来实现。该方法返回 一个Future,是一个阻塞的API。一种更好的扩展方法是api组合器调用ExecutorService.submit (Runnable),为每一个Runnable设置一个 Callback方法,用于获取每个请求的返回结果。

传统的异步回调方法和容易导致问题,并且很难理解。尤其当并行和串行请求混合在一起时。更好的方法是使用一个reactive框架定义声明式 的API组合代码。JVMreactive抽象有如下例子:

  • java 8 CompletableFutures
  • Rxjava Observables
  • Scala Futures

API网关的实现

有多种方式实现API网关,如:

  • 现成的API网关服务:这种需要很少的开发或者不需要开发,但是不灵活。如它不支持api组合。
  • 使用一个API网关框架或者web框架自己开发:十分灵活,需要更多的开发工作。

现成的API网关服务

现成的API网关很多,如AWS API网关。还有基于NGINX HTTP服务器的Kong。和用golang编写的Traefik。

自建API网关

如果需要实现API组合功能,就需要自己开发API网关。构建一个API网关,有两个问题需要解决:

  • 定义一个路由规则的机制来简化代码。
  • 正确地实现HTTP代理功能,如如何处理HTTP headers等。

开发API网关通常需要使用对应的框架,减少大量的代码。我们先看Zuul,然后再看Spring Cloud GateWay。

Zuul

Zuul框架使用filter的概念,用一些列filter过滤http请求,调用后端服务,然后转化结果返回给客户端。尽管可以直接使用Zuul,但是使用 Spring Cloud Zuul更方便。Zuul有一个很大的限制是它只能处理基于路径的路由,如它不能将GET /ORDERS和POST /ORDERS路由到不同的服务。

Spirng Cloud GateWay

Spring Cloud GateWay是在Spring FrameWork 5、SpringBoot2、Spring WebFlux等框架的基础上构建的。它简答,并且易于理解,提供 如下功能:

  • 路由请求到后端服务。
  • 实现请求处理器,执行API组合。
  • 处理edge功能,如权限验证。

具体实现可以参考代码:

基于GraphQL

具体实现参考代码。