什么是 API 网关?

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
    });
};

原文:blog.algomaster.io/p/what-is-a...

相关推荐
谢大旭2 小时前
ASP.NET Core 启动并提供静态文件
后端·asp.net
SomeB1oody3 小时前
【Rust自学】20.2. 最后的项目:多线程Web服务器
服务器·开发语言·前端·后端·设计模式·rust
Hello.Reader3 小时前
深入理解 Rust 模块中的路径与公开性:绝对路径、相对路径和 `pub` 的应用
开发语言·后端·rust
m0_748254883 小时前
Spring Boot框架知识总结(超详细)
java·spring boot·后端
极乐丶醉卧沙场4 小时前
获取程序运行目录 (jar运行目录)
java·后端·jar
chian-ocean4 小时前
DIY Shell:探秘进程构建与命令解析的核心原理
linux·后端
Snow_Dragon_L6 小时前
【MySQL】语言连接
android·数据库·后端·sql·mysql·ubuntu·adb
wn5317 小时前
【Go - 小顶堆/大顶堆】
开发语言·后端·golang
安清h7 小时前
【基于SprintBoot+Mybatis+Mysql】电脑商城项目之修改密码和个人资料
数据库·后端·mysql·spring·mybatis