Laden...

WCF: Singleton richtige Wahl?

Erstellt von Inso vor 13 Jahren Letzter Beitrag vor 13 Jahren 5.099 Views
I
Inso Themenstarter:in
19 Beiträge seit 2010
vor 13 Jahren
WCF: Singleton richtige Wahl?

Hallo,

ich bin ein absoluter Anfänger was WCF angeht.

So sieht mein WCF Host/Client momentan aus:

Der Host läuft auf einem Server, der seine Dienste dem kompletten Intranet bereitstellt.
Client wird (momentan) noch von mehreren Arbeitsplätzen gestartet.

  • Client stößt einen Job auf dem Host an, dieser führt dann eine zeitintensive Arbeit durch (Sql Abfragen etc.).
  • Host sendet regelmäßig seinen Status (in %) an Client(Callbackcontract) und an eine Jobtabelle auf einem SQL Server zurück.
    Als InstanceContextMode ist Single gesetzt, weil Job für Job abgearbeitet werden soll und nicht mehrere auf einmal.

Problem1:
Wenn mehrere Clients einen Job starten, reihen sich diese in die Warteschleife ein, wenn bereits ein Job aktiv ist. Wenn dieser Job zu lange dauert, brechen die anderen Jobs mit Timeouts ab.

Problem2:
Schließe ich den Client, bricht der gestartete Job beim Host ab, weil er den Status nicht mehr zurücksenden kann(Timeout).

Ich benötige also eine echte Warteschleife, die unabhängig von Timeouts die Jobs aufnimmt und einen nach dem anderen abarbeitet. Geht das überhaupt ohne zusätzliche SQL-Jobtabelle?
Und der Client soll nach Anstoß des Jobs geschlossen werden können, ohne dass der Job vom Host unterbrochen wird.

Wie kann ich das am Besten umsetzen?

Vielen Dank im Voraus für die Antworten

C
2.122 Beiträge seit 2010
vor 13 Jahren

Geht das überhaupt ohne zusätzliche SQL-Jobtabelle?

Warum SQL? Bzw was ist eine SQL Jobtabelle überhaupt?
SQL kann parallel laufen, du brauchst dann für jede Aktion eine separate Verbindung. Fragt sich nur ob das von der Logik her sinnvoll ist, intensive und langdauernde AKtionen parallel zu starten, vielleicht kommen die sich ja massiv in die Quere. Aber das ist dann kein WCF Problem.

Und der Client soll nach Anstoß des Jobs geschlossen werden können, ohne dass der Job vom Host unterbrochen wird.

Dann würd ich das so umbauen dass der Anstoß vom Server gleich wieder zurückgemeldet wird und dieser WCF-Aufruf damit beendet ist. Du könntest dann eine Art Sessionnummer zurückgeben, unter der sich der Client gelegentlich den aktuellen Stand der Verarbeitung abruft. Wenn der Client dann nicht mehr da ist und nachfragt, läuft der Job trotzdem weiter.

Ich hab nicht rausgelesen ob du die Jobs überhaupt wirklich warten lassen willst oder ob die parallel laufen sollen/dürfen. Falls ja, wäre Singleton nicht die richtige Wahl.

I
Inso Themenstarter:in
19 Beiträge seit 2010
vor 13 Jahren

Nein, es soll kein Job parallel laufen, daher ja Singleton.
Die Idee, dass der Client den Status abfragt ist gut, das löst schon mal ein Problem, danke.

Und ja die Jobs sollen warten. Das Problem ist, dass ein einzelner Job mal locker 1 Tag dauern kann. Wie krieg ich es hin, dass die anderen Jobs trotzdem solange warten, bis sie dran sind? Timeout hochsetzen kann ja nicht die Lösung sein.

Mit Jobtabelle ist folgendes gemeint:
Eine Tabelle auf einem SQL Server, wo sich der Client seine Jobs einträgt und der Host Job für Job da abholt und abarbeitet.
Z.B. Ganz simple:

JobName JobParameter1 JobParameter2

Job1 string1 integer1
Job2 string2 integer2
Jobn....

C
2.122 Beiträge seit 2010
vor 13 Jahren

Wie krieg ich es hin, dass die anderen Jobs trotzdem solange warten, bis sie dran sind?

Eben dadurch dass der WCF-Aufruf nur den Job einträgt und dann wieder zurückkehrt. Dann ist die Anfrage beendet und die Verbindung wieder geschlossen. Der Server weiß dann was er tun soll und macht es dann wenn er Zeit hat.

Bist du sicher dass da alles noch nach Plan läuft wenn das 1 Tag dauern kann?

I
Inso Themenstarter:in
19 Beiträge seit 2010
vor 13 Jahren

Ah ok, dann ist das damit schon erledigt.

Ja das läuft alles nach Plan. Es sind Jobs die bis zu 10 Millionen Datensätze aus versch. Tabellen abruft und diese mit einander verknüpft und wieder in eine andere Datenbank schreibt. Das Problem ist, dass je Datensatz auch Geography Daten enthalten sind, die teilweise bis zu 4GB groß sind( ein einzelner Datensatz) und noch durch komplexe Vergleiche und Manipulationen läuft.

Vielen Dank für die Hilfe 😃

699 Beiträge seit 2007
vor 13 Jahren

Hallo Inso,

das könnte vieleicht noch interessant für Dich sein Queue<T>-Klasse.

Grüße Stephan

S
8.746 Beiträge seit 2005
vor 13 Jahren

Du brauchst hier was asynchrones, also was mit "Rückruf", wenn die Batch-Bearbeitung fertig ist. Ich würde dir empfehlen unter der Haube von WCF MSMQ einzusetzen. Am besten kominiert mit "Reliable Messaging". Normales HTTP-Binding ist bei solchen Laufzeiten nicht angesagt. Ggf. Messaging über HTTP.

Da du dann eh asynchron bist kann du dann auch wieder mit PerCall arbeiten. Das skaliert dann auch ordentlich.

I
Inso Themenstarter:in
19 Beiträge seit 2010
vor 13 Jahren

Ok ich habe mein WCF Dienst nun so umgebaut, dass der Client den Job an den Host sendet und dann auf die SessionId wartet.
Irgendwie kommt nur nichts zurück und er läuft dort in den Timeout.
Ich versteh momentan nicht wirklich warum, da das generelle zurücksenden an den Client ja vorher auch ohne Probleme ging. Das ist mein ServiceContract.

  • GetData hat als Callback SendSession
  • GetOverview hat als Callback SendOverview
  • GetStatus " " SendStatus
  • CancelJob " " SendCancelOk

Jede Get-Methode soll direkt die zugehörige Send-Methode aufrufen und danach soll die Verbindung wieder geschlossen werden.
Vielleicht ist ja auch mein kompletter ServiceContract falsch?
Muss ich für jede Get-methode einen neuen machen?


[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(ICallbackGetData))]
	public interface IGetData
	{
		[OperationContract]
		void GetData(string user, int landid, string landcode, int layer, int provider);
		
		[OperationContract]
		void GetOverview();

		[OperationContract]
		void GetStatus(string sessionstring);

		[OperationContract]
		void CancelJob(string sessionstring);
	}


	interface ICallbackGetData
	{
		[OperationContract(IsOneWay = true)]
		void SendSession(string sessionstring);
		
		[OperationContract(IsOneWay = true)]
		void SendStatus(double value, string content);

		[OperationContract(IsOneWay = true)]
		void SendOverview(List<JobStructure.Job> joblist);

		[OperationContract(IsOneWay = true)]
		void SendCancelOk();

	}

Also mein Host kriegt den Job, packt ihn auch in meine Jobliste.
Ruft auch SendSession auf, der Client kriegt aber nie was.
Folgendes ist mir noch aufgefallen, wenn der Host den Job gekriegt hat gibt er auf der Console "State: Open" aus, obwohl ich das nirgends in meinem Projekt ausgebe.

Weiß Jemand wo das herkommt?
Ich weiß nicht warum es gerade nicht funktioniert.
Wie gesagt, den Status zurücksenden oder Ähnliches hat vor dem Umbau ohne probleme geklappt.

Vielleicht liegt das Problem auch hier unten begraben:
Ich würde gerne bei ServiceBehavior, InstanceContextMode komplett weglassen (auf default). Wenn ich das mache krieg ich folgende Fehlermeldung beim host.open()


Um einen der ServiceHost-Konstruktoren zu verwenden, der eine Dienstinstanz annimmt, muss der InstanceContextMode des Diensts auf InstanceContextMode.Single festgelegt sein. Dies kann über das ServiceBehaviorAttribute konfiguriert werden. Andernfalls sollten Sie erwägen, die ServiceHost-Konstruktoren zu verwenden, die ein Type-Argument annehmen.


public class JobAdministration
	{

		[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
		public class GetDataService : IGetData
		{
			public void GetData(string user, int landid, string landcode, int layer, int provider)
			{
                         .....
			}


			public void GetStatus(string sessionstring)
			{
			....
			}

			public void GetOverview()
			{
                        ....
			}

			public void CancelJob(string sessionstring)
			{
			...
			}
		}
	}

Darf ich hier dem ServieHost Konstruktor nicht getDataService mitgeben? Liegt da der Fehler?


JobAdministration.GetDataService getDataService = new JobAdministration.GetDataService();
			jobList = getDataService.JobList;


			ListChecker listChecker = new ListChecker(jobList);
			TimerCallback delegateTimer = new TimerCallback(listChecker.StatusChecker);
			AutoResetEvent autoEvent = new AutoResetEvent(false);

			jobTimer = new Timer(delegateTimer, autoEvent, 2000, 4000);	

			using (ServiceHost host = new ServiceHost(getDataService, new Uri[] {new Uri("http://localhost:8080"),}))
			{

				host.AddServiceEndpoint(typeof (IGetData), new WSDualHttpBinding(), "GetData");
				host.AddServiceEndpoint(typeof (IGetData), new WSDualHttpBinding(), "GetStatus");
				host.AddServiceEndpoint(typeof (IGetData), new WSDualHttpBinding(), "GetOverview");
				host.AddServiceEndpoint(typeof (IGetData), new WSDualHttpBinding(), "CancelJob");

				host.Open();
				
				
				
				Console.WriteLine("Service is available. " + "Press <ENTER> to exit.");


				Console.ReadLine();
				host.Close();
				jobTimer.Dispose();
			}

Was mache ich falsch? Wahrscheinlich viel, aber warum kommt nichts an den Client zurück?

I
Inso Themenstarter:in
19 Beiträge seit 2010
vor 13 Jahren

Hab meinen Fehler gefunden.
Wenn ich IGetData sämtliche OperationContracts auch IsOneWay auf true setze klappts.
hat sich erledigt


public interface IGetData
	{
		[OperationContract(IsOneWay = true)]
		void GetData(string user, int landid, string landcode, int layer, int provider);

		[OperationContract(IsOneWay = true)]
		void GetOverview();

		[OperationContract(IsOneWay = true)]
		void GetStatus(string sessionstring);

		[OperationContract(IsOneWay = true)]
		void CancelJob(string sessionstring);
	}