API(应用程序编程接口)是一套规则和协议,允许两个应用服务相互通信。
随着应用程序规模的增长,API 的数量也会增加。如果没有合适的工具管理这些 API ,将会变得非常复杂。
这就是 API 网关 需要发挥作用的地方。
API 网关充当一个中央服务器,位于客户端(例如浏览器、移动应用)和后端服务之间。
客户端不再直接与多个微服务交互,而是将请求发送到 API 网关。网关处理这些请求,执行安全策略,并将它们转发到相应的微服务。
在本文中,我们将探讨为什么需要 API 网关?它提供的功能是如何工作的?
现代应用程序中的 API 网关
现代应用程序,特别是那些使用微服务架构的应用程序,由多个后端服务管理不同的功能。
例如,在一个电子商务系统中:
- 一个服务处理 用户账户。
- 一个服务处理 支付。
- 另一个服务管理 产品库存。
- 客户端需要知道所有后端服务的位置和详细信息。
- 开发人员需要为每个服务管理身份验证与安全。
- 客户端将所有请求发送到一个地方------API 网关。
- API 网关负责路由、身份验证、安全和其他操作任务,简化了客户端和后端的交互。
API 网关的关键功能
1. 身份验证和授权
API 网关确保只有授权用户才能访问后端服务。
- 身份验证: 使用令牌(例如 OAuth、JWT)、API 密钥或证书验证客户端的身份。
- 授权: 客户端访问特定服务或资源的权限。
API 网关保证了各个服务处理身份验证的需要,减少了冗余,保持了整个系统的访问的一致性。
2. 速率限制
为了防止滥用资源,大多数 API 网关实现了 速率限制。
- 控制客户端在给定时间内请求频率。
- 保护后端服务免受过多流量或潜在的拒绝服务(DoS)攻击的影响。
例如,一个公共 API 可能允许每个用户每分钟最多 100 个请求。如果客户端超过此限制,API 网关将阻止额外的请求,直到速率重置。
3. 负载均衡
高流量应用程序依靠 负载均衡 ,将传入的请求均匀分配到多个服务上。
- 将请求重定向到空闲的服务,同时避免那些宕机或过载的服务。
- 使用轮询、最少连接或加权分配等算法智能地管理流量。
4. 缓存
为了提高响应时间并减少对后端服务的压力,大多数 API 网关提供 缓存。
- 存储经常请求的数据,例如:
-
- 对常见端点的响应(例如产品目录或天气数据)。
- 静态资源,如图像或元数据。
缓存有助于减少延迟,提升用户体验,同时降低后端服务的运营成本。
5. 请求转换
在客户端和后端服务多样化的今天,请求转换 对于兼容性至关重要。
- 修改传入请求的格式来匹配后端服务的要求。
- 在将响应发送回客户端之前进行转换,确保它们符合客户端的期望。
例如,它可能将传统服务的 XML 响应转换为现代前端应用程序的 JSON。
6. 服务发现
现代系统通常包含动态扩展的微服务。
- API 网关的服务发现功能,动态识别每个请求适配后端服务。
- 确保了即使在服务频繁扩展或缩减的环境中,请求路由也能无缝进行。
7. 中断器
中断器是一种机制,当检测到持续的故障时,它会暂时停止向后端服务发送请求。
- 响应缓慢或超时。
- 服务器错误(例如 HTTP 500 状态码)。
- 服务的高延迟或不可用。
API 网关持续监控后端服务的健康状况和性能,并使用中断器阻止对故障服务的请求。
8. 监控和日志记录
API 网关提供了强大的 监控和日志记录 功能,跟踪和分析系统行为。
- 记录每个请求的详细信息,如来源、目的地和响应时间。
- 收集请求速率、错误速率和延迟等指标。
这些数据帮助系统管理员检测异常、排查问题并优化系统性能。许多 API 网关还与 Prometheus、Grafana 或 AWS CloudWatch 等监控工具集成。
示例:餐饮订阅应用
想象一下,你正在使用一个食品配送应用订购晚餐。当你点击"下单"时,你的手机会发出一个 API 请求。首先与 API 网关通信。
当你点击"下单"时,应用会向 API 网关 发送一个请求,要求其处理你的订单。
此请求包括以下内容:
- 你的用户 ID
- 选择的餐厅和菜单项
- 送货地址
- 支付方式
- 身份验证令牌
API 网关作为后端系统的唯一入口接收请求。
在转发请求之前,API 网关会验证请求以确保:
- 所需的参数或标头存在。
- 数据格式正确(例如 JSON)。
- 请求符合预期的结构或模式。
js
// 初始请求处理示例
app.post('/api/v1/orders', async (req, res) => {
// 检查请求是否包含所需标头
if (!req.headers['content-type'].includes('application/json')) {
return res.status(400).send('无效的内容类型');
}
// 继续处理...
});
如果任何信息缺失或不正确,网关会立即拒绝请求,并向应用发送适当的错误消息。
现在,网关验证你的身份和权限,以确保只有合法用户可以下单:
- 它将你的身份验证令牌(例如 OAuth 或 JWT)转发给身份提供商以确认你的身份。
- 它检查你的权限,确保你有权使用应用下单。
js
const authenticateRequest = async (req) => {
// 从标头中提取 JWT 令牌
const token = req.headers.authorization?.split(' ')[1];
// 验证令牌并获取用户详细信息
const user = await verifyToken(token);
// 检查用户是否有下单权限
return user.permissions.includes('place_orders');
};
如果身份验证或授权失败,API 网关会向应用发送 401 未授权
或 403 禁止
错误。
为了防止滥用,API 网关会检查你最近的请求数量。例如:
- 如果你在过去一分钟内发出了 10 个"下单"请求(可能是无意的),网关可能会暂时阻止额外请求,并返回
429 请求过多
响应。
js
const checkRateLimit = async (userId) => {
const key = `rate_limit:order:${userId}`;
const current = await redis.incr(key);
// 如果是窗口期内的第一个请求,设置过期时间
if (current === 1) {
await redis.expire(key, 60); // 1 分钟窗口
}
return current <= 10; // 每分钟允许 10 个订单请求
};
如果后端服务需要特定的数据格式或额外信息,API 网关会转换请求。
例如:
- 应用以纯文本形式发送送货地址,但配送服务期望 GPS 坐标。API 网关在转发请求之前将地址转换为坐标。
js
const transformRequest = async (originalRequest) => {
const address = originalRequest.deliveryAddress;
// 使用地理编码 API 将地址转换为 GPS 坐标
const coordinates = await getCoordinatesFromAddress(address);
if (!coordinates) {
throw new Error('获取 GPS 坐标失败');
}
// 为配送服务转换请求
return {
orderId: originalRequest.orderId,
customerName: originalRequest.customerName,
deliveryLocation: {
latitude: coordinates.lat,
longitude: coordinates.lng
},
deliveryInstructions: originalRequest.instructions || ""
};
};
现在,API 网关需要协调多个后端服务来处理你的订单。
使用 服务发现,它识别:
- 订单服务: 创建新的订单记录。
- 库存服务: 检查餐厅是否有你选择的物品。
- 支付服务: 处理你的支付。
- 配送服务: 为你的订单分配配送司机。
网关使用 负载均衡 算法动态将请求路由到这些服务,确保连接到可用且良好的实例。
js
const routeRequest = async (req, serviceType) => {
// 获取服务注册表
const services = await serviceDiscovery.getServices(serviceType);
// 选择实例
const targetService = selectServiceInstance(services);
// 转发请求
return await axios.post(
`${targetService.url}/api/orders`,
req.body,
{ headers: req.headers }
);
};
一旦 API 网关从后端服务接收到响应,它会执行以下任务:
- 转换: 调整响应格式或结构以满足客户端的要求。
- 缓存(可选): 临时存储响应。
js
const handleResponse = async (serviceResponse) => {
// 如果需要,转换响应
const transformedResponse = {
orderId: serviceResponse.order_reference,
estimatedDelivery: serviceResponse.eta,
status: serviceResponse.current_status
};
// 如果适用,缓存响应
if (serviceResponse.cacheable) {
await cacheResponse(
transformedResponse.orderId,
transformedResponse
);
}
return transformedResponse;
};
最后,API 网关将处理后的响应发送回客户端。
在整个过程中,网关记录下一些指标来跟踪每个请求:
js
const logRequest = async (req, res, timing) => {
await logger.log({
timestamp: new Date(),
path: req.path,
method: req.method,
responseTime: timing,
statusCode: res.statusCode,
userId: req.user?.id
});
};