none
IIS verarbeitet nur einen Request gleichzeitig RRS feed

  • 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
    Dienstag, 24. Januar 2012 00:48

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
    Dienstag, 24. Januar 2012 07:58
    Moderator
  • 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):

    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;
            }
    
    und zumindest die Funktion ist static, könnte dies der Grund sein?

    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 

    Dienstag, 24. Januar 2012 13:48
  • Ich habe es nun mit folgender Dummyseite probiert:

    Ein Controller, der diese beiden Methoden besitzt:

    public string Test(int sec)
            {
                System.Threading.Thread.Sleep(sec * 1000);
    
                return "first: " + sec.ToString();
            }
    
            public string Test2()
            {
                return "second";
            }
    
    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?

    Mittwoch, 25. Januar 2012 00:57
  • Gibt es was neues hierzu? Ideen?
    Donnerstag, 26. Januar 2012 13:39