2017-02-15 9 views
2

begrenzen Ich habe eine API-Grenze von 10 Anrufe pro Sekunde (jedoch Tausende pro Tag), jedoch, wenn ich diese Funktion ausführen (Jede Style ID des Objekts aufgerufen,> 10 pro Sekunde):Wie API-Aufrufe pro Sekunde mit angular2

getStyleByID(styleID: number): void { 
    this._EdmundsAPIService.getStyleByID(styleID).subscribe(
     style => {this.style.push(style); }, 
     error => this.errorMessage = <any>error); 
    } 

aus dieser Funktion (nur 1 Aufruf verwendet onInit):

getStylesWithoutYear(): void { 
    this._EdmundsAPIService.getStylesWithoutYear(this.makeNiceName, this.modelNiceName, this.modelCategory) 
     .subscribe(
     styles => { this.styles = styles; 
         this.styles.years.forEach(year => 
         year.styles.forEach(style => 
          this.getStyleByID(style.id))); 

     console.log(this.styles); }, 
     error => this.errorMessage = <any>error); 
    } 

Es macht> 10 Anrufe pro Sekunde. Wie kann ich diese Anrufe drosseln oder verlangsamen, um einen 403 Fehler zu vermeiden?

Antwort

2

Ich habe eine ziemlich saubere Lösung, wo man mit zwei Observablen kombinieren die .zip() operator:

  1. eine beobachtbare die Anfragen aussendet.
  2. Eine weitere beobachtbare emittiert einen Wert alle 0,1 Sekunde.

Sie enden mit einer beobachtbaren Emittieren Anfragen bis alle 0,1 Sekunden (= 10 Anfragen pro Sekunde).

Hier ist der Code (JSBin):

// Stream of style ids you need to request (this will be throttled). 
const styleIdsObs = new Rx.Subject<number>(); 

// Getting a style means pushing a new styleId to the stream of style ids. 
const getStyleByID = (id) => styleIdsObs.next(id); 

// This second observable will act as the "throttler". 
// It emits one value every .1 second, so 10 values per second. 
const intervalObs = Rx.Observable.interval(100); 

Rx.Observable 
    // Combine the 2 observables. The obs now emits a styleId every .1s. 
    .zip(styleIdsObs, intervalObs, (styleId, i) => styleId) 
    // Get the style, i.e. run the request. 
    .mergeMap(styleId => this._EdmundsAPIService.getStyleByID(styleId)) 
    // Use the style. 
    .subscribe(style => { 
    console.log(style); 
    this.style.push(style); 
    }); 

// Launch of bunch of requests at once, they'll be throttled automatically. 
for (let i=0; i<20; i++) { 
    getStyleByID(i); 
} 

Hoffentlich werden Sie in der Lage sein, meinen Code Ihren Anwendungsfall zu übersetzen. Lass es mich wissen, wenn du irgendwelche Fragen hast.

UPDATE: Dank Adam, gibt es auch eine JSBin zeigt, wie die Anfragen zu drosseln, wenn sie kommen in nicht konsequent (CONVO in den Kommentaren sehen). Es verwendet den Operator concatMap() anstelle des Operators zip().

+1

Dieser Ansatz löst das Problem von @ Miguel nicht, es sei denn, die Anfragen kommen schneller als sie gesendet werden können. konsequent.Führen Sie zum Beispiel Ihren JSBin-Code aus, warten Sie 5 Sekunden und führen Sie ihn dann in der JSBin-Konsole aus: 'for (let i = 0; i <20; i ++) {getStyleByID (i);}'. Sie werden alle gleichzeitig feuern und nicht richtig drosseln, da die ungenutzten Intervalle im Zip gespeichert werden, der darauf wartet, verwendet zu werden, anstatt verworfen zu werden, wenn sie nicht sofort verwendet werden. – Adam

+1

Guter Fang, Adam! Nur Miguel kann sagen, ob mein Vorschlag in seinem Fall funktionieren würde oder nicht. Wenn nicht, wie würdest du es kugelsicher machen? – AngularChef

+0

[Hier ist der JSBin meiner Lösung.] (Http://jsbin.com/lesatebiwo/1/edit?js,console) Der einzige Nachteil ist, dass jede Anfrage immer um 100m verzögert wird, einschließlich der ersten. Fühlen Sie sich frei, Ihren Beitrag mit dieser Lösung zu bearbeiten, wenn Sie denken, dass es eine Verbesserung ist. Ich fand die Lösung [hier] (http://www.g9labs.com/2016/03/21/lossless-rate-limiting-with-rxjs/) – Adam

1

Während ich diesen Code nicht getestet habe, würde ich etwas in dieser Richtung versuchen.

Grundsätzlich erstelle ich eine Variable, die verfolgt, wenn die nächste Anfrage gemacht werden darf. Wenn diese Zeit nicht verstrichen ist und eine neue Anforderung eingeht, verwendet sie setTimeout, damit diese Funktion im entsprechenden Zeitintervall ausgeführt werden kann. Wenn der Wert delayUntil in der Vergangenheit liegt, kann die Anforderung sofort ausgeführt werden und der Zeitgeber wird ebenfalls um 100 ms von der aktuellen Zeit zurückgesetzt.

delayUntil = Date.now(); 

getStylesWithoutYear(): void { 
    this.delayRequest(() => { 
    this._EdmundsAPIService.getStylesWithoutYear(this.makeNiceName, this.modelNiceName, this.modelCategory) 
     .subscribe(
     styles => { this.styles = styles; 
        this.styles.years.forEach(year => 
         year.styles.forEach(style => 
         this.getStyleByID(style.id))); 

     console.log(this.styles); }, 
     error => this.errorMessage = <any>error); 
    };   
} 

delayRequest(delayedFunction) { 
    if (this.delayUntil > Date.now()) { 
    setTimeout(delayedFunction, this.delayUntil - Date.now()); 
    this.delayUntil += 100; 
    } else { 
    delayedFunction(); 
    this.delayUntil = Date.now() + 100; 
    } 
} 
+0

'ERROR in styles.component.ts (47,9): Name kann nicht gefunden werden 'delayUntil'.) styles.component.ts (50,35): Name kann 'delayUntil' nicht gefunden werden.' – Moshe

+1

Fixed that, Versuchen Sie erneut, die Methode 'delayRequest' zu kopieren. – Adam

2

Sie könnten eine zeitlich Observable nutzen, der alle n Millisekunden auslöst. Ich habe passen sich nicht Ihren Code aber dieses zeigt, wie es funktionieren würde:

someMethod() { 
    // flatten your styles into an array: 
    let stylesArray = ["style1", "style2", "style3"]; 

    // create a scheduled Observable that triggers each second 
    let source = Observable.timer(1000,1000); 
    // use a counter to track when all styles are processed 
    let counter = 0; 

    let subscription = source.subscribe(x => { 
    if (counter < stylesArray.length) { 
     // call your API here 
     counter++; 
    } else { 
     subscription.complete(); 
    } 
    }); 
} 

hier suchen plunk, die sie in Aktion zeigt

+1

Wenn ich versuche, Ihren PLNKR zu starten, werden Fehler in 'boot/app' angezeigt. 'XHR error (404) lädt https: // run.plnkr.co/Zrc7pLEuNlmc8C0p/app/boot.ts'. Ich habe es behoben, indem ich 'index.html' Zeile 25 in' System.import ('./app/boot') geändert habe. ' – Adam

Verwandte Themen