欢迎访问Spring Cloud中国社区

《重新定义Spring Cloud实战》由Spring Cloud中国社区倾力打造,基于Spring Cloud的Finchley.RELEASE版本,本书内容宽度足够广、深度足够深,而且立足于生产实践,直接从生产实践出发,包含大量生产实践的配置。欢迎加微信Software_King进群答疑,国内谁在使用Spring Cloud?欢迎登记

微服务与API Gateway

sluk3r · 5月前 · 2260 ·

微服务与API Gateway

译者注:

  1. 作为自己构建微服务知识思想体系里的一个基点,本文译自Building Microservices: Using an API Gateway。虽说原文发表于三年前,具体技术点上可能会有结落后, 不过本文使用一个具体的例子,从最原始的问题开始,最终引出API Gateway的解决方案,这种演进式的介绍方式对于初次接触这个技术点的小白来说, 还是很有启发意义的。
  2. Gateway并没有直接翻译成网关,主要是考虑到API Gateway语境下, 几乎没有网的概念。还是保留了原始的意义,从而可以更形象地表达其”通往API大门之路”的字面含义。

以下是译文。

在使用微服务思想来架构业务系统时, 需要考虑业务系统的客户端怎么与背后众多的微服务交互。在单体架构下,只有一个对接点(endpoint), 客户端的交互不成问题。不过, 微服务框架中,每一个微服务都对外提供一组业务对接点。本文中,我们将仔细探究下微服务提供多个对接点的方式, 对客户端与系统的交互方式有什么样的影响,基于这个认识,进而引出API gateway模型。

引子

本文中, 我们用一个电商系统作为例子,这个电商系统里需要开发一个原生的移动客户端。移动客户端里会有一个商品详情页。

下图显示了在Amazon的Android移动客户端里,下滑时看到的信息。

上面的场景虽说只是在一个普通手机里看到的,商品详情页展示了很多信息。 这里不仅展示了商品自身最基本的信息(如名称、描述和价格),同时还展示了下面这些信息:

  1. 购物车时已选商品的个数。
  2. 订单历史。
  3. 客户评论。
  4. 商品已经多库存警告。
  5. 快递可选项。
  6. 多个推荐信息,包括跟本商品一块购买的商品,同购买人还下单的其它商品,同购买人还浏览过的商品。
  7. 其它购买选项。

在单体架构情况下, 上面场景下, 移动客户端可以使用一个简单的REST请求就能获取到这些全部数据。REST请求经过负载均衡,打到集群里任一个实例上。后台系统接下来查询多个数据库表,取出数据后,返回给客户端。

与之相反,在微服务情况下, 上面同样业务场景下商品详情页里展示的数据会在多个微服务里保存。下面是刚才业务场景下一些微服务:

  1. 购物车服务,对应着购物车中已选商品的个数。
  2. 订单服务,对应订单历史。
  3. 商品类目服务。
  4. 客户评论服务,对应客户评论信息。
  5. 库存服务,对应着低库存告警。
  6. 快递服务,对应着快递可选项,最后投递时间以及费用,这些数据又过来从各物流公司的获取。
  7. 推荐服务,对应着被推荐的商品。

移动客户端该怎么与这样的一大坨服务交互呢?咱们按个捋下。

移动客户端跟微服务直接对话

从已有技术看,完全可以让这个移动客户端直接访问每一个微服务。每个微服务都有一个公共可访问的对接点,这个对接点可以是这样的格式https://<serviceName>.api.company.name。这样的URL背后是每个微服务自己的负载均衡,请求经由这个负载均衡分发到具体的实例上。这样,移动客户端可以遍历每一个微服务,最终取回需要的数据。

不过,这种对接方式实施起来很难,也有自身的局限性。一个典型问题是,客户端的需求跟微服务提供API的粒度不对等。本例中, 客户端需要发起7个独立的请求。复杂的场景中,这个请求数会更多。例如,Amazon自身的真实业务中,为了展示商品详情信息,背后会有上百个微服务,这样也就意味着会有上百个独立的请求对接。在LAN网络环境里,这样的多个独立的请求还算说的过去,不过在公网环境里这么多的网络访问就很不花算了,移动网络环境下,这么多的独立请求更是不可相像。另一方面,这样的对接方式,也会很大地提升客户端代码的复杂度。

这样直接访问微服务的另一个问题是,可能某些服务使用了Web环境支持的协议。某些服务可能使用了Thrift的字节RPC协议,而别的微服务选择了AMQP的消息协议。这样的协议都是浏览器或防火墙不能很好支持的,也只能是内网使用。

这种对接方式再一个弱点是,后续很难再对微服务重构。随着业务的发展,我们可能需要调整业务系统与服务的对应关系。如,可能会合并两个服务,也可能会拆分原来的一个服务。现在如果客户端直接跟背后的众多微服务对接的话, 这样的重构操作起来极为困难。

考虑到上面的各种问题,这样的直接对接方式几乎没有真正使用起来。

使用API Gateway

通常情况下,针对前面的业务场景,我们使用API Gateway。在实施层面上, API Gateway部署在独立服务器上。在设计理念上,API Gateway跟设计模式里的Façade很像。API gateway对外隐藏了内部系统,并对每一个客户端提供了定制的API。基于这样的对接,可以再加这样的职责,如认证和授权、监控、负载均衡、缓存、请求重整管理和静态响应处理。

API gateway在整个构架中的位置如下图所示。

API Gateway负责请求的路由、整合和协议转换。所有客户端上发出的请求都先经过API gateway,随后被路由到具体的微服务上。API gateway通常会把一个请求分解发放到多个微服务上,最后再整合多个微服务上返回的结果。它可以把企业内部使用的协议转换成公网可以访问的协议。

API gateway也可以为每一种客户端提供定制好的API。针对移动客户端,通常会有一个大而全的API。如上面浏览商品详情页的业务场景里,API gateway可以提供这样的对接点(/productdetails?productid=xxx),客户端使用这个API可以只用一个请求就能取出来所有的商品信息。API gateway接收到这个请求后, 再分解后发给相关的微服务(包括商品具体服务、推荐服务和用户评论服务等),等各个微服务处理完成后, 再整合后返回给客户端。

使用API gateway方面的一个典型例子是Netflix API Gateway。 Netflix公司流媒体服务可以上百种设备上观看,如电视、智能手机、游戏机、机顶盒等等。刚开始时,Netflix公司想用一个统一的API来适应这些所有的设备。不过,后来发现这招不行,设备太多需求多样很难满足。现在,Netflix在架构上调整,使用了API Gateway,最终为每一种设备提供定制的API, 现在Netflix的API gateway每天可以处理数十亿的请求。

API gateway的优缺点

跟任何措施一样, API Gateway自身也有优缺点。最大的好处是, 通过API Gateway, 可以很好地隐藏系统内部的结构。客户端不再需要调用N多个微服务,转而调用一个单一的API Gateway。API Gateway为每一种客户端提供针对性的API。这样的架构设置减少了客户端跟背后系统的网络交互次数,也简化了客户端的代码。

API Gateway也有一些缺点。针对API Gateway, 必须有一套高可用保护。API Gateway也会成为系统的性能瓶颈点。为了对外提供微服务对接点,API Gateway这边也需要更新。这样就要求更新API Gateway的过程必须尽可能地轻量,要不会出现API Gateway更新的排除问题。虽说有这些缺点,不过,针对大多数的应用场景,使API Gateway还很是有必要的。

API gateway实施中注意的几个问题

上面咱们回顾了API Gateway背后的驱动力和优缺点,接下来我们看下具体在实施时需要注意的几个设计问题。

性能和扩展性问题

虽说像Netflix一样每天处理数十亿请求的公司不多,不过对大多数的应用系统为说,API Gateway的性能和扩展性问题也很有必要提前考虑。据现有的经验看,常用的应对策略包括使用异步及非阻塞的I/O模型。在这个方向上有多种不一样的技术。JVM平台上,我们可以使用这样的基于NIO的框架,如Netty、Vertx、Spring Reactor或JBoss的Undertow。非JVM平台上, 可以考虑使用NodeJS,或NGINX Plus。NodeJS是基于Chrome的JavaScript引擎构建的一个平台。基于NGINX,可以方便地搭建成熟、可扩展、高性能的Web服务器和反向代理。NGINX Plus可以方便地提供这样的功能:管理论证和授权、访问控制、请求的负载均衡、结果的缓存、整个系统的健康检查和监控。

使用Reactive编程模型

API gateway的核心逻辑是, 把收到的请求分发到合适的后台服务上。 针对一些业务场景下的请求,API gateway调用多个后台服务,并归并后台服务返回的结果。这样的调用场景下, 分发给后台服务的请求会彼此独立。为了尽可能地降低响应时间, API gateway可以并行地执行这样的互相不依赖的请求。不过, 也有的请求之间相互有依赖。这些依赖会有隐式的, 如API Gateway在分发请求之前需要调用检权服务来验证是否有权限。业务中也有与之类似的场景, 如为了获取用户的购买心愿单信息,API Gateway需要先取得用户的基本信息。 另一个API组合的实例是Netflix Video Grid。

如果使用传统的异步回调方式来实现API Gateway的分解组合调用模式的话, 我们很快就掉到回调机制的坑儿(http://callbackhell.com/)里, 这样的代码错综复杂、难读易错。 实现API Gateway的一个更好方式是, 使用采用Reactive模型来声明式地实现逻辑。 常见的Reactive模型有Scala的Futures, Java 8的CompletableFutures, 和JavaScript的promises。 还有Microsoft的.Net平台最先开发出来的Reactive Extensions (Rx)。 Netflix公司也为API Gateway专门开发的基于JVM平台的RxJava。 使用Reactive模型,我们可以方便地设计出简易且高效的API Gateway代码。

服务调用

基于微服务的系统, 天生是分布式部署的, 这样也就只能使用进程间通信的机制。 关于进程间通信, 有两种实现方式。 一种是使用基于消息的异步式通信机制。在实现上有消息中间件(如JMS或AMQP)。 另一种是像ZeroMq一样, 不需要独立的消息中间件, 服务之间直接通信。 另一种进程间通信机制是同步调用,如使用HTTP或Thrift。 系统可以同时使用异步和同步进制, 具体在实现方式上也可以有多种。 这样从理论上来说, API Gateway需要支持多种通信进制。

服务发现

API Gateway需要知道它所对接服务的具体地址(Ip地址和端口号)。 在传统的应用中,我们可以使用硬编码的方式写死调用服务的地址。 不过, 在基于云的微服务服务新时代, 这个问题不能那么随便地确定了。 像消息中间件这样的基础设施通常有一个静态的地址, 咱们可以使用OS的环境变量指定。不过, 确定一个微服务的地址不是那么简单。 微服务的地址通常是动态赋值的。 另外,微服务的实例也会动态地调整,自动扩容或升级都会导致这样的动态调整。 随之而来为的问题,API Gateway像其它的服务的客户端一样, 都需要使用服务发现机制,具体的发现机制可以是Server-Side Discovery , 也可以是Client-Side Discovery。 后续的文章会专门详细地讨论服务发现机制,不过, 这里注意注意一点,如果系统决定使用基于客户端的服务发现机制的话, API Gateway得需要查询Service Registry,这里记录了所有微服务的实例及地址。

应对部分失败问题

在设计API Gateway时的另一个问题是调用多个服务时如何应对部分失败。 这个问题具体来说是这样的, 在分布式系统中, 被调用的系统可能响应很慢, 或者根本没有响应了, 这样在API Gateway设计时怎么应对。API Gateway肯定是不能无限期地阻塞, 死等被调用服务返回数据。 不过, 具体使用什么方式应对时, 要视具体的业务场景而定。 如在本文的实例中, 在获取商品详情时,推荐服务不能正常地返回结果时, API Gateway可以返回其它数据, 毕竟用户更关心其它数据。 这时, 推荐信息可以是空或者一个写死的Top 10列表。 相反,如果商品主数据服务不能访问了, API Gateway就只给给客户端返回一个明确的错误码了。

如果可能的话, API Gateway可以返回缓存的数据。如,商品价格很少调整,这时如果价格服务不可用情况下, API Gateway可以返回前面已经缓存好的数据。 数据可以缓存在API Gateway自身, 也可以缓存了独立的服务里, 如Redis或Memcached。这样, 通过返回一个默认写死的数据或缓存数据,API Gateway可以保证在部分服务不可用的情况下, 也不影响用户体验。

针对上面的问题, Netflix Hystrix是一个很好的框架。 当请求超过预定的时间值时, Hystrix会中止调用。 Hystrix也实现了熔断机制,在服务没有响应时, 它会中止无效的等待。 如果服务调用的失败率低于预先设定值时, Hystrix会激活熔断机制, 此时,所有的请求在一定时间内都直接失败。使用Hystrix, 在某个请求失败的情况下, 可以自定义一个回调实现, 利用此,我们可以使用缓存数据或返回一个默认写死的值。 如果API Gateway是基于JVM实现的话, 优先使用Hystrix。

总结

对于大多数基于微服务的系统来说, 架构上采用API Gateway很有必要,API Gateway在整个构架中扮演了单一入口的角色。API Gateway负责请求的路由、分解与组合,以及协议转换。 使用API Gateway, 可以给每一种客户端提供一个定制的API。 在后台服务不可用情况下, 使用API Gateway可以方便地返回缓存数据或默认写死的数据。 接下来的系列中, 我们将具体探究服务之间怎么调用的问题。


本文原始博客地址是微服务与API Gateway, 转载请标明。