使用 Apigee 进行 API 代理设计和开发的最佳做法

本页面适用于 ApigeeApigee Hybrid

查看 Apigee Edge 文档。

本文档提供了一套使用 Apigee 开发 API 代理的最佳做法。

此处涉及的主题包括设计、编码、政策使用、监控和调试。这些信息是根据与 Apigee 合作的开发者成功实施 API 项目的经验收集的。这是一个活动文档,会不时更新。

除了上述指南之外,您还可以了解反模式简介

开发标准

注释和文档

  • ProxyEndpointTargetEndpoint 配置中提供内嵌注释。注释增强了流的可读性,特别是在政策文件名不足以描述流的基本功能的情况下。
  • 提交有用的注释。避免显而易见的注释。
  • 使用一致的缩进、间距、垂直对齐等。

框架式编码

框架式编码涉及将 API 代理资源存储在您自己的版本控制系统中,以便在本地开发环境中重复使用。例如,要重复使用某项政策,请将其存储在源代码控制系统中,以便开发者能够与其同步,并在自己的代理开发环境中使用该政策。

  • 为了实现 DRY(避免重复代码)原则,在可能的情况下,政策配置和脚本应实施专门的、可重复使用的功能。例如,从请求消息中提取查询参数的专用政策可以称为 ExtractVariables.ExtractRequestParameters
  • 清理 API 代理中未使用的政策和资源(JavaScript、Java、XSLT),尤其是那些可能会减慢导入和部署过程的大型资源。

命名规则

  • 政策 name 特性和 XML 政策文件名必须相同。
  • 脚本和 ServiceCallout 政策name 特性和资源文件名应相同。
  • DisplayName 应向从未使用过该 API 代理的用户准确描述政策的功能。
  • 根据政策的功能为其命名。Apigee 建议您为自己的政策建立一致的命名规则。例如,使用简短前缀,后跟一系列由短划线分隔的描述性单词。例如,AssignMessage 政策使用 AM-xxx。另请参阅 apigeelint 工具
  • 对资源文件使用适当的扩展名,对 JavaScript 使用 .js,对 Python 使用 .py,对 Java JAR 文件使用 .jar
  • 变量名称应保持一致。如果您选择一种样式(例如:camelCase 或 under_score),请在整个 API 代理中使用该样式。
  • 请尽可能使用变量前缀来根据其用途组织变量,例如 Consumer.usernameConsumer.password

API 代理开发

初始设计注意事项

  • 如需了解有关 RESTful API 设计的指导,请下载电子书 《网络 API 设计:缺失的环节》(Web API Design: The Missing Link)
  • 尽可能利用 Apigee 政策和功能来构建 API 代理。避免对 JavaScript、Java 或 Python 资源中的所有代理逻辑进行编码。
  • 以有条理的方式构建流。使用多个流(每个流一个条件)比为同一个 PreFlow 和 Postflow 关联多个条件更可取。
  • 作为“故障安全机制”,使用 / 的 ProxyEndpoint BasePath 创建一个默认 API 代理。这可用于将基本 API 请求重定向到开发者网站、返回自定义响应,或者执行比返回默认 messaging.adaptors.http.flow.ApplicationNotFound 更有用的其他操作。
  • 使用 TargetServer 资源将 TargetEndpoint 配置与具体网址分离开来,从而支持跨环境的升级。
    请参阅跨后端服务器的负载平衡
  • 如果您有多个 RouteRules,请创建一个作为“default”,即没有条件的 RouteRule。确保在条件路由列表中最后定义默认 RouteRule。ProxyEndpoint 中的 RouteRule 以自上而下的顺序评估。 请参阅 API 代理配置参考
  • API 代理软件包大小:API 代理软件包的大小不能超过 15MB。
  • API 版本控制:如需了解 Apigee 对 API 版本控制的看法和建议,请参阅《网络 API 设计:缺失的环节》(Web API Design: The Missing Link) 电子书中的版本控制

启用 CORS

在发布 API 之前,您需要将 CORS 政策添加到 ProxyEndpoint 的请求 PreFlow 中,以支持客户端跨源请求。

CORS(跨源资源共享)是一种标准机制,允许在网页中执行的 JavaScript XMLHttpRequest (XHR) 调用与来自非源网域的资源进行交互。CORS 是所有浏览器强制执行的同源政策一种常用解决方案。例如,如果您从浏览器执行的 JavaScript 代码向 Twitter API 发出 XHR 调用,则该调用将失败。这是因为为浏览器提供网页的网域与为 Twitter API 提供服务的网域不同。CORS 针对这个问题提供了一个解决方案,允许服务器在希望提供跨域资源共享的情况下“选择加入”

如需了解如何在发布 API 之前在 API 代理上启用 CORS,请参阅向 API 代理添加 CORS 支持

消息负载大小

为了防止 Apigee 中出现内存问题,消息负载大小上限为 10MB。超出大小上限会导致 protocol.http.TooBigBody 错误。

使用 Apigee 代理请求/返回大型负载时发生错误中也讨论了此问题。

以下是在 Apigee 中处理大型消息大小的推荐策略:

  • 流式传输请求和响应。请注意,在您执行流式传输操作时,政策不再有权访问消息内容。请参阅流式传输请求和响应

故障处理

  • 利用 FaultRules 处理所有故障处理。(RaiseFault 政策用于停止消息流并将处理发送到 FaultRules 流。)
  • 在 FaultRules 流中,使用 AssignMessage 政策来构建故障响应,而不是使用 RiseFault 政策。根据发生的故障类型有条件地执行 AssignMessage 政策。
  • 始终包含默认的“catch-all”故障处理程序,以便系统生成的故障可以映射到客户定义的故障响应格式。
  • 如果可能,请始终让故障响应与您的公司或项目中可用的任何标准格式匹配。
  • 使用可针对错误情况提出解决方案的、有意义的和易于理解的错误消息。

请参阅处理故障

持久性

键/值映射

  • 仅对有限的数据集使用键/值映射。它们不是为长期数据存储而设计的。
  • 在使用键/值映射时,请考虑性能,因为此信息存储在 Cassandra 数据库中。

请参阅 KeyValueMapOperations 政策

响应缓存

  • 如果响应失败或者请求不是 GET,请勿填充响应缓存。不应缓存创建、更新和删除。<SkipCachePopulation>response.status.code != 200 or request.verb != "GET"</SkipCachePopulation>
  • 使用一种一致的内容类型(例如 XML 或 JSON)填充缓存。检索 responseCache 条目后,使用 JSONtoXML 或 XMLToJSON 转换为所需的内容类型。这可防止存储两倍、三倍或更多的数据。
  • 请确保缓存键足以满足缓存要求。在许多情况下,request.querystring 可用作唯一标识符。
  • 除非明确要求,否则请勿在缓存键中包含 API 密钥 (client_id)。大多数情况下,对于给定请求,仅受密钥保护的 API 会向所有客户端返回相同的数据。根据 API 密钥为多个条目存储相同的值效率很低。
  • 设置适当的缓存到期间隔以避免脏读。
  • 请尽可能晚地在 ProxyEndpoint 响应 PostFlow 上执行填充缓存的响应缓存策略。换句话说,让其在平移和中介步骤之后执行,包括基于 JavaScript 的中介以及 JSON 和XML 之间的转换。通过缓存中介数据,您可以避免在每次检索缓存数据时执行中介步骤的性能成本。

    请注意,如果中介导致请求与请求之间的响应不同,您可能需要缓存未中介的数据。

  • 用于查找缓存条目的响应缓存政策应出现在 ProxyEndpoint 请求 PreFlow 中。在返回缓存条目之前,请避免实现过多逻辑,而不是缓存密钥生成。否则,缓存的优势将减至最低。
  • 通常,您应该始终让响应缓存查找尽可能靠近客户端请求。相反,您应该让响应缓存填充尽可能靠近客户端响应。
  • 在代理中使用多项不同的响应缓存政策时,请遵循以下准则,以确保每种政策的行为都是独立的:
    • 根据互斥条件执行每项政策。这有助于确保只执行多个响应缓存政策中的一个。
    • 为每个响应缓存政策定义不同的缓存资源。您可以在政策的 <CacheResource> 元素中指定缓存资源。

请参阅 ResponseCache 政策

政策和自定义代码

政策还是自定义代码?

  • 首先(在可能的情况下)使用内置政策。Apigee 政策经过了加强、优化和支持。例如,使用标准 AssignMessage 政策ExtractVariables 政策而不是 JavaScript(如果可能)来创建载荷,从载荷(XPath、JSONPath 等)提取信息等等。
  • 相较于 Python 和 Java,建议使用 JavaScript。不过,如果性能是主要要求,则应使用 Java 而非 JavaScript。

JavaScript

  • 如果 JavaScript 比 Apigee 政策更直观(例如,为许多不同的 URI 组合设置 target.url 时),请使用 JavaScript。
  • 复杂的负载解析,例如迭代 JSON 对象和 Base64 编码/解码。
  • JavaScript 政策具有时间限制,因此无限循环会被屏蔽。
  • 始终使用 JavaScript 步骤并将文件放入 jsc 资源文件夹中。 JavaScript 政策类型会在部署时预编译代码。

Java

  • 如果性能是最高优先级,或者无法在 JavaScript 中实现逻辑,请使用 Java。
  • 在源代码跟踪中收录 Java 源文件。

如需了解如何在 API 代理中使用 Java 的信息,请参阅 JavaCallout 政策

Python

  • 除非绝对需要,否则请勿使用 Python。Python 脚本可能会为简单的执行带来性能瓶颈,因为它在运行时会被解释。

脚本标注(Java、JavaScript、Python)

  • 使用全局 try/catch 或等效方法。
  • 抛出有意义的异常,并正确捕获这些异常,以便在故障响应中使用。
  • 尽早抛出并捕获异常。请勿使用全局 try/catch 来处理所有异常。
  • 必要时执行 null 和未定义的检查。例如,在检索可选流变量时执行此操作。
  • 避免在脚本标注中发出 HTTP/HTTPS 请求。相反,请使用 ServiceCallout 政策,因为该政策可以很好地处理连接。

JavaScript

  • API 平台上的 JavaScript 通过 E4X 支持 XML。

请参阅 JavaScript 对象模型

Java

  • 访问消息负载时,尝试使用 context.getMessage()context.getResponseMessagecontext.getRequestMessage。这可以确保代码在请求和响应流中均可检索负载。
  • 将库导入 Apigee 组织或环境,并且不将它们包含在 JAR 文件中。这可减小软件包大小,并允许其他 JAR 文件访问同一个库代码库。
  • 使用 Apigee 资源 API 导入 JAR 文件,而不是将其包含在 API 代理资源文件夹中。这将减少部署时间,并允许多个 API 代理引用相同的 JAR 文件。另一个好处是类加载器隔离。
  • 请勿使用 Java 进行资源处理(例如,创建和管理线程池)。

Python

  • 抛出有意义的异常,并正确捕获这些异常,以便在 Apigee 故障响应中使用

请参阅 PythonScript 政策

ServiceCallouts

  • 有许多使用代理链的有效用例,您可以在一个 API 代理中使用服务标注来调用其他 API 代理。如果您使用代理链,请确保避免无限循环递归调用返回同一 API 代理。

    如果您要连接在同一组织和环境中的代理,请务必查看将 API 代理链接在一起,详细了解如何实现本地连接,以避免不必要的网络开销。

  • 使用 AssignMessage 政策构建 ServiceCallout 请求消息,并将请求对象填充到消息变量中。(包括设置请求负载、路径和方法。)
  • 在政策中配置的网址需要协议规范,这意味着,网址的协议部分不能通过变量指定,例如 https://。此外,您还必须对网址的网域部分和网址的其余部分使用单独的变量。例如:https://example.com
  • 将 ServiceCallout 的响应对象存储在单独的消息变量中。然后,您可以解析消息变量并使原始消息负载保持不变,以供其他政策使用。

请参阅 ServiceCallout 政策

访问实体

AccessEntity 政策

  • 为了获得更好的性能,请按 uuid(而不是应用名称)查找应用。

请参阅 AccessEntity 政策

日志记录

  • 在多个软件包之间和同一软件包中使用相同的 syslog 政策。这将保持一致的日志记录格式。

请参阅 MessageLogging 政策

监控

Cloud 客户无需检查 Apigee 的单个组件(路由器,消息处理器等)。根据客户的健康检查请求,Apigee 的全球运营团队全面监控所有组件以及 API 健康检查。

Apigee 分析

衡量错误百分比时,分析可以提供非关键 API 监控。

请参阅分析信息中心

调试

Apigee 界面中的跟踪工具可用于在 API 的开发或生产操作期间调试运行时 API 问题。

请参阅使用调试工具

安全

API 代理中的自定义逻辑

构建 API 代理时的一项常见要求是添加一些用于处理请求和/或响应的逻辑。虽然可以通过一组预定义的步骤/操作/政策(例如验证令牌、应用配额或使用缓存对象进行响应)来满足许多要求,但通常可能需要具备编程能力。例如,根据请求中找到的密钥从路由表中查找位置(端点),并动态应用目标端点或自定义/专有身份验证方法等。

Apigee 为开发者提供了处理此类自定义逻辑的多个选项。本文档将介绍这些选项以及何时使用哪些选项:

政策 政策用例
JavaScript 和 PythonScript

何时使用:

  • JavaScript 和 PythonScript 政策在功能上等效。开发者往往会熟练掌握某种语言,往往是选择其中一种语言。
  • JavaScript 和 PythonScript 政策最适合处理不太复杂且不需要使用第三方库的逻辑处理。
  • 这些政策的性能不如 JavaCallout 政策。

不适用情形:

  • Apigee 的 API 网关不是应用服务器(也不提供 node.js 等完整的 JavaScript 运行时)。如果调用程序始终需要几秒钟时间进行处理,则逻辑很可能不属于网关,而应改为属于底层服务。

最佳做法:Apigee 推荐使用 JavaScript 而不是 PythonScript,因为 JavaScript 的性能更佳。

JavaCallout

何时使用:

  • 以内嵌方式处理逻辑的性能至关重要。
  • 现有 Java 库提供了大部分逻辑。

不适用情形:

  • Apigee 的 API 网关并非应用服务器,旨在加载框架,如 Spring、JEE 等。如果调用程序通常需要超过 1 秒的处理时间,则逻辑可视为可正常运行(业务逻辑)。考虑将外部化作为服务。
  • 为了防止 Apigee API 网关滥用,系统对可以执行的代码类型施加了限制。例如,尝试访问特定加密库或访问文件系统的 Java 代码会被阻止执行。
  • Java 应用(特别是依赖第三方库的应用)可能会引入许多(和大型)JAR 文件。这可能会减慢网关的启动时间。
ExternalCallout

何时使用:

  • 理想情况下,它适合外部自定义逻辑,并允许自定义逻辑访问(并根据需要修改)消息上下文。
  • 外部标注实现了 gRPC,并且可能比 ServiceCallout 具有更好的性能。
  • 在 Google Cloud 上使用 Apigee 或 Apigee Hybrid 时,请考虑使用 Cloud Functions 或 Cloud Run 托管此类逻辑。
  • 可以有效替代 Apigee Edge 中的“托管目标”功能。

未使用时:

  • 对于快速内嵌的轻量级逻辑。
ServiceCallout

何时使用:

  • 复杂逻辑最好在网关外部实现。此逻辑可以有自己的生命周期(发布和版本控制),不会影响网关的功能。
  • 当 REST/SOAP/GraphQL 端点已存在或可以轻松实现时
  • 在 Google Cloud 上使用 Apigee 或 Apigee Hybrid 时,请考虑使用 Cloud Functions 或 Cloud Run 托管此类逻辑。
  • 可以有效替代 Apigee Edge 中的“托管目标”功能。

未使用时:

  • 对于可以快速执行的轻量级逻辑
  • API 代理必须传输上下文(例如变量)或从外部实现接收上下文

总结:

  1. 如果逻辑简单或无关紧要,请使用 JavaScript(最好)或 PythonScript。
  2. 如果内嵌逻辑的性能要求高于 JavaScript 或 PythonScript,请使用 JavaCallout。
  3. 如果逻辑必须外部化,请使用 ExternalCallout。
  4. 如果您已有外部实现和/或开发者熟悉 REST,请使用 ServiceCallout。

下图演示了此流程: