译者 | 布加迪
审校 | 重楼
在过去的15年里,我一直致力于设计不仅实用,还有弹性的API来适应意外故障,并在压力环境下保持高性能。API弹性是指创建能够在中断(比如网络中断或流量突然猛增)后从容恢复的系统,确保它们保持可靠性和安全性。由于API是当今互联系统的支柱,这一点变得至关重要。我的经验包括应对一些挑战,比如处理分布式架构中的服务故障以及缓解高需求系统中断的级联效应。我在本文中将介绍实用策略,以便设计可以扩展、有效处理错误并长期保持安全的API。
API架构的演变
多年来,API架构不断发展,以堵住先前设计存在的缺口,并跟上日益紧迫的需求。然而它常在这个过程中引入新的挑战。下面细述API架构方面的重大里程碑。
1. SOAP
简单对象访问协议(SOAP)开发于20世纪90年代末,是最早被广泛使用的API协议之一。它为消息格式提供了严谨的结构,便于可靠安全的通信。它基于XML,确保了严格的协议和重量级的错误处理。它非常适合复杂的企业级系统集成(银行和医疗保健等),一致的错误处理和严格的合规必不可少。然而,SOAP存在明显的缺点:
- 复杂性:XML有严格的模式和消耗资源的冗长消息。
- 紧密耦合的服务:SOAP API倾向于创建紧密耦合的服务,这会降低灵活性和可扩展性。
- 高开销:它依赖大量的元数据和处理,这影响了性能。
SOAP的局限性促使了更轻盈、更灵活的解决方案应运而生。
2. REST
2000年引入的代表状态传输(REST)是API设计领域的一个重大转变,它比SOAP更简单、更轻巧。无状态结构使其轻量级且易于使用,这有助于它流行起来,特别是用于构建Web和移动应用程序。开发人员很喜欢它使用HTTP方法(GET、POST、PUT、DELETE)以及JSON进行数据交换,因为这些方法易于理解和使用。
然而,REST存在自己的局限性:
- 过度抓取和抓取不足:客户通常获取过多或过少的数据,导致效率低下。
- 没有实时支持:REST不是为实时交互构建的,因此需要为这些场景添加额外层。
REST今天仍然被广泛使用,但日益与解决这些缺点的新方法相结合。开发出了像HATEOAS这样的扩展,以帮助克服REST的一些限制,允许客户使用链接来导航资源。尽管如此,HATEOAS并没有得到广泛的应用。相反,许多开发人员现在将REST与GraphQL之类的工具配对,以更好地满足特定需求。
3. GraphQL
GraphQL于2015年由Facebook引入,旨在克服REST的一些限制。它允许客户只请求所需的数据,从而防止获取过多或过少的信息。使用单个端点,它简化了在复杂应用程序中获取数据的过程,尤其是在处理深度嵌套的关系时。
然而,GraphQL存在其弊端:
- 服务器管理:处理和优化GraphQL查询可能需要在服务器端进行大量工作。
- 性能问题:设计不良或有害的查询会给系统资源带来沉重的负担。
虽然GraphQL在某些情况下是一种强大的工具,但其复杂性意味着最好与REST一起工作,而不是完全取代它。
4. 微服务和事件驱动架构
从单体架构向微服务转变带来了模块化和独立的可扩展性,但也引入了利用API网关来处理通信的需求。在这种方法中,每个微服务专注于一个特定的任务,这减少了依赖系统其他部分的程度,并允许独立扩展。为了协调这些服务,API网关管理和指导请求。
另一方面,对于不需要即时响应的任务,Kafka之类的事件驱动系统支持实时工作流。这种方法适用于消息传递平台或物联网应用程序等应用场景。
然而,这种架构也带来了挑战:
- 服务发现:在分布式系统中,动态识别和关联服务是一个挑战。Kubernetes等工具和Istio等服务网格通过自动化服务注册和发现来帮助处理这个问题。它们还提供负载均衡和故障切换等功能,以保持服务顺畅运行。
- 一致性模式:在分布式系统中维护一致性很棘手。最终的一致性模式可能允许过时的数据存留一段时间,而更严格的一致性可能会减慢系统的速度。开发人员需要权衡这些取舍,根据其应用最需要的因素来设计解决方案。
- 管理分布式事务:跨多个服务协调工作流需要深思熟虑的模式。Saga模式将事务分解为更小的步骤,每个步骤都有后备计划,以防出现问题。CQRS(命令查询责任分离)将读写操作分开,使扩展更容易,使架构更易于管理。
API设计的每一步都旨在改进系统的性能和适应性,不过这通常需要克服新的技术障碍。如果将强大的设计原则与合适的工具和模式相结合,团队可以构建可扩展、有弹性的架构,同时有效地应对这些复杂性。
弹性对API意味着什么?
API的弹性指能够在压力环境下保持功能正常,并在不完全失效的情况下处理错误。这通过实施确保系统很少宕机的容错机制来实现。弹性API可以不间断地管理繁重的负载。比如说,支付网关应该在高流量期间继续运行,即使某些服务暂时不可用。另一方面,缺乏弹性的API很可能在大流量或依赖服务宕机时失效。
构建弹性API的一个关键因素是可扩展性。可扩展性允许API处理不断增加的流量。这可以通过纵向扩展(向现有服务器添加更多的资源,如内存或CPU)或横向扩展(部署额外的服务器实例以分配负载)来实现。可扩展性还需要使用故障切换机制,其中API跨多个区域或服务器部署,以减小单个位置的中断造成的影响。
负载测试在确保可扩展性方面起着至关重要的作用。通过模拟不同大小的流量,开发人员可以测量吞吐量和延迟等性能指标,并进行必要的优化。AWS负载均衡器等工具有助于评估API是否可以在没有延迟或瓶颈的情况下处理需求。
简而言之,可扩展性确保API可以管理高流量,而弹性确保可以有效地处理错误和故障。总之,这些原则对于设计稳健可靠的API至关重要。
API弹性:最佳实践和常见陷阱
这节解释如何构建可靠的API,并指出可能削弱它们的常见错误。这节介绍了使API更强大更安全的方法,以便可以在不影响系统的情况下处理问题。如果遵循下面的建议,你可以创建顺畅运行的API,并保持用户体验的一致性,即使出现问题时也是如此。
1. 错误处理
在构建API时,以一种帮助开发人员并保持系统顺利运行的方式处理错误很重要。使用清晰、标准化的错误代码,比如400表示错误的请求,503表示服务不可用,这样客户就可以知道哪里出了问题,下一步该怎么做。此外,为这些代码附上解释问题并建议解决方法的详细信息。比如说,不要返回模糊的“错误请求”,而是指定“缺少必填字段‘email’”。
为了防止级联故障,不妨考虑使用断路器之类的技术,这有助于停止对失效服务的重复调用,并让系统有机会恢复、防止大范围中断。它可以很好地阻止对失效服务进行重复调用,从而防止更严重的问题。比如说,像Resilience4j for Java这样的库就可靠地实现了断路器及其他容错机制,因而更容易将这些实践集成到你的应用程序中。
另一方面,忽略详细的错误报告会使调试复杂又耗时。一般错误(比如“500内部服务器错误”)无法提供足够的信息来快速解决问题。此外,不限制错误率可能会在出现问题时给系统带来额外的负载。
2. 可扩展性和负载处理
在设计API时,重要的是通过跨多个实例分散流量来规划可扩展性。实现这一目标的一种方法是,使用负载均衡器在多个实例之间分配流量。为了提高弹性,可以考虑跨不同区域使用故障切换系统,以减小本地中断造成的影响。使用JMeter之类的工具运行负载测试有助于识别瓶颈,并验证你的API在高流量环境下性能良好。
在扩展时,可以在自动扩展策略和手动扩展策略之间进行选择。Kubernetes Horizontal Pod Autoscaler支持的自动扩展可以根据实时需求动态调整资源。这种方法减少了操作开销,并确保在流量高峰期间有效地使用资源。然而,它可能会在配置和对突发需求变化的响应时间方面带来复杂性。另一方面,手动扩展带来了更强的控制性和可预测性,但可能导致响应意外流量模式出现延迟,可能需要持续监控。
避免过度依赖向单台服务器添加资源也很重要,因为这种方法可能会变得昂贵、难以为继。此外,跳过负载测试可能会使你的API在流量高峰期间容易受到攻击,可能导致在最需要的时候偏偏停机。
3. 速率限制和节流
速率限制通过确保API不被滥用和资源被公平使用来帮助保护API。像令牌桶和漏桶算法这样的方法适用于设置限制。令牌桶算法很灵活,允许在设定的限制内的流量突发,这使得它非常适合间歇性峰值的场景。相比之下,漏桶算法强制执行一致的流量速率,使其非常适合长时期保持稳定的资源消耗。选择合适的方法取决于API的流量模式和用例。
向用户清楚地传达速率限制也很重要。当用户达到速率限制时通知用户,并指导如何调整使用量。为API端点设置适当的超时,并在重试期间使用指数回退方法,以防止在依赖项缓慢或没有响应时发生级联故障。
然而,如果速率限制设置不当,就会让用户感到沮丧。如果客户觉得在为不够大的访问权付费,激进的限制(尤其是在货币化的API方面)可能会把他们赶走。比如说,如果没有清楚地解释限制,或者如果用户没有收到反馈,他们可能会感到困惑。此外,在流量高峰时不调整限制可能会影响用户体验。
4. 安全
在设计API时,安全是保护系统和用户的关键环节。首先,从实现强身份验证方法入手,比如OAuth 2.0和JSON Web令牌(JWT)。始终使用HTTPS进行安全通信,并对传输中或存储的敏感数据进行加密。设置速率限制和IP白名单有助于防止未授权的访问和蛮力攻击,确保只有受信任的客户端可以使用你的API。此外,定期的安全检查(包括审计和渗透测试)对于识别和处理漏洞也很重要。
另一个重要的考量是保护API端点免受注入攻击。始终验证和清理所有输入,使用参数化查询,并限制输入大小,以尽量降低风险,并防止SQL或命令注入等攻击。部署速率限制措施,并使用Web应用防火墙(WAF)或专门针对API的入侵检测和防御系统(IDS/IPS)之类的工具实时监控和缓解威胁,并保护系统免受分布式拒绝服务(DDoS)攻击。
此外,还有一些常见的陷阱需要避免。直接在代码中硬编码API密钥或秘密信息有风险,因为这会使它们暴露给未授权访问者。另一个问题是没有设置适当的令牌过期和轮换,这可能使系统容易受到攻击。没有清晰而详细的安全文档的API常常导致糟糕的实施,使它们容易受到攻击。积极主动地对待安全也很重要,而不是只在出现问题后才被动反应。使用监控工具跟踪异常活动,并接收有关潜在威胁的警报。
5. 版本控制和兼容性
API版本控制有助于防止更新破坏已经使用API的系统。常见的方法包括将版本添加到URL(比如/v1/resource)或在header中指定。这种方法使客户很容易知道他们在使用哪个版本。为了避免干扰用户,保持向后兼容性很重要。当你需要淘汰旧版本时,尽早沟通弃用计划,并通过文档、电子邮件更新或带有弃用警告的API响应头通知客户,帮助他们顺利换成新版本。版本控制让开发人员可以在不给现有用户带来问题的情况下不断改进API。
另一方面,跳过向后兼容可能会让用户感到沮丧,并将他们赶走。在没有适当指导或沟通的情况下发布破坏现有系统的更新只会阻碍采用。此外,切莫将版本控制与环境(如登台或生产环境)混在一起。这会造成混乱,并使顺畅管理更新变得更困难。API网关之类的工具(比如Kong、APIgee和AWS API Gateway)可以简化版本管理和监控使用情况,这将帮助你确定何时可以安全地淘汰旧版本。
6. 可观察性
密切关注API的性能是确保其顺利运行的关键。如果跟踪请求率、错误计数和响应时间等指标,你就可以清楚地了解系统的健康状况。AWS CloudWatch、Prometheus、Grafana和Datadog之类的工具支持实时监控,从而更容易发现问题并及早解决。为延迟、错误率和吞吐量等重要指标设置警报也很有帮助,以便在问题恶化之前解决问题。
日志记录是可观察性的另一个关键部分,它对调试和故障排除极其有用。使用适当的日志级别很重要——debug(调试)用于开发期间的详细故障排除,info(信息)用于一般操作数据,warn(警告)用于警告潜在问题,error(错误)则用于需要立即注意的关键问题。使用适量的日志记录也很重要;在较低级别添加过多的日志(比如生产环境中调试)将有助于迅速找到重要的趋势,而另一方面,不足的日志可能让你看不到潜在的问题。
使用OpenTelemetry之类的开放标准,在一个框架下统一度量、日志和跟踪。这使得从系统的各个部分收集和连接数据变得更容易,从而使你清楚地了解API的行为方式。使用这种统一的方法还简化了使用监控工具,并使调试问题变得更容易。最后,确保在测试和登台环境中包含可观察性。关注这些早期阶段可以帮助你在性能问题或bug到达生产环境之前揪出它们。
构建弹性API的工具和技术
- API网关:AWS API Gateway、Kong和APIgee是一些领先的API管理和可扩展性综合功能平台。它们支持对路由、速率限制和身份验证进行集中控制。
- 监控和日志记录:AWS CloudWatch、Datadog和New Relic提供了强大的解决方案,用于监控API性能指标,并针对服务异常提供实时警报。
- 测试工具:Postman、JMeter和SoapUI之类的工具便于深度测试,并确保API将根据性能和弹性基准的要求来运行。Postman广泛用于功能测试,JMeter用于负载测试,SoapUI用于SOAP测试和REST API测试。
API设计的未来
由于人工智能、机器学习、无代码/低代码平台等的发展,API设计将继续以不断增长的速度变化。这些都旨在使API极其智能化、极具弹性,并可供更庞大的开发人员和非开发人员群体使用。
1. API设计中的AI和机器学习
人工智能和机器学习正在改变我们保持API顺畅运行的方式。这些工具有助于监控系统、预测潜在问题和提高性能。通过研究过去的数据,人工智能可以识别可能导致中断或减速的模式,这让团队有机会在问题变成更大的混乱之前解决问题。
此外,机器学习还有助于发现系统中的异常活动。它分析不同的指标,以发现一开始可能不明显的问题。这使得修复性能问题更容易,并使系统总体上趋于更稳定。人工智能和机器学习共同提供了实用的方法来确保API保持可靠和高效。
实际使用:
- Netflix使用基于人工智能的混沌工程来测试系统,通过引入小故障来发现和修复弱点。
- AWS通过预测扩展提前调整资源,确保有效好满足需求。
- Google Cloud Monitoring集成了基于机器学习的警报,可以在潜在的系统故障发生之前预测,帮助API提供商主动采取行动。
2. 无代码和低代码API平台
无代码和低代码平台让没有强大编程技能的人也可以创建和管理API。它们依赖简单的可视化工具(如拖放功能),消除了过去需要高级编码知识的任务复杂性。这种平台向更多用户(包括业务团队)敞开了流程,从而加强了部门间的协作。
一大好处是加快开发。这些对用户友好的工具减少了构建API所需的时间和精力。它们还使技术团队和非技术团队更容易协同工作,让更多的人参与到API的创建和集成。
实际使用:
- ZAPIer:无代码平台让用户在不编写代码的情况下集成面向Google Sheets、Slack和Trello等流行服务的API。
- MuleSoft Anypoint Platform:将低代码工具与高级功能相结合,帮助专业开发人员快速构建和部署易于扩展的API。
3. 自描述和可发现的API
如今API常常在其设计中包含理解它们所需的一切。这减少了对额外文档的需求,并使其更容易集成。OpenAPI(以前名为Swagger)之类的工具可以帮助开发人员直接从API代码创建准确且最新的文档。这确保了一切保持一致、易于使用。它还使导入更快速、维护更简单,并使API对开发人员更友好。RapidAPI之类的平台还使查找和连接API变得更容易,省去了过程中不必要的步骤。
实际使用:
- Stripe API:采用OpenAPI规范生成详细的、对用户友好的文档,开发人员可以一目了然和快速运用。
- RAPIdAPI Marketplace:充当集中式平台,使开发人员能够以最小的阻力发现和集成API。
4. 事件驱动架构
事件驱动API在创建实时工作且不依赖严格时间表的工作流中起着关键作用。它们尤其适合管理不断变化的情形,这使得它们非常适合聊天应用程序、物联网设备和股票交易系统之类的应用。这些API在事件发生时响应事件,从而改善了用户体验,简化了系统运行,并且更容易从容应对增长。这种结构允许系统的不同部分独立运行,这有助于在系统扩展时保持灵活性和可靠性。
除了使系统运行更顺畅外,事件驱动API还支持即时通信和更新。它们确保用户和系统都能没有延迟地获得所需的信息,这有助于一切系统更有效地运行。无论是支持消息传递应用程序,还是帮助微服务快速共享数据,这些API都在改变应用程序处理不断变化的信息的方式。
实际使用:
- Slack实时消息API:这为机器人或其他应用程序提供了即时收发消息的能力,因此它减少了协作中的阻力。
- 事件流:在Apache Kafka和AWS EventBridge之类的平台上,事件驱动API可以跨微服务实时高效地共享数据。
5. 无服务器
无服务器计算通过将基础设施管理转移到AWS Lambda和Google Cloud Functions等云平台来改变API的处理方式。开发人员编写触发时才运行的代码,而不是处理服务器。这种方法使操作更简单,使成本得到控制,并自动调整资源以满足需求。企业只需为所使用的资源付费,并获得根据流量需求扩大或缩小规模的优势。
实际使用:
- AWS Lambda支持流量模式不可预测的API,在不过度配置资源的情况下保持性能。
- Twilio使用无服务器计算来管理其通信API,在高需求期间实现无缝扩展。
6. 通过设计保证安全API
API正在适应日益加大的网络安全挑战。优先考虑安全可以帮助它们有效地应对攻击,保护敏感信息。许多现代API现在使用人工智能等工具来检测威胁,并运用零信任原则来确认每次交互。这些步骤提高了安全性,并确保API即使在有风险的情况下也保持可靠。
借助注重安全的方法,API可以防止未授权访问或不寻常的流量模式等问题。通过使用严格的身份验证方法,它们保护用户数据并建立信任,使它们成为处理敏感信息的企业的重要资源。
实际使用:
- Microsoft Azure API管理:集成了基于人工智能的威胁检测,以监控API使用情况,查找流量异常和未经授权的访问活动。
- Twilio API:通过API密钥、基于令牌的访问和定期的令牌轮换策略实现最严格的身份验证。
结论
我们需要精确而全面地设计API弹性,遵循错误处理、可扩展性和监控等方面的最佳实践。随着行业快速发展,开发人员必须密切关注,并始终跟踪新的工具、技术和策略。只有不断学习和创新,API开发人员才可以构建稳健的系统,能够适应复杂的大规模需求,同时满足用户和企业不断变化的需求。这种致力于不断改进的做法确保API保持实用功能、适应未来形势,并充当互联数字生态系统的支柱。
原文标题:Best Practices for Designing Resilient APIs for Scalability and Reliability,作者:Parvin Gasimzade