博客

我们一直在限制自己的服务

Mythex 的 API 允许每个网络地址每分钟 300 次请求。这一限制的目的是保护我们,防止某个人或脚本大量调用 API。而在生产环境中,它开始拒绝我们自己的服务——即为已发布应用提供服务的边缘部分,以及运行 AI 智能体的服务——因为它们各自只从少数几个地址访问 API。现在,携带我们内部凭据的请求可以豁免,其他所有请求依然按原有方式受限。

症状

API 开始对我们自己的部分内部调用返回 429 Too Many Requests。被击中的有三类调用,而每一类都关系到使用 Mythex 的人:

  • 查询某个自定义域名属于哪个应用。 访客在自己的域名上打开已发布的应用时,我们的边缘会向 API 询问该网站位于何处。这里出现 429,意味着访客无法访问网站。
  • 保存正在运行的智能体轮次。 智能体工作时,其进度会随时保存,因此回复在刷新页面或连接中断后仍然保留。这里出现 429,意味着进度没有被保存。
  • 检查预览的访问权限。 你正在构建的应用的预览是受保护的,而保护机制会向 API 发起查询。这里出现 429,意味着预览无法打开。

这些都不是有人在调用 API。全部都是我们自己。

为什么会这样

按地址限制的速率限制器有一个前提假设:一个地址大致对应一个调用者。对于个人来说,这基本成立。而对于我们自己的基础设施,这一假设严重不符。

  • 为已发布应用和预览提供服务的边缘服务,从少数几个共享的出站地址访问 API。每一位访问任何已发布应用的访客,以及每一次预览检查,看起来都像来自这几个地址之一。
  • 编排器——即运行智能体轮次的服务——从一个容器地址访问 API。每个用户的每一个正在运行的轮次都共享这个地址。

因此,本该属于一个人的请求额度,被整个服务的全部流量所瓜分。这些服务合起来把它用光了,而限速器只是按规则办事:拒绝了它们。

修复

我们自己的服务本身就能证明身份。每一个内部调用都携带一个内部令牌,而 API 内部区域下的每个路由在做任何事情之前都会检查该令牌。

现在,速率限制器使用同样的检查。与内部令牌匹配的请求会被放入允许列表,不计入按地址的额度。其他所有请求的计数方式与之前完全相同:每个地址每分钟 300 次请求。

有两个细节使它不会变成一个漏洞:

  1. 错误的令牌不会获得豁免。 允许列表只匹配真正的内部令牌。携带伪造令牌的请求会像其他请求一样被计数和限流,而内部路由仍然会以 401 Unauthorized 拒绝它。
  2. 限制数值本身没有改变。 我们没有为了腾出空间而提高这个数字。提高它会暂时掩盖问题,直到流量再次增长,而且会削弱限制本应保护的那种情况下的防护能力。

我们把限速器的设置移到了一个小函数中,并为它编写了测试。其中一个测试以允许 5 次的限速器发送 20 个携带内部令牌的请求,并期望全部 20 个都成功。另一个测试从同一地址发送普通请求,期望前五个成功,之后的请求被拒绝。第三个测试发送携带伪造令牌的请求,期望它们以同样的方式被拒绝。

我们的收获

  • 限制本身带有假设。 「一个地址对应一个调用者」内置于按地址的限制之中。一旦你自己的服务位于共享地址之后——而很多服务确实如此:负载均衡器、边缘网络、容器、办公网络——这个假设就不再成立。
  • 通过身份而不是地址区分自己的流量。 地址只是流量到达的方式,而不是发送者是谁。我们早已有一个能证明调用来自我们自己的凭据,只是限速器没有去看它。
  • 不要通过提高数字来解决。 更大的限额可能会让 429 暂时消失,但防护能力会降低,之后还会再次失败。
  • 留意谁被限流了。 落在你自己服务上的 429,是关于你的限制的一份错误报告,而不是限速器正在正常工作的迹象。

这对构建在 Mythex 上的人意味着什么

你不需要做任何改动。已发布在自定义域名上的应用、智能体轮次和预览,不再依赖于 Mythex 其余部分的繁忙程度。你的应用自身的流量是你应用自己的事:这里所说的限制针对的是我们的 API,而不是你发布的应用。

如果你运行自己的后端,同样的陷阱也适用于你添加的任何速率限制。我们关于什么是 API和 AI 构建应用的安全检查清单的指南是很好的入门材料。另外,关于我们自己的基础设施妨碍自身的另一个例子,请参阅我们如何将部署的 502 错误降到零。

Mythex · Pricing · 用 AI 构建任何东西 · 网站和应用模板 · Mythex 对比 · AI 应用构建术语表 · 免费的 AI 应用构建工具 · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 繁體中文(香港) 繁體中文(台灣)