Pregunta

Los usuarios legítimos de mi sitio de vez en cuando clavan el servidor con solicitudes de API que causan resultados no deseados. Quiero establecer un límite de no más de decir una API llamada cada 5 segundos o n llamadas por minuto (no se han dado cuenta el límite exacto todavía). Naturalmente, podría registrar cada llamada a la API en una base de datos y hacer el cálculo en cada petición para ver si están por encima del límite, pero todo esto sobrecarga adicional en cada petición sería derrotar el propósito. ¿Cuáles son otros métodos menos intensivos en recursos podría utilizar para establecer un límite? Estoy usando PHP / Apache / Linux, por lo que vale la pena.

¿Fue útil?

Solución

Ok, no hay manera de hacer lo que le pedía sin cualquier escribe en el servidor, pero al menos se puede eliminar la tala cada petición. Una forma es mediante el método de estrangulamiento "cubo agujereado", en el que sólo mantiene un registro de la última solicitud ($last_api_request) y una relación de la cantidad de solicitudes / límite para el marco de tiempo ($minute_throttle). El cubo agujereado nunca se restablece su contador (a diferencia del acelerador de la API de Twitter, que se actualiza cada hora), pero si el cubo se llena (usuario alcanza el límite), se debe esperar segundos n para el cubo para vaciar un poco antes de que puedan hacer otra petición . En otras palabras, es como un límite de rodadura: si hay solicitudes anteriores dentro del marco de tiempo, están goteando lentamente de la cubeta; sólo se restringe si usted llena el cubo.

Este fragmento de código calculará un nuevo valor $minute_throttle en cada petición. He especificado el minutos en $minute_throttle porque se puede añadir aceleradores para cualquier período de tiempo, tales como horarios, diarios, etc ... aunque más de uno va a comenzar rápidamente para que sea confuso para los usuarios.

$minute = 60;
$minute_limit = 100; # users are limited to 100 requests/minute
$last_api_request = $this->get_last_api_request(); # get from the DB; in epoch seconds
$last_api_diff = time() - $last_api_request; # in seconds
$minute_throttle = $this->get_throttle_minute(); # get from the DB
if ( is_null( $minute_limit ) ) {
    $new_minute_throttle = 0;
} else {
    $new_minute_throttle = $minute_throttle - $last_api_diff;
    $new_minute_throttle = $new_minute_throttle < 0 ? 0 : $new_minute_throttle;
    $new_minute_throttle += $minute / $minute_limit;
    $minute_hits_remaining = floor( ( $minute - $new_minute_throttle ) * $minute_limit / $minute  );
    # can output this value with the request if desired:
    $minute_hits_remaining = $minute_hits_remaining >= 0 ? $minute_hits_remaining : 0;
}

if ( $new_minute_throttle > $minute ) {
    $wait = ceil( $new_minute_throttle - $minute );
    usleep( 250000 );
    throw new My_Exception ( 'The one-minute API limit of ' . $minute_limit 
        . ' requests has been exceeded. Please wait ' . $wait . ' seconds before attempting again.' );
}
# Save the values back to the database.
$this->save_last_api_request( time() );
$this->save_throttle_minute( $new_minute_throttle );

Otros consejos

Puede controlar la velocidad con la algoritmo de cubetas de fichas , que es comparable a la cubeta con goteo algoritmo. Tenga en cuenta que tendrá que compartir el estado de la cubeta (es decir, la cantidad de fichas) sobre los procesos (o lo que sea el alcance que desea controlar). Así que es posible que desee pensar de bloqueo para evitar condiciones de carrera.

Las buenas noticias: Hice todo eso para usted: ancho de banda del acelerador / Token-Bucket

use bandwidthThrottle\tokenBucket\Rate;
use bandwidthThrottle\tokenBucket\TokenBucket;
use bandwidthThrottle\tokenBucket\storage\FileStorage;

$storage = new FileStorage(__DIR__ . "/api.bucket");
$rate    = new Rate(10, Rate::SECOND);
$bucket  = new TokenBucket(10, $rate, $storage);
$bucket->bootstrap(10);

if (!$bucket->consume(1, $seconds)) {
    http_response_code(429);
    header(sprintf("Retry-After: %d", floor($seconds)));
    exit();
}

No sé si este hilo sigue vivo o no, pero yo sugeriría mantener estas estadísticas en la memoria caché como memcached. Esto reducirá la sobrecarga de registro de la solicitud a la base de datos pero todavía servir al propósito.

solución más simple sería simplemente dar a cada clave de API un número limitado de solicitudes por 24 horas, y restablecerlos en algún conocido, fijo, el tiempo.

Si se agotan sus solicitudes de API (es decir. El contador llegue a cero, o el límite, dependiendo de la dirección en la que está contando), dejar de servir a los datos hasta que restablezca su contador.

De esta manera, será en su mejor interés para que no martillo con las solicitudes.

Usted dice que "todos los thos sobrecarga adicional en cada petición se anulando el propósito", pero no estoy seguro de que es correcta. No es el propósito de evitar el golpe de su servidor? Esta es probablemente la manera de que ponerlo en práctica, ya que en realidad sólo requiere una rápida lectura / escritura. Incluso se puede subcontratar el servidor comprueba API a un DB / disco diferente si estaban preocupados por el rendimiento.

Sin embargo, si quieres alternativas, que debe salir mod_cband , un módulo de Apache de terceros diseñado para ayudar en la del ancho de banda. A pesar de ser principalmente para el ancho de banda de limitación, se puede acelerador basado en solicitudes por segundo también. Nunca he usado, así que no estoy seguro de qué tipo de resultados que obtiene. Hubo otro módulo llamado mod-acelerador así, pero ese proyecto parece que está cerrada ahora, y nunca fue puesto en libertad por cualquier cosa por encima de la serie Apache 1.3.

Además de la aplicación desde cero que también se puede echar un vistazo a la infraestructura de la API como 3scale ( http: //www.3scale .NET ) que no limitante de la velocidad, así como un montón de otras cosas (analytics etc.). Hay un plugin para PHP que:. https://github.com/3scale/3scale_ws_api_for_php

También puede pegar algo así como barniz enfrente de la API y hacer el tipo de API limitando así.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top