Laden...

Wie mehrere mehrstufige HttpWebRequest Parallel durchführen ?

Erstellt von micha0827 vor 6 Jahren Letzter Beitrag vor 6 Jahren 2.004 Views
M
micha0827 Themenstarter:in
85 Beiträge seit 2015
vor 6 Jahren
Wie mehrere mehrstufige HttpWebRequest Parallel durchführen ?

Guten Tag,

vielen Dank an alle die mir bis jetzt geholfen haben, hier nun eine ausführliche Schilderung der Problemstellung:

Ziel ist es mit Hilfe einer API Abfrage Positionen in Suchergebnisseiten zu speichern um Sie später auszuwerten.

Stufe 1 ist eine DB Tabelle mit 1. "dem Namen meiner Seite" und 2. "den Keywords"

Stufe 2 ist ein Api Call der maximal 10.000 Ergebnisse liefert aber paginiert zurückgegeben wird. Ich würde gern die ersten 1000 Ergebnisse abfragen (würde die Menge aber gern variabel gestalten). Maximale Ergebnisse pro Page sind 100. Der Call liefert die Anzahl der Ergebnisse und die Anzahl der Pages bei jedem Call mit. Bei 1000 Ergebnissen mache ich also 10 Aufrufe a 100 Ergebnisse mit dem Aufruf apicall.de?Page=x ... Zusätzlich gibt es noch die Beschränkung von max. 18 parallelen Aufrufen des Calls apicall.de?Page=x

Die Auswertung mache ich so dass ich die 100 Ergebnisse die ich in einer Liste zurückbekomme in einer mehrdimensionalen Liste speichere. Ich habe dann eine Liste mit 10*100 Ergebnissen die zu einer URL gehören in einer Liste. Diese speicher ich dann mit Positionsnummern in einer neuen eindimensionalen Liste, die ich danach durchlaufe und mit meinem "Namen meiner Seite" vergleiche. So bekomme ich die Position meiner Seite.

Alles ab Stufe 2 funktioniert Dank der Hilfe aus dem Forum hier.

Nur die Stufe 1, daran beiss ich mir momentan die Zähne aus. Synchron ist kein Problem, ich rufe die Datenbank ab und mache jeden Eintrag schön nacheinander. Nur Parallel will mir nicht gelingen.

Eventuell käme noch eine Stufe 0 hinzu, wenn ich bei verschiedenen Abfragen mit verschiedenen Abfrageintervallen arbeiten möchte .... Oder man müsste eine Lösung finden wenn die ganze Prozedur länger dauert als der Abfrageintervall.

Michael

D
985 Beiträge seit 2014
vor 6 Jahren

Dann gibt es also diese Beschränkung mit maximal 18 gleichzeitigen API-Calls nicht mehr?

M
micha0827 Themenstarter:in
85 Beiträge seit 2015
vor 6 Jahren

Ja, sorry ... habe ich nochmal geändert.

Michael

16.835 Beiträge seit 2008
vor 6 Jahren

Ich hoffe, dass Du so etwas nicht direkt in ASP.NET umsetzt (Du hast auch ASP.NET MVC Themen), sondern zB. in einem Windows Service.
ASP.NET ist für so einen Workflow die falsche Basis.

Für die Trennung der Stufen nimmst Du einfach ein Pipelining-System wie zB. die TPL Pipelines.
Jede Stufe wird damit durch einen eigenen Workflow mit In- und Ouput umgesetzt. Skalieren kann man dann ganz einfach mit der Anzahl der Consumer-Tasksauf eine Queue (zB maximal 18 Consumer Tasks auf die Stufe, die den API Call macht, um das Limit zu beachten).
Die Queue kann nun eine BlockingCollection sein, wie in dem Link als Beispiel; kann aber genauso gut irgendeine Datenbank (mit entsprechendem Listener, Timer, Trigger, Event o.Ä.) sein.
Es ist daher egal, wie Stufe Zwei egal aus welcher Queue die Information von Stufe Eins kommt.
Spalte also Deine Logik in mehrere Schritte auf. Je simpler der einzelne Step, desto simpler und modularer auch die Gesamtumsetzung.

Was die Frage nun genau auf Step 1 bezieht weiß ich nicht.
Gehts Dir um den Aufbau der Datenbank, das Schema, oder was ist nun die genaue Frage?

M
micha0827 Themenstarter:in
85 Beiträge seit 2015
vor 6 Jahren

ASP.NET ist für so einen Workflow die falsche Basis.){gray}

Ja aktuell als Win Programm. Wenn es fertig ist und ich mich in .NET Core eingearbeitet habe wollte ich nochmal umstellen. Mit ASP.NET mach ich die Auswertung im Frontend.

Michael

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo micha0827,

ich verstehe in der Anforderung nicht genau was in der DB steht und was ein API-Call ist genau ist. Auch verstehe ich nicht warum die Abfragen seitenweise sein müssen.

Können nicht alle API-Calls auf einmal (schon begrenzt auf maximal zulässige Parallelität) durchgeführt werden und die "Ergebnisse" in der DB (andere Tabelle) gespeichert werden?
Erst fürs Anzeigen lädst du die Daten dann aus der DB und dort kommt das Seitenweise ins Spiel.

In diesem Fall wären die Ergebnisse zwar nicht mehr ganz "live", da sie eben vorher abgeholt werden, aber spielt das für diesen Anwendungsfall eine Rolle?
Dafür gewinnst du an Übersicht im Programm und schnelleres Anzeigen in der Auswertung ist auch dabei, da die Daten nur mehr aus der DB geholt werden müssen.

Trenne als die einzelnen Anliegen und Zuständigkeiten auf! Dann ergibt sich eine einfache und überschaubare Architektur, die sich auch umsetzen lässt und i.d.R. weniger Probleme bereitet.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

M
micha0827 Themenstarter:in
85 Beiträge seit 2015
vor 6 Jahren

Hallo Gü,

ich mache das seitenweise weil ich von der API die ersten 1000 Ergebnisse haben möchte aber nur 100 auf einmal bekomme. Ich muss also abfragen url?Page=1 .... url?Page=2 usw.

Momentan verzweifle ich an JSON .... Die Deserialisierung hängt sich immer auf als ob der String nicht komplett ist.


string content = await httpClient.GetStringAsync(url + (page + 1)).ConfigureAwait(false);
 Rootobject listingitem = Task.Run(() => JsonConvert.DeserializeObject<Rootobject>(content)).Result;

Michael

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo micha0827,

ganz schlau werde ich daraus immer noch nicht 😉
Hast du meinen Hinweis mit dem Vorabspeichern in der Db gesehen? Und ginge das?

Um welche Art von API geht es hier überhaupt? Da du nur 100 aufeinmal bekommst, kommt mir das es ein wenig komisch vor. Od. anders gefragt: erlauben die AGB dieser API einen solchen Zugriff wie du es vorhast?

Die Deserialisierung hängt sich immer auf als ob der String nicht komplett ist.

Dazu können wir mangels Kenntnis von diesem String nichts sagen.
Du kannst dir aber den String im Debugger anschauen und dann feststellen ob er ein gültiges / valides Json darstellt.

Task.Run(() => JsonConvert.DeserializeObject<Rootobject>(content)).Result;  

Das ist -- sorry to say -- Müll. Einen Task starten, nur um dann zu Warten bis er fertig ist. Lass es einfach synchron laufen, das ist außerdem eh CPU-lastig und da gelten wieder andere Gesetze als bei asynchronen IO-Vorgängen.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

16.835 Beiträge seit 2008
vor 6 Jahren

Es ist normal, dass APIs (besonders Hypermedia APIs) eine Paginierung haben. Das ist ein Schutzmechanismus.
Gibt genug APIs, die nur 10 Einträge pro Call zurück liefern; manche akzeptieren auch Skip und Take als Parameter mit einem variablen, größeren Range.

So halte ich mich i.d.R. immer an REST und gebe via Skip und Take die Möglichkeit die Chunkgröße selbst zu definieren.
Aber Take wird bei mir immer limitiert, sodass ich zB. egal was der Caller übergibt, ich nur 100 Resultate zurück gebe.
Ähnlich macht es auch OData.

Eine gute (Hypermedia) API gibt auch zusätzlich bei einer Collection-Anfrage immer mit, was und wieviele Elemente überhaupt abgerufen werden können.
So sieht mein Standardmodell für den Return i.d.R. so aus:


{
  Metadata: {
      Skip: 0, // Elemente zu überspringen
      Take: 100, // Angeforderte Anzahl von Elementen. Wenn der Client 5555 anfragt, ich aber nur Max 100/Request zulasse, dann steht hier 100.
      Total: 4364, // Total verfügbare Elemente. Notwendig, dass der Client selbst eine ordentliche Paginierung mit Seitenzahlen aufbauen kann.
      Count: 100 // Tatsächlich zurückgelieferte Elemente. Wenn Take 100 ist, aber Total nur 70, dann kann Count auch nur 70 sein. Count = Value.Count().
  },
  Value: [{ <payload here > }]
}


Wenn Dein Json-String ungültig ist, dann gibt es eine Exception.
Da Du hier aber Task.Run völlig missbräuchlich verwendest, wird die Exception verschluckt und .Result endet mit Pech im Deadlock.

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo Abt,

danke für die Erklärung, das macht schon so Sinn.
Da merke ich wieder, dass ich nur im Intranet zu Hause bin 😉

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"