web-dev-qa-db-fra.com

Comment puis-je accélérer les utilisateurs de l'API de mon site?

Les utilisateurs légitimes de mon site martent occasionnellement le serveur avec des demandes d'API entraînant des résultats indésirables. Je souhaite mettre en place une limite de ne pas dire qu'un appel d'une API toutes les 5 secondes ou n appels par minute (je n'ai pas encore compris la limite exacte). Je pouvais évidemment enregistrer tous les appels d'API dans une DB et faire le calcul sur chaque demande de voir si elles sont sur la limite, mais toutes ces frais générales supplémentaires sur chaque demande seraient en train de vaincre le but. Quelles sont les autres méthodes moins intensives de ressources que je pourrais utiliser pour instituer une limite? J'utilise PHP/Apache/Linux, pour ce que ça vaut.

50
scotts

Ok, il n'y a aucun moyen de faire ce que j'ai demandé sans tout écrit sur le serveur, mais je peux au moins éliminer la journalisation de chaque demande. Une solution consiste à utiliser la méthode de l'étranglement "fuite godet", où il n'arrête que la trace de la dernière demande ($last_api_request) et un rapport du nombre de demandes/limite pour le délai ($minute_throttle). Le seau qui fuit ne réinitialise jamais son compteur (contrairement à l'accélérateur de l'API Twitter qui réinitialise toutes les heures), mais si le godet devient plein (l'utilisateur atteint la limite), ils doivent attendre n secondes pour que le godet soit vidant un peu avant ils peuvent faire une autre demande. En d'autres termes, c'est comme une limite de roulement: s'il y a des demandes précédentes dans le délai, elles fuient lentement le godet; Cela ne vous limite que si vous remplissez le godet.

Cet extrait de code calculera un nouveau $minute_throttle valeur sur chaque demande. J'ai spécifié le minute dans $minute_throttle Parce que vous pouvez ajouter des gaz pour une période de temps, telle que toutes les heures, quotidiennes, etc. Bien que plus d'un commencent rapidement à le rendre confus aux utilisateurs.

$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 );
52
scotts

Je ne sais pas si ce fil est toujours en vie ou non, mais je suggérerais de garder ces statistiques dans la mémoire cache de mémoire comme memcached. Cela réduira les frais généraux de la journalisation de la demande à la DB mais servira toujours le but.

4
Kedar Joshi

La solution la plus simple serait de donner à chaque touche API un nombre limité de demandes par 24 heures et de les réinitialiser à certaines personnes connues, fixes, temps.

S'ils épuisent leurs demandes d'API (c.-à-d. Le compteur atteint zéro ou la limite, en fonction de la direction que vous comptez), arrêtez de les servir de données jusqu'à ce que vous réinitialisez leur compteur.

De cette façon, ce sera dans leur Le meilleur intérêt de ne pas vous frapper avec des demandes.

En plus de la mise en œuvre à partir de zéro, vous pouvez également jeter un coup d'œil à l'infrastructure API comme 3CALE ( http://www.3scale.net ) qui limite-t-il un tas d'autres trucs (Analytics etc.). Il y a un PHP plugin pour celui-ci: https://github.com/3scale/3scale_ws_api_for_php .

Vous pouvez également coller quelque chose comme le vernis en face de l'API et faire le taux d'API limitant comme ça.

1
steve

Vous dites que "tous les frais de surcharge supplémentaires sur chaque demande seraient vaincre le but", mais je ne suis pas sûr que ce soit correct. Le but n'est-il pas de prévenir le martelage de votre serveur? C'est probablement la façon dont je voudrais le mettre en œuvre, car il ne nécessite que seulement une lecture rapide/écriture. Vous pouvez même parcourir le serveur API Vérifie à un DB/disque différent si vous étiez inquiet pour la performance.

Toutefois, si vous souhaitez des alternatives, vous devriez vérifier mod_cband , un module Apache tiers conçu pour aider à la bande passante. Bien qu'il soit principalement destiné à la limitation de la bande passante, cela peut être accéléré sur des demandes par seconde. Je ne l'ai jamais utilisé, alors je ne suis pas sûr de quel genre de résultats obtenus. Il y avait aussi un autre module appelé mod-mantle, mais ce projet semble être fermé maintenant et n'a jamais été publié pour rien au-dessus de la série Apache 1.3.

1
zombat