Auswahl an Dienstprogrammen für andere Module. Enthält LambdaReflection
, das das Festlegen und Abrufen von Feldwerten einer Instanz oder den Zugriff auf den Konstruktor über von LambdaMetafactory
erstellte Funktionsschnittstellen ermöglicht.
Abstrakte Serialisierungsbibliothek, die jede Pufferimplementierung unterstützt. Automatisch (de-)serialisiert Klassen, die mit @AutoSerializable
gekennzeichnet sind und aus Feldern von Standardtypen, Proto4jSerializable
-Implementierungen oder anderen @AutoSerializable
Mitgliedern bestehen.
Unterstützt auch Vererbung: Die serialisierbare Klasse kann andere @AutoSerializable
erweitern. In diesem Fall werden auch alle übergeordneten Felder transitiv serialisiert.
Felder, die während der Serialisierung ignoriert werden sollen, sollten mit @Transient
annotiert werden.
Netzwerkbibliothek mit benutzerdefinierter UDP-basierter Implementierung mit konfigurierbarer Zuverlässigkeit.
Proto4jServer
und Proto4jClient
können Sie Daten mithilfe von Datagrammen zwischen Sockets übertragen.
Standardmäßig sind alle übertragenen Daten geordnet und zuverlässig, dürfen in mehrere UDP-Pakete aufgeteilt und auf der Empfängerseite wieder kombiniert werden und werden garantiert zugestellt.
Alle gesendeten UDP-Pakete haben die folgende Struktur:
Sie haben die Wahl, wie die Daten übermittelt werden. Es kann durch Angabe von Flags für Sendemethoden konfiguriert werden. Sie befinden sich alle in Proto4jPacket
. Flag
.
Die folgenden Flags sind verfügbar:
Name | Wert | Bedeutung |
---|---|---|
CONFIRMATION | 0x01 | Markiert, dass dieses Paket ein Indikator dafür ist, dass andere Pakete erfolgreich empfangen wurden. Erforderlich für die Übertragungssicherheit. Im Allgemeinen nur für den internen Gebrauch. |
PARTIAL | 0x02 | Markiert, dass genau dieses UDP-Paket Teil eines größeren Pakets ist. Wenn es zusammen mit CONFIRMATION Flag verwendet wird, zeigt es an, dass ein Teil eines größeren Pakets zugestellt wurde. |
UNORDERED | 0x04 | Markiert, dass dieses Paket nicht in der richtigen Reihenfolge verarbeitet werden kann. |
UNSIGNED_BODY | 0x08 | Standardmäßig werden alle gesendeten Pakete mit CRC32 signiert, aber für Pakete mit diesem Flag wird nur der Header eines Pakets signiert. Dies bedeutet, dass Pakete möglicherweise ungültige Bytes enthalten (obwohl weiterhin kein Datenverlust garantiert ist). |
UNRELIABLE | 0x10 | Markiert dieses Paket als nicht bestätigungsbedürftig. Falls der Empfänger dieses Paket nicht empfängt, unternimmt der Absender nichts. |
INDIVISIBLE | 0x20 | UDP-Pakete sind in ihrer Länge begrenzt, daher teilt Proto4J große Datenmengen in mehrere kleinere Pakete auf. Dieses Flag gibt an, dass im Falle, dass das Paket die Größenbeschränkung des einzelnen Pakets überschreitet, eine Ausnahme ausgelöst wird, anstatt eine Aufteilung durchzuführen. |
Auf dieser Ebene wird kein Handshake oder Ping unterstützt, aber Sie können Ihre eigenen Pakethandler mit der Methode Proto4jSocket
.setInitialPacketHandler(BiConsumer<C, Proto4jPacket>)
einrichten. Pakete, die an diesem Punkt ankommen, werden niemals mit den Flags CONFIRMATION
oder PARTIAL
markiert, sodass alle dort verarbeiteten Proto4jPacket
Instanzen genau die vom Absender gesendeten Daten enthalten (bis zum Flag UNSIGNED_BODY
).
Außerdem wird beim Starten des Sockets ein CompletionStage<Void>
zurückgegeben, das Ihnen bei der Initiierung der Kommunikationslogik zwischen Sockets helfen kann.
Wenn Sie einen beliebigen Socket in Proto4J instanziieren möchten, müssen Sie die Anzahl der Worker- und Handler-Threads an den Socket-Konstruktor übergeben.
Worker werden nur zum Lesen von Daten aus dem Socket verwendet.
Handler werden für die Handhabungslogik verwendet, wenn ein neues Paket erscheint.
Dies ist eine Schnittstelle höherer Ebene als die vorherige Ebene. Um damit zu arbeiten, werfen Sie einen Blick auf Proto4jHighServer
und Proto4jHighClient
oder ihre Basisimplementierungen: BaseProto4jHighServer
und BaseProto4jHighClient
.
Wenn der Client zunächst mit dem Server interagiert, initiiert er den Handshake . Nach Abschluss pingen sich Server und Client gegenseitig, um sicherzustellen, dass die Verbindung nicht verloren geht.
Im Gegensatz zu Low-Level können Sie High-Level-Pakete nicht nur durch die Manipulation von Rohbytes über das Netzwerk senden, sondern auch durch die Verwendung komplexer Entitäten. Erstellen Sie dazu Ihre eigene Klasse, EnumeratedProto4jPacket
oder CallbackProto4jPacket
erweitert. Damit es funktioniert, müssen Sie lediglich die Methoden write(Buffer)
und read(Buffer)
implementieren und Ihr Paket auf beiden Seiten im PacketManager
registrieren.
Außerdem gibt es eine alternative PacketHandler
-Klasse, die mit diesen Paketen anstelle von Proto4jPacket
s arbeitet.
Es kommt häufig vor, dass man darauf wartet, dass ein Paket auf die gesendeten Pakete antwortet. Diese Funktionalität ist auf dieser Ebene bereits implementiert. Sie können die maximale Wartezeit angeben und die Antwort nach Ihren Wünschen verarbeiten. Dies kann durch Senden des ersten Pakets mit HighChannel
erfolgen. sendWithCallback(CallbackProto4jPacket)
-Methode.
Im Folgenden finden Sie eine Liste von Systemeigenschaften, mit denen Sie das interne Verhalten von Modulen beeinflussen können. Alle Zeitwerte werden in Millisekunden angegeben.
Name | Standardwert | Beschreibung |
---|---|---|
proto4j.maxDatagramSize | 508 | Maximal zulässige Datagrammgröße. Beachten Sie, dass die gesamte UDP-Paketgröße gezählt wird. |
proto4j.maxSequenceNumber | 2_000_000_000 | Maximale Sequenznummer des Pakets. Wenn der interne Zähler diesen Wert erreicht, wird er auf Null zurückgesetzt. |
proto4j.reliabilityThreshold | 20 | Verzögerung unbestätigter (und nicht mit der Flagge UNRELIABLE markierter) Pakete. |
proto4j.callbacksRegistryDelay | 100 | Rate, mit der die Registrierungsprüfungen von Rückrufen die Rückrufe mit Zeitüberschreitung abrufen. |
proto4j.callbacksInitialDelay | 500 | Dies ist die Standardzeit, die immer dann verwendet wird, wenn ein Paket gesendet und gewartet wird, sofern die Wartezeit nicht explizit angegeben ist. |
proto4j.highTimeout | 10_000 | Wenn der Server so lange keine Pakete vom Client empfängt, wird er die Verbindung zum Client trennen. |
proto4j.highPingDelay | 1_000 | Wenn der Server angibt, dass es so lange keine Empfangs- oder Sendevorgänge vom Client gegeben hat, sendet er die Antwort an den Client und wartet auf ein Ping-Paket. |
Dies ist eine API auf höherer Ebene als die API auf hoher Ebene . Anstatt Pakete und deren Handhabung manuell umzusetzen, arbeiten Sie über Dienste.
Um damit zu arbeiten, verwenden Sie RpcServer
und RpcClient
.
Der Server auf dieser Ebene wird nur für Routingzwecke verwendet, aber Clients fungieren sowohl als Dienstbenutzer als auch als Implementierer.
Der Service besteht aus Schnittstellen- und Implementierungsteilen. Als Dienstbenutzer können Sie eine Dienstschnittstelleninstanz über RpcClient
.getServiceManager().getService(Class<S>)
erhalten. Alle seine Methoden werden an registrierte Implementierungen weitergeleitet und remote ausgeführt.
Um Ihren eigenen Dienst zu erstellen, beginnen Sie mit einer Schnittstelle und kommentieren Sie diese mit @Proto4jService.
Die Serviceschnittstelle darf über Standardmethoden und statische Methoden verfügen, ihr Rückgabetyp muss jedoch void
, serializable oder ein CompletionStage
der vorherigen Typen sein. Außerdem müssen alle Argumente serialisierbar sein.
Serialisierbare Typen sind die folgenden:
String
und UUID
@AutoSerializable
annotierte KlassenBufferSerializable
implementierenList
, Set
und Map
serialisierbarer Typen Wenn eine Methode auf allen registrierten Dienstimplementierungen ausgeführt werden soll, sollte sie mit @Broadcast
annotiert werden. Solche Methoden können jedoch nur void
oder CompletionStage<Void>
zurückgeben.
Wenn Sie die Methode aufrufen, wird sie standardmäßig in einer zufälligen Implementierung ausgeführt. Wenn Sie die Ausführungsverteilung steuern möchten, markieren Sie einige Argumente der Methode mit @Index
: Bei jedem Aufruf der Methode wird die Implementierung basierend auf dem Hash-Code der markierten Argumente ausgewählt.
Immer wenn der Dienst registriert wird, werden alle Methoden in Ganzzahlbezeichner konvertiert. Es kann nicht zwei Methoden mit demselben Bezeichner geben, aber eine solche Situation kann auftreten. Um damit umzugehen, kommentieren Sie die Methode mit @MethodIdentifier
mit explizit angegebenem statischen Bezeichner.
Wenn Sie bereits eine Serviceschnittstelle erstellt haben, erstellen Sie nun deren Implementierung und registrieren Sie sie mit RpcClient
.getServiceManager().registerService(Class<S>, I)
.
Ein häufiges Szenario besteht darin, dass auf zwei Clientgruppen eine Serviceschnittstelle vorhanden ist, die Implementierung jedoch nur auf einem von ihnen vorhanden ist.
Dies ist eine übergeordnete Ebene gegenüber dem Basis-RPC.
Beim Erstellen eines verteilten Back-Ends (z. B. Microservices) empfiehlt es sich, die Anzahl der Fehlerquellen zu minimieren. In dem im vorherigen Abschnitt beschriebenen Schema gibt es nur eine Fehlerquelle, nämlich eine einzelne Serverinstanz. Conclave ist eine Reihe von Servern, die gleichzeitig arbeiten.
Alle Server auf Conclave sind miteinander verbunden, aber jeder Client ist nur mit einem einzigen Server verbunden. RPC-Abfragen werden ordnungsgemäß über das gesamte Netzwerk verteilt und weitergeleitet, sodass Sie sich darüber keine Sorgen machen müssen.
Um mit Conclave zu arbeiten, werfen Sie einen Blick auf RpcConclaveServer
und RpcConclaveClient
. Um einen von ihnen zu instanziieren, müssen Sie eine List<InetSocketAddress>
übergeben – eine Liste aller Zielpunkte aller Server.
Was das Transportmodul betrifft, gibt es eine Reihe von Systemeigenschaften, nach denen im RPC- Modul gesucht wird.
Name | Standardwert | Beschreibung |
---|---|---|
proto4j.conclaveWorkers | 2 | Anzahl der Arbeitsthreads, die von jedem der serverinternen Clients verwendet werden (die für den Zugriff auf andere Server verwendet werden). |
proto4j.conclaveHandlers | 2 | Anzahl der Handler-Threads, die von jedem der serverinternen Clients verwendet werden (die für den Zugriff auf andere Server verwendet werden). |
proto4j.conclaveTimeout | 1_000 | Maximale Zeit, die der Server wartet, bis der Handshake mit einem anderen Server abgeschlossen ist. Andernfalls wird Letzteres als nicht laufende Verbindung betrachtet, die eigene Verbindungsversuche beendet. In diesem Fall wird die Verbindung nur dann neu gestartet, wenn bei ihrem Start eine Anfrage von einer anderen Verbindung gestellt wird. |