Freigeben über


Microsoft .NET Remoting: Eine technische Übersicht

 

Piet Obermeyer und Jonathan Hawkins
Microsoft Corporation

Zusammenfassung: Dieser Artikel bietet eine technische Übersicht über das Microsoft .NET Remoting Framework. Es enthält Beispiele für die Verwendung eines TCP-Kanals oder eines HTTP-Kanals. (15 gedruckte Seiten)

Hinweis Dieser Artikel enthält aktualisierten Beta 2-Code.

Inhalte

Einführung Remoteobjekte Proxyobjekte Kanäle Aktivierungsobjektlebensdauer mit Leasingabschluss Anhang A: Remotingbeispiel unter Verwendung eines TCP-Kanals

Einführung

Microsoft® .NET-Remoting bietet ein Framework, das es Objekten ermöglicht, domänenübergreifend miteinander zu interagieren. Das Framework bietet eine Reihe von Diensten, einschließlich Aktivierungs- und Lebensdauerunterstützung, sowie Kommunikationskanäle, die für den Transport von Nachrichten zu und von Remoteanwendungen verantwortlich sind. Formatierer werden zum Codieren und Decodieren der Nachrichten verwendet, bevor sie vom Kanal übertragen werden. Anwendungen können binäre Codierungen verwenden, wenn die Leistung entscheidend ist, oder XML-Codierung, wenn die Interoperabilität mit anderen Remotingframeworks unerlässlich ist. Die gesamte XML-Codierung verwendet das SOAP-Protokoll beim Transport von Nachrichten von einer Anwendungsdomäne in die andere. Remoting wurde unter Berücksichtigung der Sicherheit entwickelt, und es werden eine Reihe von Hooks bereitgestellt, mit denen Kanalsenken zugriff auf die Nachrichten und den serialisierten Stream erhalten können, bevor der Stream über den Kanal übertragen wird.

Die Verwaltung der Lebensdauer von Remoteobjekten ohne Unterstützung durch das zugrunde liegende Framework ist häufig umständlich. .NET-Remoting bietet eine Reihe von Aktivierungsmodellen zur Auswahl. Diese Modelle lassen sich in zwei Kategorien einteilen:

  • Vom Client aktivierte Objekte
  • Vom Server aktivierte Objekte

Vom Client aktivierte Objekte unterliegen der Kontrolle eines leasebasierten Lebensdauer-Managers, der sicherstellt, dass das Objekt nach Ablauf der Lease garbage collection wird. Bei vom Server aktivierten Objekten haben Entwickler die Wahl zwischen einem "single call"- oder "singleton"-Modell. Die Lebensdauer von Singletons wird auch durch die leasebasierte Lebensdauer gesteuert.

Remoteobjekte

Eines der Standard Ziele jedes Remotingframeworks besteht darin, die erforderliche Infrastruktur bereitzustellen, die die Komplexität des Aufrufens von Methoden für Remoteobjekte und die Rückgabe von Ergebnissen ausblendet. Jedes Objekt außerhalb der Anwendungsdomäne des Aufrufers sollte als Remoteobjekt betrachtet werden, auch wenn die Objekte auf demselben Computer ausgeführt werden. Innerhalb der Anwendungsdomäne werden alle Objekte als Verweis übergeben, während primitive Datentypen nach Wert übergeben werden. Da verweise auf lokale Objekte nur innerhalb der Anwendungsdomäne gültig sind, in der sie erstellt werden, können sie nicht an Remotemethodenaufrufe in dieser Form übergeben oder von diesen zurückgegeben werden. Alle lokalen Objekte, die die Anwendungsdomänengrenze überschreiten müssen, müssen nach Wert übergeben und mit dem benutzerdefinierten Attribut [serializable] gekennzeichnet werden, oder sie müssen die ISerializable-Schnittstelle implementieren. Wenn das Objekt als Parameter übergeben wird, serialisiert das Framework das Objekt und übergibt es an die Zielanwendungsdomäne, in der das Objekt wiederhergestellt wird. Lokale Objekte, die nicht serialisiert werden können, können nicht an eine andere Anwendungsdomäne übergeben werden und sind daher nicht aktualisierbar.

Jedes Objekt kann in ein Remoteobjekt geändert werden, indem es von MarshalByRefObject abgeleitet wird. Wenn ein Client ein Remoteobjekt aktiviert, empfängt er einen Proxy für das Remoteobjekt. Alle Vorgänge für diesen Proxy werden entsprechend indirekt, damit die Remotinginfrastruktur die Aufrufe entsprechend abfangen und weiterleiten kann. Diese Dereferenzierung hat einige Auswirkungen auf die Leistung, aber der JIT-Compiler und die Ausführungs-Engine (EE) wurden optimiert, um unnötige Leistungseinbußen zu vermeiden, wenn sich der Proxy und das Remoteobjekt in derselben Anwendungsdomäne befinden. In Fällen, in denen sich der Proxy und die Remoteobjekte in unterschiedlichen Anwendungsdomänen befinden, werden alle Methodenaufrufparameter im Stapel in Nachrichten konvertiert und an die Remoteanwendungsdomäne übertragen, wo die Nachrichten wieder in einen Stapelrahmen umgewandelt und der Methodenaufruf aufgerufen wird. Die gleiche Prozedur wird für die Rückgabe von Ergebnissen aus dem Methodenaufruf verwendet.

Proxyobjekte

Proxyobjekte werden erstellt, wenn ein Client ein Remoteobjekt aktiviert. Das Proxyobjekt fungiert als Vertreter des Remoteobjekts und stellt sicher, dass alle Aufrufe des Proxys an das richtige Remoteobjekt instance weitergeleitet werden. Um genau zu verstehen, wie Proxyobjekte funktionieren, müssen wir sie genauer untersuchen. Wenn ein Client ein Remoteobjekt aktiviert, erstellt das Framework eine lokale instance der Klasse TransparentProxy, die eine Liste aller Klassen sowie Schnittstellenmethoden des Remoteobjekts enthält. Da die TransparentProxy-Klasse bei der CLR registriert wird, wenn sie erstellt wird, werden alle Methodenaufrufe für den Proxy von der Runtime abgefangen. Hier wird der Aufruf untersucht, um festzustellen, ob es sich um eine gültige Methode des Remoteobjekts handelt und ob sich ein instance des Remoteobjekts in derselben Anwendungsdomäne wie der Proxy befindet. Wenn dies zutrifft, wird ein einfacher Methodenaufruf an das tatsächliche Objekt weitergeleitet. Wenn sich das Objekt in einer anderen Anwendungsdomäne befindet, werden die Aufrufparameter auf dem Stapel in ein IMessage-Objekt verpackt und durch Aufrufen der Invoke-Methode an eine RealProxy-Klasse weitergeleitet. Diese Klasse (bzw. eine interne Implementierung davon) ist für die Weiterleitung von Nachrichten an das Remoteobjekt verantwortlich. Sowohl die TransparentProxy - als auch die RealProxy-Klassen werden im Hintergrund erstellt, wenn ein Remoteobjekt aktiviert wird, aber nur transparentProxy wird an den Client zurückgegeben.

Um diese Proxyobjekte besser verstehen zu können, müssen wir einen Umweg nehmen und kurz Erwähnung ObjRef. Eine ausführliche Beschreibung von ObjRef finden Sie im Abschnitt Aktivierung. Im folgenden Szenario wird kurz beschrieben, wie ObjRef und die beiden Proxyklassen zusammenhängen. Es ist wichtig zu beachten, dass dies eine sehr umfassende Beschreibung des Prozesses ist; Es gibt einige Variationen, je nachdem, ob Objekte client- oder serveraktivieren und ob es sich um Singleton- oder Single-Call-Objekte handelt.

  • Ein Remoteobjekt wird in einer Anwendungsdomäne auf einem Remotecomputer registriert. Das Objekt wird gemarshallt, um eine ObjRef zu erzeugen. Das ObjRef enthält alle Informationen, die erforderlich sind, um das Remoteobjekt von einem beliebigen Ort im Netzwerk aus zu finden und darauf zuzugreifen. Diese Informationen umfassen den starken Namen der Klasse, die Hierarchie der Klasse (ihre übergeordneten Elemente), die Namen aller Schnittstellen, die die Klasse implementiert, den Objekt-URI und Details aller verfügbaren Kanäle, die registriert wurden. Das Remotingframework verwendet den Objekt-URI, um die ObjRef-instance abzurufen, die für das Remoteobjekt erstellt wurde, wenn es eine Anforderung für dieses Objekt empfängt.
  • Ein Client aktiviert ein Remoteobjekt durch Aufrufen einer neuen oder einer der Activator-Funktionen wie CreateInstance. Bei vom Server aktivierten Objekten wird transparentProxy für das Remoteobjekt in der Clientanwendungsdomäne erstellt und an den Client zurückgegeben, es werden keine Remoteaufrufe durchgeführt. Das Remoteobjekt wird nur aktiviert, wenn der Client eine Methode für das Remoteobjekt aufruft. Dieses Szenario funktioniert offensichtlich nicht für vom Client aktivierte Objekte, da der Client erwartet, dass das Framework das Objekt aktiviert, wenn er dazu aufgefordert wird. Wenn ein Client eine der Aktivierungsmethoden aufruft, wird ein Aktivierungsproxy auf dem Client erstellt, und ein Remoteaufruf an einen Remoteaktivator auf dem Server wird mit der URL und dem Objekt-URI als Endpunkt initiiert. Der Remoteaktivator aktiviert das Objekt, und ein ObjRef wird an den Client gestreamt, wo es aufgehoben wird, um einen TransparentProxy zu erzeugen, der an den Client zurückgegeben wird.
  • Während der Entmarshaling wird objRef analysiert, um die Methodeninformationen des Remoteobjekts zu extrahieren, und sowohl die TransparentProxy - als auch die RealProxy-Objekte werden erstellt. Der Inhalt des analysierten ObjRef wird den internen Tabellen von TransparentProxy hinzugefügt, bevor letztere bei der CLR registriert wird.

TransparentProxy ist eine interne Klasse, die nicht ersetzt oder erweitert werden kann. Andererseits sind die RealProxy - und ObjRef-Klassen öffentlich und können bei Bedarf erweitert und angepasst werden. Die RealProxy-Klasse ist beispielsweise ein idealer Kandidat für den Lastenausgleich, da sie alle Funktionsaufrufe für ein Remoteobjekt verarbeitet. Wenn Invoke aufgerufen wird, kann eine von RealProxy abgeleitete Klasse Ladeinformationen zu Servern im Netzwerk abrufen und den Aufruf an einen entsprechenden Server weiterleiten. Fordern Sie einfach einen MessageSink für den erforderlichen ObjectURI aus dem Kanal an, und rufen Sie SyncProcessMessage oder AsyncProcessMessage auf, um den Aufruf an das erforderliche Remoteobjekt weiterzuleiten. Wenn der Aufruf zurückgegeben wird, verarbeitet RealProxy automatisch den Rückgabeparameter.

Hier ist ein Codeausschnitt, der zeigt, wie eine abgeleitete RealProxy-Klasse verwendet wird.

   MyRealProxy proxy = new MyRealProxy(typeof(Foo));
   Foo obj = (Foo)proxy.GetTransparentProxy();
   int result = obj.CallSomeMethod();

Der oben abgerufene TransparentProxy kann an eine andere Anwendungsdomäne weitergeleitet werden. Wenn der zweite Client versucht, eine Methode für den Proxy aufzurufen, versucht das Remotingframework, eine instance von MyRealProxy zu erstellen, und wenn die Assembly verfügbar ist, werden alle Aufrufe über diese instance weitergeleitet. Wenn die Assembly nicht verfügbar ist, werden Aufrufe über das Standardremoting RealProxy weitergeleitet.

Ein ObjRef kann einfach angepasst werden, indem Ersetzungen für die Standardmäßigen ObjRef-EigenschaftenTypeInfo, EnvoyInfo und ChannelInfo bereitgestellt werden. Der folgende Code zeigt, wie dies geschehen kann.

public class ObjRef {
  public virtual IRemotingTypeInfo TypeInfo 
  {
    get { return typeInfo;}
    set { typeInfo = value;}
  }

  public virtual IEnvoyInfo EnvoyInfo
  {
    get { return envoyInfo;}
    set { envoyInfo = value;}
  }

  public virtual IChannelInfo ChannelInfo 
  {
    get { return channelInfo;}
    set { channelInfo = value;}
  }
}

Channels

Kanäle werden verwendet, um Nachrichten zu und von Remoteobjekten zu transportieren. Wenn ein Client eine Methode für ein Remoteobjekt aufruft, werden die Parameter sowie andere Details im Zusammenhang mit dem Aufruf durch den Kanal an das Remoteobjekt übertragen. Alle Ergebnisse des Aufrufs werden auf die gleiche Weise an den Client zurückgegeben. Ein Client kann einen der auf dem "Server" registrierten Kanäle auswählen, um mit dem Remoteobjekt zu kommunizieren, sodass Entwickler die Kanäle auswählen können, die ihren Anforderungen am besten entsprechen. Es ist auch möglich, alle vorhandenen Kanäle anzupassen oder neue Kanäle zu erstellen, die unterschiedliche Kommunikationsprotokolle verwenden. Die Channelauswahl unterliegt den folgenden Regeln:

  • Es muss mindestens ein Kanal beim Remotingframework registriert werden, bevor ein Remoteobjekt aufgerufen werden kann. Channels müssen vor Objekten registriert werden.
  • Kanäle werden pro Anwendungsdomäne registriert. Es können mehrere Anwendungsdomänen in einem einzelnen Prozess vorhanden sein. Wenn ein Prozess stirbt, werden alle Kanäle, die er registriert, automatisch zerstört.
  • Es ist unzulässig, denselben Kanal zu registrieren, der mehrmals an demselben Port lauscht. Obwohl Kanäle pro Anwendungsdomäne registriert sind, können verschiedene Anwendungsdomänen auf demselben Computer nicht denselben Kanal registrieren, der am gleichen Port lauscht. Sie können denselben Kanal registrieren, der an zwei verschiedenen Ports lauscht.
  • Clients können über jeden registrierten Channel mit einem Remoteobjekt kommunizieren. Das Remotingframework stellt sicher, dass das Remoteobjekt mit dem richtigen Kanal verbunden ist, wenn ein Client versucht, eine Verbindung mit dem Objekt herzustellen. Der Client ist für den Aufruf von RegisterChannel für die ChannelService-Klasse verantwortlich, bevor versucht wird, mit einem Remoteobjekt zu kommunizieren.

Alle Kanäle leiten von IChannel ab und implementieren je nach Zweck des Kanals entweder IChannelReceiver oder IchannelSender. Die meisten Kanäle implementieren sowohl die Empfänger- als auch die Absenderschnittstelle, damit sie in beide Richtungen kommunizieren können. Wenn ein Client eine Methode für einen Proxy aufruft, wird der Aufruf vom Remotingframework abgefangen und in eine Nachricht geändert, die an die RealProxy-Klasse weitergeleitet wird (oder besser gesagt, ein instance einer Klasse, die RealProxy implementiert). RealProxy leitet die Nachricht zur Verarbeitung an die Kanalsenkenkette weiter.

Diese erste Senke in der Kette ist normalerweise eine Formatierungssenke, die die Nachricht in einen Bytestrom serialisiert. Die Nachricht wird dann von einer Kanalsenke zur nächsten übergeben, bis sie die Transportsenke am Ende der Kette erreicht. Die Transportsenke ist dafür verantwortlich, eine Verbindung mit der Transportsenke auf der Serverseite herzustellen und den Bytedatenstrom an den Server zu senden. Die Transportsenke auf dem Server leitet dann den Bytedatenstrom durch die Senkenkette auf der Serverseite weiter, bis er die Formatierersenke erreicht. An diesem Punkt wird die Nachricht von ihrem Verteilungspunkt an das Remoteobjekt selbst deserialisiert.

Ein verwirrender Aspekt des Remotingframeworks ist die Beziehung zwischen Remoteobjekten und Kanälen. Wie kann beispielsweise ein SingleCall-Remoteobjekt auf Clients lauschen, mit der eine Verbindung hergestellt wird, wenn das Objekt nur aktiviert wird, wenn ein Anruf eingeht?

Ein Teil der Magie beruht auf der Tatsache, dass Remoteobjekte Kanäle gemeinsam nutzen. Ein Remoteobjekt besitzt keinen Kanal. Serveranwendungen, die Remoteobjekte hosten, müssen die erforderlichen Kanäle sowie die Objekte registrieren, die sie mit dem Remotingframework verfügbar machen möchten. Nachdem ein Channel registriert wurde, beginnt er automatisch damit, den angegebenen Anschluss auf Clientanforderungen zu überwachen. Wenn ein Remoteobjekt registriert wird, wird eine ObjRef für das Objekt erstellt und in einer Tabelle gespeichert. Wenn eine Anforderung in einem Kanal eingeht, untersucht das Remotingframework die Nachricht, um das Zielobjekt zu bestimmen, und überprüft die Tabelle der Objektverweise, um den Verweis in der Tabelle zu suchen. Wenn der Objektverweis gefunden wird, wird das Frameworkzielobjekt aus der Tabelle abgerufen oder bei Bedarf aktiviert, und dann leitet das Framework den Aufruf an das -Objekt weiter. Bei synchronen Aufrufen wird die Verbindung des Clients für die Dauer des Nachrichtenaufrufs aufrecht erhalten. Da jede Clientverbindung in einem eigenen Thread behandelt wird, kann ein einziger Channel mehrere Clients gleichzeitig bedienen.

Sicherheit ist ein wichtiger Aspekt beim Erstellen von Geschäftsanwendungen, und Entwickler müssen in der Lage sein, Sicherheitsfeatures wie Autorisierung oder Verschlüsselung zu Remotemethodenaufrufen hinzuzufügen, um geschäftsspezifische Anforderungen zu erfüllen. Um diesen Anforderungen gerecht zu werden, können Kanäle angepasst werden, um Entwicklern die Kontrolle über den tatsächlichen Transportmechanismus von Nachrichten an und aus dem Remoteobjekt zu bieten.

HTTP-Channel

Der HTTP-Kanal transportiert Nachrichten mithilfe des SOAP-Protokolls zu und von Remoteobjekten. Alle Nachrichten werden über den SOAP-Formatierer übergeben, wobei die Nachricht in XML geändert und serialisiert wird, und die erforderlichen SOAP-Header werden dem Stream hinzugefügt. Es ist auch möglich, den HTTP-Kanal für die Verwendung des binären Formatierers zu konfigurieren. Der resultierende Datenstrom wird dann mithilfe des HTTP-Protokolls an den Ziel-URI übertragen.

TCP-Channel

Der TCP-Kanal verwendet einen binären Formatierer, um alle Nachrichten in einen binären Datenstrom zu serialisieren und den Stream mithilfe des TCP-Protokolls an den Ziel-URI zu übertragen. Es ist auch möglich, den TCP-Kanal für den SOAP-Formatierer zu konfigurieren.

Aktivierung

Das Remotingframework unterstützt die Server- und Clientaktivierung von Remoteobjekten. Die Serveraktivierung wird normalerweise verwendet, wenn Remoteobjekte keinen Zustand zwischen Methodenaufrufen beibehalten müssen. Es wird auch in Fällen verwendet, in denen mehrere Clients Methoden für dasselbe Objekt aufrufen instance und das Objekt den Zustand zwischen Funktionsaufrufen beibehält. Auf der anderen Seite werden clientaktivierte Objekte vom Client instanziiert, und der Client verwaltet die Lebensdauer des Remoteobjekts mithilfe eines leasebasierten Systems, das für diesen Zweck bereitgestellt wird.

Alle Remoteobjekte müssen beim Remotingframework registriert werden, bevor Clients darauf zugreifen können. Die Objektregistrierung erfolgt normalerweise durch eine Hostinganwendung, die startet, einen oder mehrere Kanäle bei ChannelServices registriert, ein oder mehrere Remoteobjekte bei RemotingConfiguration registriert und dann wartet, bis sie beendet wird. Es ist wichtig zu beachten, dass die registrierten Kanäle und Objekte nur verfügbar sind, während der Prozess, in dem sie registriert wurden, aktiv ist. Wenn der Prozess beendet wird, werden alle von diesem Prozess registrierten Kanäle und Objekte automatisch aus den Remotingdiensten entfernt, in denen sie registriert wurden. Die folgenden vier Informationen sind erforderlich, wenn Sie ein Remoteobjekt beim Framework registrieren:

  1. Der Assemblyname, in dem die Klasse enthalten ist.
  2. Der Typname des Remoteobjekts.
  3. Der Objekt-URI, den Clients zum Suchen des Objekts verwenden.
  4. Der für die Serveraktivierung erforderliche Objektmodus. Dies kann SingleCall oder Singleton sein.

Ein Remoteobjekt kann registriert werden, indem RegisterWellKnownServiceType aufgerufen wird, die obigen Informationen als Parameter übergeben werden, oder indem die oben genannten Informationen in einer Konfigurationsdatei gespeichert und anschließend Configure aufgerufen wird, wodurch der Name der Konfigurationsdatei als Parameter übergeben wird. Eine dieser beiden Funktionen kann verwendet werden, um Remoteobjekte zu registrieren, da sie genau die gleiche Funktion ausführen. Letzteres ist bequemer zu verwenden, da der Inhalt der Konfigurationsdatei geändert werden kann, ohne die Hostanwendung neu zu kompilieren. Der folgende Codeausschnitt zeigt, wie Die HelloService-Klasse als SingleCall-Remoteobjekt registriert wird.

RemotingConfiguration.RegisterWellKnownServiceType(
  Type.GetType("RemotingSamples.HelloServer,object"), 
  "SayHello", 
  WellKnownObjectMode.SingleCall);

Wobei RemotingSamples der Namespace, HelloServer der Name der Klasse und Object.dll der Name der Assembly ist. SayHello ist der Objekt-URI, in dem unser Dienst verfügbar gemacht wird. Der Objekt-URI kann eine beliebige Textzeichenfolge für das direkte Hosting sein, erfordert jedoch eine REM- oder SOAP-Erweiterung, wenn der Dienst in IIS gehostet wird. Daher ist es ratsam, diese Erweiterungen für alle Remotingendpunkte (URI's) zu verwenden.

Wenn das Objekt registriert wird, erstellt das Framework einen Objektverweis für dieses Remoteobjekt und extrahiert dann die erforderlichen Metadaten zum Objekt aus der Assembly. Diese Informationen werden zusammen mit dem URI und dem Assemblynamen dann in dem Objektverweis gespeichert, der in einer Remotingframeworktabelle für die Nachverfolgung registrierter Remoteobjekte gespeichert wird. Es ist wichtig zu beachten, dass das Remoteobjekt selbst nicht durch den Registrierungsprozess instanziiert wird. Dies geschieht nur, wenn ein Client versucht, eine Methode für das Objekt aufzurufen oder das Objekt von der Clientseite aus zu aktivieren.

Jeder Client, der den URI dieses Objekts kennt, kann nun einen Proxy für dieses Objekt abrufen, indem er den bevorzugten Kanal bei ChannelServices registriert und das Objekt durch Aufrufen von new, GetObject oder CreateInstance aktiviert. Der folgende Codeausschnitt zeigt ein Beispiel dafür, wie dies geschieht.

""      ChannelServices.RegisterChannel(new TcpChannel());
      HelloServer obj =  (HelloServer)Activator.GetObject(
        typeof(RemotingSamples.HelloServer), 
        "tcp://localhost:8085/SayHello");

Hier "tcp://localhost:8085/SayHello" wird angegeben, dass eine Verbindung mit dem Remoteobjekt am SayHello-Endpunkt über TCP an Port 8085 hergestellt werden soll. Der Compiler benötigt offensichtlich Typinformationen zur HelloServer-Klasse , wenn dieser Clientcode kompiliert wird. Diese Informationen können auf eine der folgenden Arten bereitgestellt werden:

  • Geben Sie einen Verweis auf die Assembly an, in der die HelloService-Klasse gespeichert ist.
  • Teilen Sie das Remoteobjekt in eine Implementierungs- und Schnittstellenklasse auf, und verwenden Sie die Schnittstelle als Verweis beim Kompilieren des Clients.
  • Verwenden Sie das SOAPSUDS-Tool, um die erforderlichen Metadaten direkt vom Endpunkt zu extrahieren. Dieses Tool stellt eine Verbindung mit dem bereitgestellten Endpunkt her, extrahiert die Metadaten und generiert eine Assembly oder einen Quellcode, der dann zum Kompilieren des Clients verwendet werden kann.

GetObject oder new kann für die Serveraktivierung verwendet werden. Es ist wichtig zu beachten, dass das Remoteobjekt nicht instanziiert wird, wenn einer dieser Aufrufe erfolgt. Tatsächlich werden überhaupt keine Netzwerkaufrufe generiert. Das Framework ruft genügend Informationen aus den Metadaten ab, um den Proxy zu erstellen, ohne überhaupt eine Verbindung mit dem Remoteobjekt herzustellen. Eine Netzwerkverbindung wird nur hergestellt, wenn der Client eine Methode für den Proxy aufruft. Wenn der Aufruf auf dem Server eingeht, extrahiert das Framework den URI aus der Nachricht, untersucht die Remotingframeworktabellen, um den Verweis für das Objekt zu suchen, das dem URI entspricht, und instanziiert dann das Objekt, falls erforderlich, und leitet den Methodenaufruf an das Objekt weiter. Wenn das Objekt als SingleCall registriert ist, wird es nach Abschluss des Methodenaufrufs zerstört. Für jede aufgerufene Methode wird eine neue instance des -Objekts erstellt. Der einzige Unterschied zwischen GetObject und new besteht darin, dass Sie mit ersterem eine URL als Parameter angeben können, wobei letztere die URL aus der Konfiguration abruft.

CreateInstance oder new kann für vom Client aktivierte Objekte verwendet werden. Beide ermöglichen das Instanziieren eines Objekts mithilfe von Konstruktoren mit Parametern. Eine Aktivierungsanforderung wird an den Server gesendet, wenn ein Client versucht, ein clientaktiviertes Objekt zu aktivieren. Die Lebensdauer von clientaktivierten Objekten wird durch den Leasingdienst gesteuert, der vom Remotingframework bereitgestellt wird. Objektleasing wird im folgenden Abschnitt beschrieben.

Objektlebensdauer mit Leasing

Jede Anwendungsdomäne enthält einen Lease-Manager, der für die Verwaltung von Leases in seiner Domäne verantwortlich ist. Alle Leases werden in regelmäßigen Abständen auf abgelaufene Leasezeiten überprüft. Wenn ein Mietvertrag abgelaufen ist, werden mindestens eine der Sponsoren des Leasings aufgerufen, wenn sie die Möglichkeit haben, den Mietvertrag zu verlängern. Wenn sich keiner der Sponsoren entscheidet, den Mietvertrag zu verlängern, entfernt der Leasemanager die Lease, und das Objekt wird mülltisiert. Der Lease-Manager verwaltet eine Leaseliste mit Leases, die nach verbleibender Leasezeit sortiert sind. Die Leases mit der kürzesten verbleibenden Zeit werden oben in der Liste gespeichert.

Leases implementieren die ILease-Schnittstelle und speichern eine Sammlung von Eigenschaften, die bestimmen, welche Richtlinien und Methoden erneuert werden sollen. Leases können auf Abruf verlängert werden. Jedes Mal, wenn eine Methode für das Remoteobjekt aufgerufen wird, wird die Leasezeit auf das Maximum der aktuellen LeaseTime plus RenewOnCallTime festgelegt. Wenn die LeaseTime abgelaufen ist, wird der Sponsor aufgefordert, den Mietvertrag zu verlängern. Da wir von Zeit zu Zeit mit unzuverlässigen Netzwerken zu tun haben, kann die Situation auftreten, dass der Lease-Sponsor nicht verfügbar ist und um sicherzustellen, dass wir keine Zombieobjekte auf einem Server lassen, hat jede Lease ein SponsorshipTimeout. Dieser Wert gibt an, wie lange gewartet werden muss, bis ein Sponsor antwortet, bevor die Lease beendet wird. Wenn SponsorshipTimeout NULL ist, wird CurrentLeaseTime verwendet, um zu bestimmen, wann die Lease abläuft. Wenn CurrentLeaseTime den Wert 0 hat, läuft die Lease nicht ab. Konfiguration oder APIs können verwendet werden, um die Standardwerte für InitialLeaseTime, SponsorshipTimeout und RenewOnCallTime zu überschreiben.

Der Lease-Manager führt eine Liste der Sponsoren (sie implementieren die ISponsor-Schnittstelle ), die in der Reihenfolge der verringerung der Sponsoringzeit gespeichert ist. Wenn ein Sponsor für die Verlängerung der Mietzeit benötigt wird, wird ein oder mehrere Sponsoren am Anfang der Liste aufgefordert, die Zeit zu verlängern. Der Anfang der Liste stellt den Sponsor dar, der zuvor die größte Leaseverlängerungszeit angefordert hat. Wenn ein Sponsor nicht in der SponsorshipTimeOut-Zeitspanne antwortet, wird er aus der Liste entfernt. Die Lease eines Objekts kann abgerufen werden, indem GetLifetimeService aufgerufen und das Objekt übergeben wird, für das die Lease als Parameter erforderlich ist. Dieser Aufruf ist eine statische Methode der RemotingServices-Klasse . Wenn das Objekt lokal für die Anwendungsdomäne ist, ist der Parameter für diesen Aufruf ein lokaler Verweis auf das -Objekt, und die zurückgegebene Lease ist ein lokaler Verweis auf die Lease. Wenn das Objekt remote ist, wird der Proxy als Parameter übergeben, und ein transparenter Proxy für die Lease wird an den Aufrufer zurückgegeben.

Objekte können ihre eigenen Leases bereitstellen und so ihre eigene Lebensdauer steuern. Dazu wird die InitializeLifetimeService-Methode für MarshalByRefObject wie folgt überschrieben:

public class Foo : MarshalByRefObject {
  public override Object InitializeLifetimeService()
  {
    ILease lease = (ILease)base.InitializeLifetimeService();
    if (lease.CurrentState == LeaseState.Initial)  {
      lease.InitialLeaseTime = TimeSpan.FromMinutes(1);
      lease.SponsorshipTimeout = TimeSpan.FromMinutes(2);
      lease.RenewOnCallTime = TimeSpan.FromSeconds(2);
    }
    return lease;
  }
}

Die Leaseeigenschaften können nur geändert werden, wenn sich eine Lease im Anfangszustand befindet. Die Implementierung von InitializeLifetimeService ruft normalerweise die entsprechende Methode der Basisklasse auf, um die vorhandene Lease für das Remoteobjekt abzurufen. Wenn das Objekt noch nie gemarst wurde, befindet sich die zurückgegebene Lease im Anfangszustand, und die Leaseeigenschaften können festgelegt werden. Nachdem das Objekt gemarst wurde, wechselt die Lease vom anfänglichen zum aktiven Zustand, und jeder Versuch, die Leaseeigenschaften zu initialisieren, wird ignoriert (eine Ausnahme wird ausgelöst). InitializeLifetimeService wird aufgerufen, wenn das Remoteobjekt aktiviert wird. Im Aktivierungsaufruf kann eine Liste von Sponsoren für die Lease angegeben werden, und sobald die Lease aktiv ist, können jederzeit weitere Sponsoren hinzugefügt werden.

Leasezeiten können wie folgt verlängert werden:

  • Ein Client kann die Renew-Methode für die Lease-Klasse aufrufen.
  • Der Leasingvertrag kann eine Verlängerung von einem Sponsor anfordern.
  • Wenn ein Client eine Methode für das -Objekt aufruft, wird die Lease automatisch durch den RenewOnCall-Wert erneuert.

Sobald eine Lease abgelaufen ist, ändert sich der interne Zustand von Aktiv in Abgelaufen, es werden keine weiteren Aufrufe an die Sponsoren ausgeführt, und das Objekt wird mit Müll gesammelt. Da es für Remoteobjekte häufig schwierig ist, einen Rückruf für einen Sponsor durchzuführen, wenn der Sponsor im Web oder hinter einer Firewall bereitgestellt wird, muss sich der Sponsor nicht am gleichen Ort wie der Client befinden. Es kann sich auf einem beliebigen Teil des Netzwerks befinden, der vom Remoteobjekt erreichbar ist.

Die Verwendung von Leases zum Verwalten der Lebensdauer von Remoteobjekten ist ein alternativer Ansatz zum Referenzzählen, der gegenüber unzuverlässigen Netzwerkverbindungen in der Regel komplex und ineffizient ist. Obwohl man argumentieren könnte, dass die Lebensdauer eines Remoteobjekts länger als erforderlich verlängert wird, macht die Verringerung des Netzwerkdatenverkehrs für Referenzzählungs- und Pingclients das Leasing zu einer sehr attraktiven Lösung.

Zusammenfassung

Die Bereitstellung eines perfekten Remoting-Frameworks, das die Anforderungen der meisten Geschäftsanwendungen erfüllt, ist sicherlich ein schwieriges, wenn nicht unmögliches Unterfangen. Durch die Bereitstellung eines Frameworks, das nach Bedarf erweitert und angepasst werden kann, hat Microsoft einen wichtigen Schritt in die richtige Richtung getan.

Anhang A: Remotingbeispiel unter Verwendung eines TCP-Kanals

In diesem Anhang wird gezeigt, wie sie eine einfache "Hallo Welt"-Remoteanwendung schreiben. Der Client übergibt einen String an das Remoteobjekt, das die Wörter "Hi There" an die Zeichenfolge anhängt und das Ergebnis an den Client zurückgibt. Um dieses Beispiel so zu ändern, dass HTTP anstelle von TCP verwendet wird, ersetzen Sie tcp einfach durch HTTP in den Quelldateien.

Speichern Sie diesen Code als server.cs:

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace RemotingSamples {
  public class Sample {

    public static int Main(string [] args) {

      TcpChannel chan = new TcpChannel(8085);
      ChannelServices.RegisterChannel(chan);
      RemotingConfiguration.RegisterWellKnownServiceType
      (Type.GetType("RemotingSamples.HelloServer,object"), 
      "SayHello", WellKnownObjectMode.SingleCall);
      System.Console.WriteLine("Hit <enter> to exit...");
      System.Console.ReadLine();
      return 0;
    }
  }
}

Speichern Sie diesen Code als client.cs:

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace RemotingSamples {
  public class Client
  {
    public static int Main(string [] args)
    {
      TcpChannel chan = new TcpChannel();
      ChannelServices.RegisterChannel(chan);
      HelloServer obj = 
   (HelloServer)Activator.GetObject(typeof(RemotingSamples.HelloServer)
   , "tcp://localhost:8085/SayHello");
      if (obj == null) 
      System.Console.WriteLine("Could not locate server");
      else Console.WriteLine(obj.HelloMethod("Caveman"));
      return 0;
    } 
  }
}

Speichern Sie diesen Code als object.cs:

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace RemotingSamples {
  public class HelloServer : MarshalByRefObject {

    public HelloServer() {
      Console.WriteLine("HelloServer activated");
    }

    public String HelloMethod(String name) {
      Console.WriteLine("Hello.HelloMethod : {0}", name);
      return "Hi there " + name;
    }
  }
}

Hier sehen Sie die Makefile:

all: object.dll server.exe client.exe

object.dll: share.cs
   csc /debug+ /target:library /out:object.dll object.cs

server.exe: server.cs
   csc /debug+ /r:object.dll /r:System.Runtime.Remoting.dll server.cs

client.exe: client.cs server.exe
   csc /debug+ /r:object.dll /r:server.exe 
   /r:System.Runtime.Remoting.dll client.cs