使用 Node.js 构建 API Gateway [译]

译者说:大前端的概念被提出已经很久了,那么在大前端的背景下,我们前端开发人员使用 Node.js 应该做什么,不应该做什么呢?相信此文能够提供一定的参考。

背景

后台有着各种各样的服务,他们可能会使用不同的语言、数据库、协议及传输层。而客户端对数据也会有不同的要求,比如一个客户端可能需要 XML 格式而其他的需要 JSON。多少情况下,这些你都要支持。另一方面,不同的服务会有一些通用的共享逻辑,例如身份验证,我们不想在所有的服务中重复的去实现。于是就有了 API Gateway 的出现,它可以为不同的服务协议提供一个共享层,满足不同客户端的需求。

什么是 API Gateway?

API Gateway 是一种微服务架构中的一种服务,它可以为客户端与内部服务通信提供一个共享层与 API。API Gateway 能够路由请求、转换协议、数据聚合及实现共享的逻辑(如身份验证、流量限制)。

你可以把 API Gateway 当作微服务世界的入口。根据客户端的需求,我们的系统可以有一个或多个 API Gateway。例如对桌面端和移动端我们可以有不同的 gateway。

API Gateway for Desktop and Mobile

前端团队的 Node.js API Gateway

因为 API Gateway 的功能是提供给客户端的,所以它可以由负责前端应用的团队实现管理。这也意味着它的实现语言应该由负责客户端的团队选择。对应熟悉 JavaScript 的前端开发来说,Node.js 是很好的实现 API Gateway 的语言。

Netflix 成功的使用了 Node.js 的 API Gateway 与 Java 后端为多种客户端提供服务(The “Paved Road” PaaS for Microservices at Netflix)。

Node.js API Gateway with Java in Netflix

Paved PaaS to Microservices

API Gateway 的功能

前面我们讨论了可以把通用共享逻辑放入 API Gateway,接下来将介绍 gateway 常见的职责。

路由和版本控制

使用 API Gateway 作为微服务的入口时,在 gateway 服务中,你可以派发客户端的请求到不同的服务,通过路由控制版本。

进化设计

API Gateway 还可以帮你分解庞大的体系结构。在大多数情况下,从头开始重写系统作为微服务不是一个好主意,也不可能,因为我们需要在转换过程中为业务提供功能。

这种情况下,我们可以把代理或者 API Gateway 放在庞大的体系结构前,把新的功能实现为微服务。之后我们就可以分解庞大的体系结构,迁移存在的功能到新的服务。

通过进化设计,我们可以从庞大的体系结构平滑过渡到微服务。

身份验证

大多数微服务需要处理认证。将共享逻辑如身份验证添加到 API Gateway 可帮助您保持服务的小型化和领域化。

在微服务架构中,您可以通过网络配置将您的服务保护在 DMZ(demilitarized zone)中,并通过 API Gateway 将其展示给客户端。该 gateway 还可以支持多种身份验证方法。例如,您可以同时支持基于 cookie 和 token 的身份验证。

数据聚合

在微服务架构中,客户端可能需要不同聚合级别的数据,比如在各种微服务中进行数据实体的非规范化。在这种情况下,我们可以使用我们的API Gateway 来解决这些依赖关系并从多个服务收集数据。

序列化格式转换

可能发生的情况是,我们需要支持具有不同数据序列化格式要求的客户端。

假设我们的微服务使用 JSON,但我们的一个客户只能使用 XML。在这种情况下,我们可以将 JSON 转换为 XML 到API Gateway,而不是在所有微服务中实现。

协议转换

微服务架构允许传输多种协议,从中获得不同技术的好处。但是大多数客户端只支持一种协议。在这种情况下,我们需要为客户端转换协议。

API网关还可以处理客户端和微服务之间的协议转换。在下图中,您可以看到客户端期望通过 HTTP REST 进行所有的通信,而我们的内部微服务使用 gRPC 和 GraphQL。

流量控制与缓存

过于庞大的 API Gateway

实现 API Gateway 的时候,应该避免把非通用的逻辑(如特点域的数据转换)放入 API Gateway。

服务应始终对其数据域拥有完全所有权。建立一个过于庞大的 API Gateway 违背了微服务的理念。

这就是为什么你应该小心在你的 API Gateway 中的数据聚合 - 它可能是强大的,但也可能导致你应该避免的特定领域的数据转换或规则处理逻辑。

始终为您的 API Gateway 定义明确的责任,并且只在其中包含通用共享逻辑。

Node.js API Gateway

当你想要做简单的事情,例如将请求路由到特定的服务,您可以使用像 nginx 的反向代理。但是在某些时候,您可能需要实现一般代理不支持的逻辑。在这种情况下,您可以在 Node.js 中实现自己的 API Gateway。

在 Node.js 中,您可以使用 http-proxy 包简单地将请求代理到特定服务,或者使用功能更多的功能丰富的 express-gateway 来创建 API Gateway。

下面,我们在将在请求代理到用户服务之前对其进行身份验证。

const express = require('express')
const httpProxy = require('express-http-proxy')
const app = express()

const userServiceProxy = httpProxy('https://user-service')

// Authentication
app.use((req, res, next) => {
  // TODO: my authentication logic
  next()
})

// Proxy request
app.get('/users/:userId', (req, res, next) => {
  userServiceProxy(req, res, next)
})

除此之外可以在 API Gateway 中发出新请求,并将响应返回给客户端:

const express = require('express')
const request = require('request-promise-native')
const app = express()

// Resolve: GET /users/me
app.get('/users/me', async (req, res) => {
  const userId = req.session.userId
  const uri = `https://user-service/users/${userId}`
  const user = await request(uri)
  res.json(user)
})

总结

API Gateway 提供了一个共享层,以满足微服务架构的客户端的需求。它有助于保持您的服务小而专注于领域。您可以将不同的通用逻辑添加到 API Gateway,但您应该避免使用过于庞大的 API Gateway,因为它们会从服务获得控制权。