L'exigence de limitation de courant est de limiter le nombre d'exécutions simultanées. Une fois ce nombre dépassé, il doit être mis en cache dans une file d'attente. Cet article présente principalement la pratique de la méthode de limitation de courant du service Koa. L'éditeur pense que c'est assez bon. Maintenant, je vais le partager avec vous et vous donner une référence. Suivons l'éditeur et jetons un œil. J'espère que cela pourra aider tout le monde.
J'ai récemment reçu une requête. C'est très simple, il suffit de configurer un serveur, d'appeler une interface fournie lors de la réception de la requête, puis de renvoyer le résultat. En raison des problèmes de performances de cette interface, le nombre de requêtes simultanées ne peut pas dépasser un certain nombre, et le courant doit être limité dans le service.
le middleware koa n'appelle pas suivant
L'idée originale est de compter dans le middleware koa et de mettre en cache la fonction suivante lorsqu'il y en a plus de 6. Lorsque la tâche en cours se termine, appelez Next pour poursuivre les autres demandes.
Plus tard, j'ai découvert que dans le middleware koa, si la requête de fonction suivante n'est pas exécutée, elle ne s'arrêtera pas. Au lieu de cela, le middleware suivant ne sera plus appelé et le contenu sera renvoyé directement.
const Koa = require('koa'); const app = new Koa(); app.use((ctx, next) => { console.log('middleware 1'); setTimeout(() => { next(); }, 3000); ctx.body = 'hello'; }); app.use((ctx, next) => { console.log('middleware 2'); }); app.listen(8989);
Le code ci-dessus imprime d'abord « middleware 1 » sur la console => Le navigateur reçoit « bonjour » => La console imprime « middleware 2 ».
Une autre chose à noter ici est qu'une fois qu'une requête est terminée (fin), sa méthode suivante peut toujours être appelée et le middleware suivant continuera à s'exécuter (mais la modification de ctx ne prendra pas effet, car le la demande a été retournée). De même, la demande de fermeture (close) se comporte de la même manière.
Utiliser wait pour faire attendre la requête
Retarder l'exécution de la fonction suivante ne peut pas atteindre l'objectif. La prochaine chose naturelle à laquelle penser est d'utiliser wait pour attendre la requête en cours. La fonction wait renvoie une promesse. Nous stockons la fonction de résolution dans cette promesse dans la file d'attente et retardons l'appel.
const Koa = require('koa'); const app = new Koa(); const queue = []; app.use(async (ctx, next) => { setTimeout(() => { queue.shift()(); }, 3000); await delay(); ctx.body = 'hello'; }); function delay() { return new Promise((resolve, reject) => { queue.push(resolve); }); } app.listen(8989);
Le code ci-dessus renvoie une promesse dans la fonction de retard, et la fonction de résolution de la promesse est stockée dans la file d'attente. Réglez le minuteur pour exécuter la fonction de résolution dans la file d'attente après 3 secondes, afin que la requête puisse continuer à être exécutée.
Le flux est-il limité pour les itinéraires ou pour les demandes ?
Une fois le principe de base de la limitation de courant mis en œuvre, la question suivante est de savoir où doit être écrit le code de limitation de courant ? Fondamentalement, il y a deux emplacements :
Limitation de courant pour les interfaces
En raison de nos besoins, la limitation de courant est due aux performances limitées de l'interface demandée. Nous pouvons donc limiter le flux de cette demande séparément :
async function requestSomeApi() { // 如果已经超过了最大并发 if (counter > maxAllowedRequest) { await delay(); } counter++; const result = await request('http://some.api'); counter--; queue.shift()(); return result; }
Il existe également une version réutilisable pratique ci-dessous.
async function limitWrapper(func, maxAllowedRequest) { const queue = []; const counter = 0; return async function () { if (counter > maxAllowedRequest) { await new Promise((resolve, reject) => { queue.push(resolve); }); } counter++; const result = await func(); counter--; queue.shift()(); return result; } }
Limiter le flux pour le routage
Cette méthode consiste à écrire un middleware koa et à limiter le flux dans le middleware :
async function limiter(ctx, next) => { // 如果超过了最大并发数目 if (counter >= maxAllowedRequest) { // 如果当前队列中已经过长 await new Promise((resolve, reject) => { queue.push(resolve); }); } store.counter++; await next(); store.counter--; queue.shift()(); };
Ensuite, utilisez simplement ce middleware dans le routeur pour différentes routes :
router.use('/api', rateLimiter);
Comparez
pour implémenter une limitation de courant pour l'interface, ce qui semble logique. C'était un peu compliqué, alors je suis passé à une limitation de trafic spécifique au routage, et tout s'est parfaitement déroulé.
Jusqu'à ce que je reçoive une autre requête, je voulais demander cette interface trois fois et renvoyer le tableau de résultats des trois requêtes. Maintenant le problème vient, on ne peut pas appeler directement l'interface car le courant est limité. Vous ne pouvez pas appeler directement la fonction de l'interface de requête car notre limite actuelle est basée sur le routage. Ce qu'il faut faire? Nous pouvons uniquement demander cet itinéraire et le demander nous-mêmes. . .
Choses à noter
Écoutez l'événement de clôture et supprimez la demande de la file d'attente
Les demandes qui ont été stockées dans la file d'attente peuvent être annulées par l'utilisateur Condition. Comme mentionné précédemment, même si la requête est annulée dans koa, les middlewares suivants fonctionneront toujours, ce qui signifie que les interfaces nécessitant une limitation de courant seront également exécutées, provoquant du gaspillage.
Vous pouvez écouter l'événement de clôture pour atteindre cet objectif. Nous devons marquer chaque requête avec une valeur de hachage :
ctx.res.on('close', () => { const index = queue.findIndex(item => item.hash === hash); if (index > -1) { queue.splice(index, 1); } });
Définir le délai d'attente
. Afin d'empêcher les utilisateurs de Si vous attendez trop longtemps, vous devez définir un délai d'attente, facile à mettre en œuvre dans koa :
const server = app.listen(config.port); server.timeout = DEFAULT_TIMEOUT;
La file d'attente actuelle est déjà trop longue
Si la file d'attente actuelle est déjà trop longue, même si rejoindre la file d'attente expirera également. Par conséquent, nous devons également faire face à la situation où la file d'attente est trop longue :
if (queue.length > maxAllowedRequest) { ctx.body = 'error message'; return; }
Recommandations associées :
La route de l'architecte Java - technologie limitante actuelle et divers langages de programmation
algorithme de limitation de courant nginx
Utiliser Redis pour la limitation de courant
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!