Laden...

IIS 6: jeweils ein Worker Process pro Request

Erstellt von brainwave vor 10 Jahren Letzter Beitrag vor 10 Jahren 1.769 Views
brainwave Themenstarter:in
436 Beiträge seit 2007
vor 10 Jahren
IIS 6: jeweils ein Worker Process pro Request

Hallo zusammen,

ist es möglich den IIS 6.0 (Application Pool) so einzustellen, dass er jeweils genau ein Worker Process pro Reuest erzeugt?

Aktuell werden die ersten zwei Requests in einem Prozess verarbeitet und erst ab der dritten Anfrage ein neuer Prozess gestartet. Das Problem ist, dass mein WebService bzw. die Komponenten davon nicht MultiThread fähig sind und gezwungenermaßen
in einem Prozess laufen müssen.

16.806 Beiträge seit 2008
vor 10 Jahren

Worker Prozesse haben nichts mit dem Request zutun.
Jeder Request wird über den ThreadPool abgehandelt; das auf einen Thread zu beschränken geht nicht.

Pro Application Pool wird ein Worker Prozess gestartet. Man sollte auch tunlichst vermeiden, dass mehr als ein Worker Prozess pro Anwendung gestartet wird, da ansonsten das Recycling und Co die anderen Instanzen beeinflussen könnte.
Das ist noch ein Relikt aus der Urzeit des IIS.
Das heisst aber auch als Best Practise gleichzeitig, dass jede Anwendung ihren eigenen AppPool haben sollte, sodass diese sich nicht gegenseitig in die Quere kommen (zB Default AppPool Stop after n Unhandled Exceptions).

Ergo:
Jede Anwendung = ein Prozess
Jeder Request = ein Thread
Prozess != Thread

brainwave Themenstarter:in
436 Beiträge seit 2007
vor 10 Jahren

Dh. jede Anfrage wird nur in einem Prozess mit n Threads durchgeführt? So wie du es
beschreibst gibt es keine Möglichkeit einen Request in einem "Isolation Mode" auszuführen, stimmts? In meinem Fall ist es nur wichtig, dass jede Anfrage, die vom Client kommt, auf den Server in jeweils einem Prozess (und damit meine ich keine n Threads) verarbeitet wird. Irgend ne Chance?

16.806 Beiträge seit 2008
vor 10 Jahren

In How IIS Process ASP.NET Request wird die Arbeit ganz gut erklärt.
Für Webservices ist es (fast) identisch.

In IIS 5.0 gabs eigene Isolation Modes (Low, Medium, High).

Wenn man heutzutage von Isolation redet, dann meint man eigentlich das AppPool Prinzip; dass eben jede Anwendung in ihrem eigenen AppPool und damit eigenen Prozess läuft (ab IIS 6.0). Dabei hat jeder AppPool auch seine eigenen Kernel Queue über die HTTP.sys (dedicated).
Jeder Request einer Anwendung erfolgt dann eben als eigener Thread wie gesagt.

Ich kenne jetzt auf Anhieb keine Möglichkeit jeden Request in einem eigenen Prozess laufen zu lassen; vor allem ist das unfassbar unperformant, da jedes mal die Intialisierung von Queue und Cache und Co erfolgen muss.
Da biste sicher bei 5-20 Sekunden Overhead je Request.

brainwave Themenstarter:in
436 Beiträge seit 2007
vor 10 Jahren

Danke, die Seite habe ich mir vorhin schon durchgelesen. Das Ganze leuchtet mir mehr oder weniger ein nur verstehe ich nicht warum es hierfür kein Konstrukt gibt.

Das ist jetzt für mich ein großes Problem. Ich habe leider auch keine Möglichkeit den WebService so zu verändern, dass es läuft. Liegt an bestimmte COM Komponenten,
die ich verwende und diese sind eben nicht MultiThread fähig X(

brainwave Themenstarter:in
436 Beiträge seit 2007
vor 10 Jahren

Ich habe auf diese Seite http://www.west-wind.com/presentations/howaspnetworks/howaspnetworks.asp einen interessanten Hinweis gelesen:

Threads are served from the .NET ThreadPool and by default are Multithreaded Apartment (MTA) style threads. You can override this apartment state in ASP.NET pages with the ASPCOMPAT="true" attribute in the @Page directive. ASPCOMPAT is meant to provide COM components a safe environment to run in and ASPCOMPAT uses special Single Threaded Apartment (STA) threads to service those requests. STA threads are set aside and pooled separately as they require special handling.

Eventuell hilft es mir den Apartment State auf STA umzustellen.

849 Beiträge seit 2006
vor 10 Jahren

Hallo,

ich hatte auch einmal so eine Com Komponente die nur genau 1 x laufen durfte, da sie sonst Probleme bereitete. Ich bin dann so verblieben, das ich den WebService nur als Gateway benutzt habe, der dann seinerseits einen self hosted WCF service auf dem Server angesprochen hat wo diese Com Componente genau 1x lief. (und es auch keinen App Pool Reset gab)

Gruß

16.806 Beiträge seit 2008
vor 10 Jahren

...nur verstehe ich nicht warum es hierfür kein Konstrukt gibt.

Weil die komplette Kommunikation im Web auf dem Prinzip der Parallelität basiert.
Wenn Request A lange brauchen würde, könntest Du den kompletten Server zum Stillstand bringen.

742 Beiträge seit 2005
vor 10 Jahren

Du kannst doch auch einfach eine interne Queue verwenden und alle Requests zu dieser Komponente Single-Threaded abarbeiten:

Beispielweise mit der TPL und einem eigenen Scheduler: http://msdn.microsoft.com/en-us/library/ee789351%28v=vs.110%29.aspx

Falls du für jeden Request auf die Komponente zugreift hast du aber ein Problem, das skaliert dann praktisch gar nicht, aber wenn du wenig Benutzer hast oder die Komponente selten brauchst, sollte das schon passen.

brainwave Themenstarter:in
436 Beiträge seit 2007
vor 10 Jahren

unconnecteds Ansatz scheint meine einizige Option sein. Der WebService leitet die Anfrage an einem Prozess weiter. Ist zwar eine etwas größere Umstelleuung notwendig aber mir bleibt da leider nichts anderes übrig. So wie ich das TPL Konzept verstanden habe, wird hier mit "locks" gearbeitet. Dh. der COM-Objekt Zugriff ist exklusiv und sequenziell. Klar, als zu performant ist dieses Konzept nicht..

16.806 Beiträge seit 2008
vor 10 Jahren

Ist jetzt aber prinzipiell kein Unterschied ob Du den Ansatz von unconnected oder malignate verwendest.
Ich würde wohl zu malignates tendieren, um Overhead der Kommunikation zu vermeiden.

brainwave Themenstarter:in
436 Beiträge seit 2007
vor 10 Jahren

Ich habe mich für die folgende Lösung entschieden:

Der Client ruft WebMethode "GetMessage()" auf. GetMessage() packt den Input + Methodenname in den SharedMemory und ruft WorkerProcess.exe zum verarbeiten auf. WorkerProcess referenziert die WebService.dll. WorkerProess liest den Input aus dem SharedMemory und führt mittels Reflection die eigentliche Methode "GetMessageProcess()" auf. Der Output wird in den SharedMemory gepackt und an "GetMessage()" signalisiert, dass sie fertig ist. "GetMessage()" holt den Output aus dem Speicher und liefert das Ergebnis an den Client. D.h. jede Anfrage, die der Client stellt wird in einem seperaten Prozess abgearbeitet. Natürlich müssen die Namespaces eindeutig sein. Unschön finde ich allerdings, dass ich zu jeder WebMethode noch die eigentliche Methode benötige. Schöne wäre es alles in einer WebMethode zu packen.

WebService:


[WebMethod]
public string GetMessage(object parameter)
{
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = @"WorkerProcess.exe";
object input = new object[] { "GetMessageProcess", parameter };
object output = null;
 {
                        int InvalidHandleValue = -1;
                        IntPtr nativeHandle = SharedMemory.NativeMethods.CreateFileMapping((IntPtr)InvalidHandleValue, IntPtr.Zero, (int)SharedMemory.MapProtections.PageReadWrite, 0, SharedMemory.SharedMemory.GetOptMemorySize(input), "Local\\MapIn");
                        IntPtr nativePointer = SharedMemory.NativeMethods.MapViewOfFile(nativeHandle, (int)SharedMemory.MapAccess.FileMapAllAccess, 0, 0, IntPtr.Zero);
                        long marshalledSize = SharedMemory.SharedMemory.GetMinMemorySize(input);
                        MemoryStream ms = new MemoryStream();
                        BinaryFormatter formatter = new BinaryFormatter();
                        BinaryWriter bw = new BinaryWriter(ms);
                        bw.Write(marshalledSize);
                        formatter.Serialize(ms, input);
                        ms.Seek(0, SeekOrigin.Begin);
                        BinaryReader reader = new BinaryReader(ms);
                        byte[] data = reader.ReadBytes((int)ms.Length);
                        Marshal.Copy(data, 0, nativePointer, (int)ms.Length);
                    }

                    EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.AutoReset, "MyWaitHandle");
                    proc.Start();
                    System.Threading.Thread.Sleep(0);
                    wh.WaitOne();
                    {
                        IntPtr nativeHandle = SharedMemory.NativeMethods.OpenFileMapping((int)SharedMemory.MapAccess.FileMapAllAccess, true, "Local\\MapOut");
                        IntPtr nativePointer = SharedMemory.NativeMethods.MapViewOfFile(nativeHandle, (int)SharedMemory.MapAccess.FileMapAllAccess, 0, 0, IntPtr.Zero);
                        MemoryStream ms = new MemoryStream();
                        long objLength = (long)Marshal.ReadIntPtr(nativePointer);
                        IntPtr source = (IntPtr)((long)nativePointer + sizeof(long));
                        byte[] data = new byte[objLength];
                        Marshal.Copy(source, data, 0, (int)objLength);
                        BinaryWriter writer = new BinaryWriter(ms);
                        writer.Write(data);
                        ms.Seek(0, SeekOrigin.Begin);
                        BinaryFormatter bf = new BinaryFormatter();
                        output = bf.Deserialize(ms);
                        SharedMemory.NativeMethods.UnmapViewOfFile(nativePointer);
                        SharedMemory.NativeMethods.CloseHandle(nativeHandle);
                    }

                    wh.Set();
                    wh.Close();
return output;
}

public string GetMessageProcess(object parameter)
{
   return parameter + "Hallo Welt!");
}

WorkerProcess.exe:


static void Main()
        {
            EventWaitHandle wh = EventWaitHandle.OpenExisting("MyWaitHandle");

            object[] input = null;

            {
                IntPtr nativeHandle = SharedMemory.NativeMethods.OpenFileMapping((int)SharedMemory.MapAccess.FileMapAllAccess, true, "Local\\MapIn");
                IntPtr nativePointer = SharedMemory.NativeMethods.MapViewOfFile(nativeHandle, (int)SharedMemory.MapAccess.FileMapAllAccess, 0, 0, IntPtr.Zero);
                MemoryStream ms = new MemoryStream();
                long objLength = (long)Marshal.ReadIntPtr(nativePointer);
                IntPtr source = (IntPtr)((long)nativePointer + sizeof(long));
                byte[] data = new byte[objLength];
                Marshal.Copy(source, data, 0, (int)objLength);
                BinaryWriter writer = new BinaryWriter(ms);
                writer.Write(data);
                ms.Seek(0, SeekOrigin.Begin);
                BinaryFormatter bf = new BinaryFormatter();
                input = (object[])bf.Deserialize(ms);
                SharedMemory.NativeMethods.UnmapViewOfFile(nativePointer);
                SharedMemory.NativeMethods.CloseHandle(nativeHandle);
            }

            string methodName = (string)input[0];
            object[] parameters = new object[input.Length - 1];
            Array.Copy(input, 1, parameters, 0, input.Length - 1);

            MyWebServiceGate service = new MyWebService.MyWebServiceGate();
            object output = service.GetType().GetMethod(methodName).Invoke(service, parameters);

            {
                int InvalidHandleValue = -1;
                IntPtr nativeHandle = SharedMemory.NativeMethods.CreateFileMapping((IntPtr)InvalidHandleValue, IntPtr.Zero, (int)SharedMemory.MapProtections.PageReadWrite, 0, SharedMemory.SharedMemory.GetOptMemorySize(output), "Local\\MapOut");
                IntPtr nativePointer = SharedMemory.NativeMethods.MapViewOfFile(nativeHandle, (int)SharedMemory.MapAccess.FileMapAllAccess, 0, 0, IntPtr.Zero);
                long marshalledSize = SharedMemory.SharedMemory.GetMinMemorySize(output);
                MemoryStream ms = new MemoryStream();
                BinaryFormatter formatter = new BinaryFormatter();
                BinaryWriter bw = new BinaryWriter(ms);
                bw.Write(marshalledSize);
                formatter.Serialize(ms, output);
                ms.Seek(0, SeekOrigin.Begin);
                BinaryReader reader = new BinaryReader(ms);
                byte[] data = reader.ReadBytes((int)ms.Length);
                Marshal.Copy(data, 0, nativePointer, (int)ms.Length);
            }

            wh.Set();
            wh.WaitOne();
            wh.Close();
        }