Problème: j'ai besoin de télécharger un fichier énorme (5-10 Go) avec de simples requêtes XHR, pour simplifier une question que je dois faire au moins: 100 requêtes (peut-être plus). Je voudrais en améliorer les performances en utilisant RXJS (requêtes http parallèles).
Les questions:
Stack:
Ce que j'ai réellement atteint:
Ce dont j'ai réellement besoin, c'est d'appliquer la synchronisation sur mergeMap
pour l'activer / la désactiver, par exemple j'ai 100urls mais je ne souhaite pas qu'elles s'exécutent avant la dernière.
Par exemple, je veux qu'il s'exécute jusqu'au 17, mais je suspendrai les demandes et attendrons que certaines logiques et demandes se terminent.
Exemple de code:
from(observableQueries) .pipe( mergeMap(partialQuery => this.messageService.getResults(partialQuery, true), 4), takeWhile(o => { if (!o.isMoreResults && o.auditMessageList.length === 0) { this.logger.log(`First page that returns false ${ o.number }`); this.logger.log(`Count of responses that exists in array: ${ allResults.length }`); if (!firstPageThatShouldBeStopped) { firstPageThatShouldBeStopped = o.number; } if (allResults.length === firstPageThatShouldBeStopped) { return false; } } return true; }), retryWhen(genericRetryStrategy()), catchError((err, caught) => { this._alertService.error(this._translate.instant('EXPORT_TO_CSV_DOWNLOAD_ERROR')); return throwError(err); }) ) .subscribe( (res: MessagesResult) => { if (reThrowError) { this.logger.info('Error will be thrown for e2e purposes'); throw new Error('Error will throw for e2e purposes'); } if (res.isMoreResults && res.auditMessageList.length > 0) { allResults[res.number] = res; this.subject.next(true); } else if (!res.isMoreResults && res.auditMessageList.length > 0) { allResults[res.number] = res; this.subject.next(true); } else { this.subject.next(false); } }, err => { // clear subject after emitting value... this.subject.next(true); return this.handleError(err); }, () => { this.logger.info('Download file finished...'); this.logger.info('Time consumed: ', performance.now() - start); try { this.logger.info(`Count Responses: ${ allResults.length } `); const allResultSorted = this._sortResults(allResults); let counter = 0; for (let i = 0; i < allResultSorted.length; i++) { this.logger.info('Index: ' + i, allResultSorted[i]); counter += allResultSorted[i].auditMessageList.length; this.logger.info('Length OF Messages: ' + i, counter); this.fileSaver.save(!allResultSorted[i].isMoreResults, allResultSorted[i].auditMessageList, `audit-events_${ LOCAL_QUERY_COPY.application }_${ timestamp }_${ moment() .tz(this._timezoneService.timezone).zoneName() }.csv`, null, headers); } this.subject.next(false); } catch (e) { this._alertService.error(this._translate.instant('EXPORT_TO_CSV_DOWNLOAD_ERROR')); return this.handleError(e); } finally { // clear subject after emitting value... this.subject.next(true); } } );
Le code fonctionne! Mais le problème vient des appels redondants. Comment il serait possible de les arrêter jusqu'à ce que les dernières requêtes soient exécutées.
4 Réponses :
La parallélisation des requêtes ne sera-t-elle pas la même chose que l'utilisation d'une requête pour chaque itération de la boucle de requête? La bande passante est la même - NON
=> Je dirais NON, car, sur le serveur, lorsqu'un thread exécute la première requête, le second tread peut télécharger la seconde requête => Pour moi, il vaut mieux paralléliser, mais peut-être que je me trompe
Avez-vous un exemple de code sur la façon de paralléliser les choses sans connaître la quantité de href avant l'exécution?
Je viens de faire une autre réponse afin d'y ajouter du code.
@davidr, il serait préférable de mettre à jour votre réponse existante et de l'ajouter ici. Il n'y a aucune raison d'avoir plusieurs réponses dans ce cas :)
Vous pouvez utiliser l'opérateur RXJS 'merge': En supposant que getData (url) est la méthode qui effectue la requête et que cette méthode renvoie un observable, vous pouvez faire:
const urls: string[] = {url1, url2, url3,...}; let mergeHttpCallObservalbe: Observable<any>; urls.forEach((url: string) => { const newHttpCallObservalbe : Observable<any> = myService.getData(url); if (mergeHttpCallObservalbe){ mergeHttpCallObservalbe = merge(mergeHttpCallObservalbe, newHttpCallObservalbe); } else { mergeHttpCallObservalbe = newHttpCallObservalbe; } }); // Now you have merged all your Observable, you can subscribe: mergeHttpCallObservalbe.subscribe(result => { // Do your stuff... });
Voici un bon article concernant les opérateurs Rxjs: https://blog.angularindepth.com/learn- combiner-des-séquences-rxjs-avec-des-diagrammes-interactifs-super-intuitifs-20fce8e6511
J'espère que cela aidera
et que faire si j'ai 100 URL et que j'ai besoin d'en faire des paires ou des triplets? Faire toutes les demandes. J'agrège en fait des données pour un fichier énorme (qui est téléchargé via AJAX)
Oui, bien sûr uniquement si votre serveur est capable de gérer la concurrence d'accès souhaitée
1 requête pour chaque itération de la boucle de requête est une requête séquentielle après l'une et l'autre donc ce n'est pas la même chose que la parallélisation
Normalement, si vous avez 100 demandes, nous limiterons la concurrence à un serveur numérique qui ne se bloquera pas sur des demandes excessives, démontrez-le au code ci-dessous
const urls=[url1,url2,url3] const concurrency=5 const fetchUrls=urls.map(url=>defer(_=>fetch(url))) merge(...fetchUrls,concurrency).subscribe(console.log)
La réponse courte est, oui, le parallélisme devrait améliorer les choses, jusqu'à un certain point.
Un rapide google des "limites de demandes du navigateur" affiche le following , qui montre que vous pouvez faire entre 2 et 13 requêtes simultanées par domaine, selon le navigateur. Tant que la connexion Internet de l'utilisateur n'est pas saturée et que vous n'avez pas encore atteint cette limite, vous devriez en effet être en mesure de faire et d'attendre des demandes simultanément. Ce sera plus rapide que de faire vos demandes une à la fois, car le navigateur n'aura pas à faire de travail en attendant la prochaine.
La bande passante est en effet la même, mais le débit est plus élevé.
Ce qui améliorera vos performances dépend de votre situation particulière, mais je suppose que faire 100 petites requêtes est en fait plus lent que d'en faire une seule grande, car chaque petite requête doit accomplir une poignée de main séparée avant de pouvoir commencer à recevoir des données.
En ce qui concerne RxJs, j'aime la réponse de Fan Cheung , mais je vois que vous avez un mécanisme de nouvelle tentative, qui leur répond ne fait pas. Il devrait être assez facile d'ajouter une nouvelle tentative à chacun des appels "fetch", ou de conserver une liste des URL ayant échoué, et de les réessayer après la tentative du premier lot. Encore une fois, cela dépend de votre situation particulière.
Qu'entendez-vous exactement par appels redondants? Quel comportement aimeriez-vous voir que vous ne voyez pas?
pouvez-vous plutôt modifier votre question d'origine avec une capture d'écran ou quelque chose?
Je peux essayer d'expliquer. J'ai une limite de 100 demandes, mais je ne veux pas y arriver si, par exemple, il a terminé sur les 12 demandes. Mais comme le résultat des données est de grande taille (10k), il continue en fait de faire plus de requêtes "BLANK" et arrête de travailler sur la 42ème requête. Comment peut - "geler" la mergeMap
pour effectuer des appels redondants?
c'est aussi soudainement une question très différente de «le parallélisme améliore-t-il les performances»
1 URL avec différents paramètres de requête
continuons cette discussion dans le chat .
Vous bloquez peut-être le thread principal. Pour éviter cela, utilisez un travailleur Web
Juste une pensée aléatoire: la parallélisation d'un processus pourrait être mieux adaptée par un schéma de contrôle dans lequel le client et le serveur continuent de s'accorder sur leur meilleure connexion / concurrence. Si le serveur ne peut pas gérer plus de demandes simultanées, il ne doit pas en autoriser davantage et le client doit cesser d’obtenir plus de concurrence lorsque la bande passante ou la puissance de traitement ne le prend plus en charge.