Estoy recibiendo el mensaje “429 Demasiadas solicitudes” para las solicitudes de API a mi instancia autoalojada, incluso con:
DISCOURSE_MAX_ADMIN_API_REQS_PER_MINUTE aumentado a 600
Lo configuré en la sección env de app.yml y luego ejecuté ./launcher rebuild y confirmé que la variable estaba configurada en el contenedor reconstruido.
Esto está muy por encima del número de solicitudes por minuto que estoy intentando.
una clave de API de administrador sin restricciones.
Parece que esto ya se ha discutido sin una respuesta clara sobre por qué cambiar DISCOURSE_MAX_ADMIN_API_REQS_PER_MINUTE no parece funcionar:
Ahora estoy revisando esto nuevamente, ya que hemos lanzado una integración de Discourse y queremos asegurarnos de no encontrar ningún problema relacionado con los límites de velocidad.
Lo probé con una nueva clave para asegurarme de que no esté limitada de ninguna manera. Para ser claro, ¿qué significa exactamente una clave de API de administrador?
Dice: “La clave de API no tiene restricciones y todos los puntos finales son accesibles”.
Estoy probando esto haciendo solicitudes a la API desde un shell de Python local, por lo que provienen de la misma dirección IP. También nos encontramos con los límites de velocidad al ejecutar un script en nuestro servidor. En ese caso, todas las solicitudes provenían de la misma dirección IP.
Confirmé que el límite de velocidad se alcanza con el siguiente código:
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
# Solo obtén una porción de 15 de los temas en topic_ids para probar.
topics = asyncio.run(get_topic_post_streams(topic_ids[:15]))
Tenga en cuenta que el parámetro max_per_second está comentado, lo que resulta en que no haya límites en el número de solicitudes.
Esto se completa en 2.05 s y 2 de las 15 solicitudes devuelven 429.
Cuando lo ejecuto con max_per_second=1, todo se completa con éxito.
Avísame si puedo proporcionar más detalles. ¡Gracias!
@Bas, aquí tienes el equivalente en javascript del código python para que sea más fácil de reproducir usando la consola de herramientas de desarrollador:
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;
}
// No limites la tasa de las solicitudes y comprueba que obtienes dos 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)); // Retraso de 1 segundo
}
return results;
}
// 1 solicitud por segundo devuelve todos los 200
(async () => {
const topics = await getTopicPostStreamsRateLimited(topicIds.slice(0, 15));
console.log(topics);
})();
Si tuviera que arriesgar una suposición, este es muy probablemente el problema. No se le está limitando la velocidad en la API, sino en función de su IP.
Me parece que no debería estar recibiendo estos 429, independientemente de la configuración mencionada en esa publicación. En el ejemplo que proporcioné, envié 15 solicitudes, lo cual está por debajo de todos los límites predeterminados de la API. Lo hice usando una clave de API y un nombre de usuario de administrador.
El ejemplo no excede los siguientes valores predeterminados por IP:
Ni siquiera excede los límites para no administradores:
Cambiar DISCOURSE_MAX_REQS_PER_IP_MODE a warn o none no ayudó.
¿Me estoy perdiendo algo?
Por cierto, cambié la configuración editando app.yml y ejecutando ./launcher destroy app && ./launcher start app.
Puedo ver en /var/log/nginx/access.log que la dirección IP es correcta, así que no creo que Discourse considere que todas las solicitudes provienen de la misma IP.
También puedo ver las direcciones IP de los usuarios en el panel de administración.
EDITAR: Acabo de revisar el contenido de la respuesta de una de las solicitudes fallidas y noté que mencionaba 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
Investigaré más sobre los temas que mencionan nginx.
location @discourse {
add_header Strict-Transport-Security 'max-age=31536000'; # recuerda el certificado durante un año y conéctate automáticamente a HTTPS para este dominio
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;
}
}
Mis preguntas restantes ahora son:
¿Debo editar ambas secciones para que coincidan con mi configuración de Discourse? ¿O solo los valores para location @discourse?
¿Cuál es la forma correcta de modificar estos valores y mantener los cambios en futuras reconstrucciones?
Supongo que puedo editar la configuración de nginx directamente en el contenedor y luego detener/iniciar el contenedor. Pero parece que estos valores originalmente provinieron de templates/web.ratelimited.template.yml y pueden ser sobrescritos en una reconstrucción.
Me temo que esto está fuera de mi zona de confort.
Si estás siendo limitado por nginx, entonces sí, tiene sentido jugar con esas configuraciones y hacerlas menos restrictivas. ¿No estoy seguro si Nginx puede poner en lista blanca direcciones IP?
Sí, deberías hacer algunos reemplazos con pups durante la compilación para que esto sea persistente, consulta por ejemplo web.ssl.template.yml sobre cómo abordarlo.
O podrías olvidarte de esto y hacer que tu script cliente de API se ejecute más lentamente insertando algunas pausas en lugares estratégicos. ← enfoque recomendado