Laden...

[WCF] Callback-Channel bricht zusammen / Debuggen des Callbacks

Erstellt von emuuu vor 6 Jahren Letzter Beitrag vor 6 Jahren 2.079 Views
emuuu Themenstarter:in
286 Beiträge seit 2011
vor 6 Jahren
[WCF] Callback-Channel bricht zusammen / Debuggen des Callbacks

Guten Abend zusammen,

ich habe ein kleines Problem, bei dem ich vor allem nicht weiterkomme, weil ich nicht weiß wie ich das vernünftig debuggen kann:

Ich habe einen WCF-Service über den der Benutzer Daten abrufen/aktualisieren kann. Da das ganze Multiuser-fähig sein soll verwende ich Callbacks. Um das ganze modularer zu halten habe ich dafür einen weiteren Service der die entsprechenden Callbacks verabeitet.

Alle User nutzen den Service_1 (InstanceContextMode.PerCall) sowie den Service_2 (InstanceContextMode.Single)
Service_1 kennt Service_2 und ruft diesen auf, wenn ein Datensatz aktualisiert wurde.
Service_2 verteilt dann an alle aktuell registrierten Clients den Callback (in dem nur die ID des aktualisierten Datensatzes enthalten ist).
Der CallbackClient beim User empfängt diesen und feuert ein Event, dass wiederum Service_1 triggert den aktualisierten Datensatz abzufragen.

Ich hoffe das Konstrukt ist einigermaßen verständlich wiedergegeben.

Beispielhaft läuft das dann so:


//User löst Update aus
        private void DoUpdate()
        {
            orderService.UpdateOrder(activeOrder);
        }


//OrderService verarbeitet Update und ruft UpdateService
        public void UpdateOrder(Order activeOrder)
        {
             //DB aktualisieren usw.

             updateService.SendOrderUpdate(activeOrder.OrderID);
        }


//UpdateService verteilt das Update
        public void SendOrderUpdate(string orderID)
        {
                _publishInProgress.Reset();
                List<Subscriber> listeners;
                lock (_locker)
                {
                    var toRemove = _subscribers.FindAll(a => ((ICommunicationObject)a.Channel).State != CommunicationState.Opened);
                toRemove.ForEach(a => _subscribers.Remove(a));
                    listeners = _subscribers;
                }

                ThreadPool.QueueUserWorkItem(_ =>
                {
                    Parallel.ForEach(listeners, subscriber =>
                    {
                        try { subscriber.Channel.UpdatedOrder(orderID); }
                        catch (Exception e) { _onError("Failed to send data to {0}. {1}", new object[] { subscriber.Name, e }); }
                    });
             _publishInProgress.Set();
                });
        }


//User empfängt Update
        public void UpdatedOrder(string updateOrderID)
        {
            SendOrPostCallback callback =
                delegate
                {
                     OrderUpdated(this, new ObjectUpdatedEventArgs(updateOrderID));
                };

            _uiSyncContext.Send(callback, null);
        }

Das Event löst dann wieder die Abfrage nach dem aktualisierten Datensatz aus.

Das funktioniert auch soweit gut, nur das Problem ist, dass irgendwann die Updates nicht mehr zurückkommen. Der Updatevorgang selbst funktioniert immer, da der Channel vor jedem Aufruf neu kreiert wird.

Nur bleiben irgendwann die Updates aus. Und hier setzt das Problem an: Ich habe keine Ahnung was/wann passiert. Zumal das ganze "zufällig" auftritt.
Ich habe für die ChannelFactory des CallbackClient sämtliche Events (Faulted, Closing, Closed) abonniert und hinterlegt, dass in meine debug-Datei einfach nur geschrieben wird wann bei welchem Client, bei welchem Update der Channel flöten geht: Kein Ergebnis, keines der Events wird je getriggert.

Ich hab nun leider keine Idee wo die Schwachstelle/der Fehler sein könnte. Bzw. mit welchem Mitteln ich dem auf die Schliche kommen kann.

Vielleicht hat ja von euch jemand einen Ansatz.

Besten Dank im voraus 👍
Timm

2+2=5( (für extrem große Werte von 2)

T
461 Beiträge seit 2013
vor 6 Jahren

Hmm, irr ich mich oder muß der gesamte Inhalt gelocked werden?:


//UpdateService verteilt das Update
        public void SendOrderUpdate(string orderID)
        {
                lock (_locker)
                {
                _publishInProgress.Reset();
                List<Subscriber> listeners;
                    var toRemove = _subscribers.FindAll(a => ((ICommunicationObject)a.Channel).State != CommunicationState.Opened);
                toRemove.ForEach(a => _subscribers.Remove(a));
                    listeners = _subscribers;

                ThreadPool.QueueUserWorkItem(_ =>
                {
                    Parallel.ForEach(listeners, subscriber =>
                    {
                        try { subscriber.Channel.UpdatedOrder(orderID); }
                        catch (Exception e) { _onError("Failed to send data to {0}. {1}", new object[] { subscriber.Name, e }); }
                    });
             _publishInProgress.Set();
                });
            }
       }    

da ja auf subscribers auch im Thread-Pool darauf zugegriffen wird.

Jetzt wird gerade der Pool abgearbeitet, wärend dessen kommt eine neue Anfrage über 'SendOderUpdate' herein und entfernt gleichzeitig einen Eintrag aus der Liste...
Dasselbe wegen '_publishInProgress.'...

Könnte mir vorstellen, daß er da durcheinander kommt...

Ich habe den Titel mal angepasst, so dass Suchende auch etwas damit anfangen können. EDIT: Ich sollte beim Wort "Shift" im Titel das "f" nicht vergessen... 😄

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo emuuu,

kennst du WCF-Tracing? Wenn nicht schau dir das einmal ein, denn da schreibt der Service-Stack selbst seine Fehler (entsprechende Konfiguration vorausgesetzt) und dort steht eine Menge drin, v.a. wenn der Trace-Level weit unten (z.B. ActivityTracing) ist. So solltest du den ursprünglichen Fehler ermitteln können.

BTW: für den "Broadcast" würde ich auch das Bordmittel nutzen -> UDP-Broadcast, anstatt das selbst zu implementieren. Dadurch kannst du den Fehler schon von vornherein vermeiden.
Od. -- je nach Art der Clients -- auch SignalR nutzen und dort gleich den neuen Datensatz mitübergeben, denn das spart einen Roundtrip zum Server.

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.825 Beiträge seit 2008
vor 6 Jahren

Warum ein ThreadPoolItem um das Paralell.For?
Was ist _publishInProgress ?

emuuu Themenstarter:in
286 Beiträge seit 2011
vor 6 Jahren
        private readonly AutoResetEvent _publishInProgress = new AutoResetEvent(true);

Das ThreadPoolItem ist mmn nötig um das Multithreading korrekt abzuarbeiten (da es vom UpdateService ja nur eine Instanz gibt auf die alle zugreifen), oder irre ich da?

@gfoidl
Das Wcf-Tracing habe ich schon laufen:


   <system.diagnostics>  
      <sources>  
            <source name="System.ServiceModel"   
                    switchValue="Information, ActivityTracing"  
                    propagateActivity="true">  
            <listeners>  
               <add name="traceListener"   
                   type="System.Diagnostics.XmlWriterTraceListener"   
                   initializeData= "\\server-ob\Debug\innerServiceTraces.svclog" />  
            </listeners>  
         </source>  
      </sources>  
   </system.diagnostics>  

Allerdings finde ich dort keine Ausnahmen o.ä. die mit dem UpdateService in Verbindung stehen.

2+2=5( (für extrem große Werte von 2)

16.825 Beiträge seit 2008
vor 6 Jahren

Das kommt auf Dein ServiceBehavior an.
Wenn Du ConcurrencyMode auf Multiple setzt, gibt es pro Call/Request ein Thread.
Macht in der Form aber nur sinn, wenn der InstanceMode auf Sinlge steht (was ein Singleton darstellt).

Oder eben InstanceMode auf PerCall, dann gibt es pro Call/Request ein eigenständiges Serviceobjekt.

emuuu Themenstarter:in
286 Beiträge seit 2011
vor 6 Jahren

Alle User nutzen den Service_1 (InstanceContextMode.PerCall) sowie den Service_2 (InstanceContextMode.Single)

OrderService wird mit jedem Aufruf neu instantiiert, UpdateService ist Singleton (was auch meines Erachtens keinen Sinn macht, denn woher sollten die anderen Clients die den Callback empfangen sollen von der jeweiligen Instanz wissen).

Edit:


    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single, AutomaticSessionShutdown = false)]


Ist der UpdateService, mir ist gerade auch aufgefallen, dass der ConcurrencyMode dort noch auf Multiple war.

2+2=5( (für extrem große Werte von 2)

16.825 Beiträge seit 2008
vor 6 Jahren

Multiple ist auch korrekt, wenn Du eine Separierung der Threads willst (was Du hier offensichtlich willst).