Fragensteller
IIS verarbeitet nur einen Request gleichzeitig

Allgemeine Diskussion
-
Hallo Zusammen,
ich habe folgendes Problem und dafür auch mehrere Lösungsansätze gefunden, doch keiner scheint zu funktionieren...
Server: Windows 2008 R2
Ich habe eine Webseite, die Daten per SQL Daten abruft (dauert ca. 25 Sekunden da die SQL-Prozedur noch Berechnungen durchführt) und daraus ein Chart mit mehreren Series erstellt (dauert auch noch mal ein paar Sekunden). Insgesamt dauert der Vorgang so an die 30 Sekunden.
Das Problem: Während dieser Request verarbeitet wird, reagiert der Server auf keinen neuen Request in einem anderen Tab/Browser auf dem gleichen/einem anderen Client. Erst wenn dieser Request abgearbeitet wurde, wird der neue Request verarbeitet.
Zur web.config der Application:
<compilation debug="false" defaultLanguage="c#" targetFramework="4.0" />
In der machine.config (in Framework und Framework64 für .NET 4 da es eine .NET 4.0 Anwendung ist) steht nun (diese Einstellung hatte ich vorher nicht, bin heute drüber gestolpert):
<deployment retail="true"/>
In den IIS ASP Einstellungen (sofern das überhaupt relevant für eine ASP.NET Applikation ist):
Der Application-Pool der die Application hosted läuft mit:
- .NET Framework Version 4
- Verwalteter Pipelinemodus: Integriert
- Identität: ApplicationPoolIdentity
- Maximale Anzahl von Arbeitsprozessen: 4 (habe den Wert hochgesetzt, brachte aber keinen Erfolg)
- Benutzerprofil laden: False
- Schutz für schnelle Fehler Aktiviert: False
- ...der Rest scheint Standard zu sein
Für die Sitzung verwende ich den lokale ASP.NET Zustandsdienst, demnach in der web.config:
<sessionState mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" stateNetworkTimeout="40" cookieless="UseCookies" />
Warum verarbeitet der IIS nur einen Request gleichzeitig?
Gruß
asymetrixs- Typ geändert Stefan FalzModerator Dienstag, 11. März 2014 20:22 Thread ohne bestätigte Lösung abgeschlossen
Alle Antworten
-
Hi,
Ich habe eine Webseite, die Daten per SQL Daten abruft (dauert ca. 25 Sekunden da die SQL-Prozedur noch Berechnungen durchführt) und daraus ein Chart mit mehreren Series erstellt (dauert auch noch mal ein paar Sekunden). Insgesamt dauert der Vorgang so an die 30 Sekunden.
Das Problem: Während dieser Request verarbeitet wird, reagiert der Server auf keinen neuen Request in einem anderen Tab/Browser auf dem gleichen/einem anderen Client. Erst wenn dieser Request abgearbeitet wurde, wird der neue Request verarbeitet.
ich kann das Verhalten eigentlich nur mit statischen Objekten (Shared bzw. static) oder eigens gebauten Locks nachvollziehen.
Zeig doch mal bitte den relevanten Code und insbesondere die Deklaration der Connection und Command Objekte.
Generell wäre noch wichtig zu wissen, ob eine reine Dummyseite (also eine Seite, die nichts mit der Datenbank oder einer sonstigen Resource macht, sondern bspw. nur Hallo Welt ausgibt) auch "hängt". Falls ja, leg die Dummyseite anstelle von Dummy.aspx auch mal aus Dummy.html an und schau, ob es dann immer noch auftritt.
Die Anwendung selbst hast Du ja als Release kompiliert, bevor Du sie auf den Server geladen hast? (Ist unabhängig von der web.config Einstellung)
Die "maximal Anzahl an Arbeitsprozessen" bitte wieder auf 1 setzen. Webgärten willst Du nicht, glaubs mir :)
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community -
Hi,
also ich baue das Ganze über eine .bat Datei in csproj-Ordner wie folgt:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe /target:APDeployFull /p:Configuration=Release
und in der csproj dann im Target "APDeployFull"<MSBuild Projects="Main.Administration.csproj" Targets="Clean;Build" StopOnFirstFailure="true" />
Der Abschnitt in dem ich die Daten von der Datenbank abrufe sieht so aus:
List<System.Threading.Tasks.Task> collectData = new List<System.Threading.Tasks.Task>(); for (int i = 0; i < endpoints.Count; i++) { if (showCalls || showBilltime) { if (ChartScale.Hour == chartScale) { collectData.Add(System.Threading.Tasks.Task.Factory.StartNew(new Action(endpoints.ElementAt(i).GetCallsAndBilltimeByHour))); } else if (ChartScale.Day == chartScale) { collectData.Add(System.Threading.Tasks.Task.Factory.StartNew(new Action(endpoints.ElementAt(i).GetCallsAndBilltimeByDay))); } else if (ChartScale.Month == chartScale) { collectData.Add(System.Threading.Tasks.Task.Factory.StartNew(new Action(endpoints.ElementAt(i).GetCallsAndBilltimeByMonth))); } else // Year { collectData.Add(System.Threading.Tasks.Task.Factory.StartNew(new Action(endpoints.ElementAt(i).GetCallsAndBilltimeByYear))); } } if (showConcurrent) { collectData.Add(System.Threading.Tasks.Task.Factory.StartNew(new Action(endpoints.ElementAt(i).GetConcurrentCalls))); } } // Collect values in parallel System.Threading.Tasks.Task.WaitAll(collectData.ToArray());
Die Funktion, die dann letztlich während des Abholens aufgerufen wird, sieht exemplarisch so aus (die anderen wie GetConcurrentCalls, etc rufen Funktionen auf die genauso aussehen, nur eine andere Prozedur aufrufen):und zumindest die Funktion ist static, könnte dies der Grund sein?internal static LinkedList<KeyValuePair<DateTime, object[]>> GetAdmCallsAndBilltimeByDay(DateTime dateStart, DateTime dateEnd, Enums.Accumulation accumulation, Enums.Endpoint endpoint) { LinkedList<KeyValuePair<DateTime, object[]>> values = new LinkedList<KeyValuePair<DateTime, object[]>>(); using (Npgsql.NpgsqlConnection connection = new Npgsql.NpgsqlConnection(ConfigurationManager.ConnectionStrings["voipDirectConnection"].ConnectionString)) { using (Npgsql.NpgsqlCommand command = new Npgsql.NpgsqlCommand("web_adm_calls_and_billtime_by_day", connection)) { command.CommandType = System.Data.CommandType.StoredProcedure; command.CommandTimeout = 120; try { command.Parameters.Add("p_date_start", NpgsqlTypes.NpgsqlDbType.TimestampTZ).Value = dateStart.ToUniversalTime(); command.Parameters.Add("p_date_end", NpgsqlTypes.NpgsqlDbType.TimestampTZ).Value = dateEnd.ToUniversalTime(); command.Parameters.Add("p_accumulation", NpgsqlTypes.NpgsqlDbType.Varchar).Value = accumulation.GetName(); command.Parameters.Add("p_endpoint", NpgsqlTypes.NpgsqlDbType.Varchar).Value = endpoint.GetName(); connection.Open(); using (Npgsql.NpgsqlDataReader dr = command.ExecuteReader()) { while (dr.Read()) { TimeSpan billtime; int calls = 0; calls = (int)dr.GetInt64(0); billtime = new TimeSpan(dr.GetInterval(1).Ticks); DateTime key = new DateTime(dr.GetDate(2).Year, dr.GetDate(2).Month, dr.GetDate(2).Day); values.AddLast(new KeyValuePair<DateTime, object[]>(key, new object[] { billtime, calls })); } } } catch (Exception e) { Logger.Error(e, typeof(Node)); throw e; } finally { connection.Close(); } } } return values; }
Nachdem ich alle Werte abgeholt habe, befülle ich dann die Charts
// Populate Series collectData.Clear(); foreach (var item in populations) { collectData.Add(System.Threading.Tasks.Task.Factory.StartNew(new Action(item.Do))); } System.Threading.Tasks.Task.WaitAll(collectData.ToArray());
was beispielsweise so aussieht:private void DoHour() { LinkedList<KeyValuePair<int, object[]>> callsAndBilltimeValues = this._endpoint.Hours; // check if values from 0 to 23 are specified or add if missing for (int i = 0; i < 24; i++) { try { callsAndBilltimeValues.Where(ca => ca.Key == i).Single(); } catch { object[] o = new object[2] { 0, 0 }; KeyValuePair<int, object[]> newKvp = new KeyValuePair<int, object[]>(i, o); callsAndBilltimeValues.AddLast(newKvp); } } using (IEnumerator<KeyValuePair<int, object[]>> e = callsAndBilltimeValues.OrderBy(ca => ca.Key).GetEnumerator()) { while (e.MoveNext()) { string hour = e.Current.Key.ToString(); TimeSpan? billTime = e.Current.Value[0] as TimeSpan?; int totalCalls = (int)e.Current.Value[1]; if (SeriesType.BillTime == this._type) { if (billTime.HasValue) { this._series.Points.AddXY(hour, Math.Round(billTime.Value.TotalHours)); } else { this._series.Points.AddXY(hour, 0); } } else if (SeriesType.Calls == this._type) { this._series.Points.AddXY(hour, totalCalls); } } } }
Habe überlegt, das Ganze mal mit einem AsyncController auszuprobieren, aber letztlich will ich die Datenbankabfragen schon parallel laufen lassen, da die nacheinander wirklich lange brauchen (Bitte keine Vorschläge zur Optimierung des SQLs ;-))Gruß
asymetrixs -
Ich habe es nun mit folgender Dummyseite probiert:
Ein Controller, der diese beiden Methoden besitzt:
Beim Aufruf von Test2 erscheint erwartungsgemäß quasi sofort der String "second" im Browser. Beim Aufruf von Test?sec=X mit der Angabe von 5 erscheint "first: 5" nach 5 Sekunden. Rufe ich zuerst Test?sec=5 und direkt danach Test2 auf, so erscheint zuerst das Ergebnis von Test und erst danach das von Test2. Der Webserver verarbeitet die Requests somit nacheinander. Warum?public string Test(int sec) { System.Threading.Thread.Sleep(sec * 1000); return "first: " + sec.ToString(); } public string Test2() { return "second"; }