aas
2023 年8 月 7 日 15:18
1
我收到“429 Too Many Requests”消息,即使我已将自托管实例的 API 请求:
DISCOURSE_MAX_ADMIN_API_REQS_PER_MINUTE 增加到 600
我在 app.yml 的 env 部分设置了此项,然后运行了 ./launcher rebuild 并确认该变量已在重建的容器中设置。
这远远超过了我尝试的每分钟请求数
一个不受限制的管理员 API 密钥
似乎之前已经讨论过这个问题,但没有明确的答案说明为什么更改 DISCOURSE_MAX_ADMIN_API_REQS_PER_MINUTE 似乎不起作用:
如何确保使用管理员密钥/用户的 API 请求不受限制?
Bas
(Bas van Leeuwen)
2023 年8 月 8 日 08:39
2
您好 @aas ,
您能提供一些背景信息吗?
您正在发出多少 API 请求?每秒、每分钟、每小时、每天
您确定您使用的是管理员 API 密钥吗?
所有这些请求都来自同一个 IP 地址吗?可能是由于反向代理?
会不会是 nginx 或其他软件给您返回了此错误?
1 个赞
aas
2023 年10 月 3 日 22:52
3
您好 @Bas ,
抱歉回复延迟!
我现在正在重新审视这个问题,因为我们已经集成了 Discourse,并且希望确保不会遇到任何与速率限制相关的问题。
Bas van Leeuwen:
我用一个新密钥进行了测试,以确保它没有任何限制。明确地说,您所说的管理员 API 密钥具体指什么?
我创建了一个具有以下设置的密钥:
它显示,“API 密钥没有限制,所有端点均可访问。”
Bas van Leeuwen:
所有这些请求都来自同一个 IP 地址吗?也许是通过反向代理?
我正在通过从本地 Python shell 发出 API 请求来测试这一点,因此它们来自同一个 IP 地址。我们在服务器上运行脚本时也遇到了速率限制。在这种情况下,所有请求都来自同一个 IP 地址。
我已确认使用以下代码会达到速率限制:
async def get_topic_post_stream(topic_id):
url = f"{DISCOURSE_URL}/t/{topic_id}"
async with httpx.AsyncClient(headers=HEADERS) as client:
topic = await client.get(url)
return topic.status_code
async def get_topic_post_streams(topic_ids):
tasks = [functools.partial(get_topic_post_stream, topic_id) for topic_id in topic_ids]
topics = await aiometer.run_all(
tasks,
# max_per_second=1,
)
return topics
# 只获取 topic_ids 中 15 个主题的一个切片进行测试。
topics = asyncio.run(get_topic_post_streams(topic_ids[:15]))
请注意,max_per_second 参数被注释掉了,这意味着请求数量没有限制。
这在 2.05 秒内完成,并且 15 个请求中有 2 个返回 429。
当我运行 max_per_second=1 时,所有请求都成功完成。
如果您能提供更多详细信息,请告诉我。谢谢!
aas
2023 年10 月 10 日 22:41
4
@Bas ,这是 Python 代码的 JavaScript 等效代码,以便使用开发者工具控制台更轻松地重现:
const DISCOURSE_URL = '';
const HEADERS = {
'Api-Key': '',
'Api-Username': '',
'Content-Type': 'application/json'
};
const topicIds = Array.from({ length: 100 }, (_, i) => i + 1);
async function getTopicPostStream(topicId) {
const url = `${DISCOURSE_URL}/t/${topicId}`;
const response = await fetch(url, { headers: HEADERS });
return response.status;
}
async function getTopicPostStreams(topicIds) {
const results = await Promise.all(topicIds.map(topicId => getTopicPostStream(topicId)));
return results;
}
// 不要限制请求速率,看看你是否会收到两个 429。
(async () => {
const topics = await getTopicPostStreams(topicIds.slice(0, 15));
console.log(topics);
})();
async function getTopicPostStreamsRateLimited(topicIds) {
const results = [];
for (const topicId of topicIds) {
const result = await getTopicPostStream(topicId);
results.push(result);
await new Promise(resolve => setTimeout(resolve, 1000)); // 延迟 1 秒
}
return results;
}
// 每秒 1 个请求返回所有 200
(async () => {
const topics = await getTopicPostStreamsRateLimited(topicIds.slice(0, 15));
console.log(topics);
})();
1 个赞
Bas
(Bas van Leeuwen)
2023 年10 月 11 日 12:19
5
如果我必须猜测一下,这很可能是问题所在。你不是在 API 上受到速率限制,而是在你的 IP 地址的基础上受到限制。
你可以在这里查看每个 IP 地址的设置:Available settings for global rate limits and throttling - #27
如果这确实解决了你的问题,请告诉我
1 个赞
aas
2023 年10 月 12 日 14:01
6
谢谢,@Bas !
在我看来,无论该帖子中提到任何设置,都不应该出现这些 429 错误。在我提供的示例中,我发送了 15 个请求,这低于所有默认 API 限制。我使用的是管理员 API 密钥和用户名完成的此操作。
该示例未超过以下每个 IP 的默认限制:
甚至未超过非管理员限制:
将 DISCOURSE_MAX_REQS_PER_IP_MODE 更改为 warn 或 none 均无效。
我是否遗漏了什么?
顺便说一句,我是通过编辑 app.yml 并运行 ./launcher destroy app && ./launcher start app 来更改设置的。
aas
2023 年10 月 12 日 15:10
7
我在 /var/log/nginx/access.log 中可以看到 IP 地址是正确的,所以我认为 Discourse 没有将所有请求都视为来自同一个 IP。
我也能在管理员界面看到用户的 IP 地址。
这些是我修改过的设置:
DISCOURSE_MAX_ADMIN_API_REQS_PER_MINUTE: 1200
DISCOURSE_MAX_USER_API_REQS_PER_MINUTE: 60
DISCOURSE_MAX_REQS_PER_IP_MODE: none
DISCOURSE_MAX_REQS_PER_IP_PER_10_SECONDS: 100
DISCOURSE_MAX_REQS_PER_IP_PER_MINUTE: 400
编辑:我刚检查了一个失败请求的响应内容,注意到它提到了 nginx:
<html>\r\n<head><title>429 Too Many Requests</title></head>\r\n<body>\r\n<center><h1>429 Too Many Requests</h1></center>\r\n<hr>\n<center>nginx</center>\r\n</body>\r\n</html>\r\n
我将对提及 nginx 的主题做进一步调查。
1 个赞
aas
2023 年10 月 12 日 21:28
8
nginx 配置中两个相关部分似乎是:
limit_req_zone $binary_remote_addr zone=flood:10m rate=12r/s;
limit_req_zone $binary_remote_addr zone=bot:10m rate=200r/m;
limit_req_status 429;
limit_conn_zone $binary_remote_addr zone=connperip:10m;
limit_conn_status 429;
server {
listen 80;
return 301 https://community.ankihub.net$request_uri;
}
和
location @discourse {
add_header Strict-Transport-Security 'max-age=31536000'; # remember the certificate for a year and automatically connect to HTTPS for this domain
limit_conn connperip 20;
limit_req zone=flood burst=12 nodelay;
limit_req zone=bot burst=100 nodelay;
proxy_set_header Host $http_host;
proxy_set_header X-Request-Start "t=${msec}";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $thescheme;
proxy_pass http://discourse;
}
}
我现在剩下的问题是:
非常感谢您的帮助!
1 个赞
Bas
(Bas van Leeuwen)
2023 年10 月 13 日 08:06
9
恐怕我现在要超出我的能力范围了。
如果你受到 nginx 的速率限制,那么调整这些设置并使其不那么严格是有意义的。我不确定 Nginx 是否可以列入 IP 地址白名单?
RGJ
(Richard - Communiteq)
2023 年10 月 13 日 08:58
10
类似如下配置:
map $remote_addr $exclude_from_limit {
default 0;
192.168.1.1 1;
192.168.1.2 1;
}
然后将限制包裹在 if 语句中:
if ($exclude_from_limit = 0) {
limit_req zone=flood burst=24 nodelay;
limit_req zone=bot burst=400 nodelay;
limit_conn connperip 20;
}
是的,您应该在构建过程中使用 pups 进行一些替换以使其持久化,例如,请参阅 web.ssl.template.yml 以了解如何处理。
或者您可以忽略这一点,通过在战略位置插入一些 sleep 来让您的 API 客户端脚本运行得更慢。 ← 推荐的方法
4 个赞
pfaffman
(Jay Pfaffman)
2023 年10 月 14 日 22:11
11
就像在 rescue 中,当它受到速率限制时。这通常是我通常会做的事情。
1 个赞
Isambard
(Isambard)
2024 年3 月 12 日 17:01
12
标准安装下的 API 请求每分钟或每秒的最大持续速率是多少?
我尝试过每秒一次,但似乎达到了限制。而每 7 秒一次请求则运行正常。