使用 Node.js 构建 API Gateway [译]
译者说:大前端的概念被提出已经很久了,那么在大前端的背景下,我们前端开发人员使用 Node.js 应该做什么,不应该做什么呢?相信此文能够提供一定的参考。
背景
后台有着各种各样的服务,他们可能会使用不同的语言、数据库、协议及传输层。而客户端对数据也会有不同的要求,比如一个客户端可能需要 XML 格式而其他的需要 JSON。多少情况下,这些你都要支持。另一方面,不同的服务会有一些通用的共享逻辑,例如身份验证,我们不想在所有的服务中重复的去实现。于是就有了 API Gateway 的出现,它可以为不同的服务协议提供一个共享层,满足不同客户端的需求。
什么是 API Gateway?
API Gateway 是一种微服务架构中的一种服务,它可以为客户端与内部服务通信提供一个共享层与 API。API Gateway 能够路由请求、转换协议、数据聚合及实现共享的逻辑(如身份验证、流量限制)。
你可以把 API Gateway 当作微服务世界的入口。根据客户端的需求,我们的系统可以有一个或多个 API Gateway。例如对桌面端和移动端我们可以有不同的 gateway。
前端团队的 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)。
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,因为它们会从服务获得控制权。