Skip to content

Latest commit

 

History

77 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

Globs RPC Direct

Point-to-point exchange of Glob messages over plain blocking sockets, one thread per connection. No HTTP, no broker, no IDL: what travels on the wire is a Glob serialized with globs-bin-serialisation (a TLV format), and the contract between the two ends is the GlobType itself.

Two APIs, for two shapes of traffic:

Package Shape Entry points
network.rpc.direct request/response — the client asks, the server answers one Glob GlobsServer, RpcGlobClient
network.exchange streaming — either side pushes Globs, with optional acknowledgement, over one or several servers GlobsServer, GlobSingleClient, GlobMultiClient

Requirements

  • Java 21
  • org.globsframework:globs and globs-bin-serialisation
  • every field of an exchanged GlobType must carry a FieldNumber annotation — that is what the binary format keys on:
GlobTypeBuilder typeBuilder = GlobTypeBuilderFactory.create("Exchange");
id   = typeBuilder.declareIntegerField("id", FieldNumber.create(1));
data = typeBuilder.declareStringField("data", FieldNumber.create(2));
TYPE = typeBuilder.build();

Installation

<dependency>
    <groupId>org.globsframework</groupId>
    <artifactId>globs-rpc-direct</artifactId>
    <version>5.3.0</version>
</dependency>

Note the artifactId: the repository is globs-network, the artifact is globs-rpc-direct.

Request / response

The server exposes endpoints, each one a path plus the GlobType it expects; the client calls a path and says which type it wants back.

GlobsServer server = GlobsServer.create();
ExposedEndPoint endPoint = server.addEndPoint("localhost", 3000);
endPoint.addReceiver("/", (data, globInstantiator) ->
        CompletableFuture.completedFuture(data), DummyObject.TYPE);

RpcGlobClient client = RpcGlobClient.create("localhost", 3000);
Glob response = client.request("/",
        DummyObject.TYPE.instantiate()
                .set(DummyObject.id, 1)
                .set(DummyObject.name, "test"),
        DummyObject.TYPE).join();

server.shutdown();

Both sides are asynchronous under the socket: the receiver returns a CompletableFuture<Glob>, and request gives one back, so a slow handler does not hold the connection's reader.

Exchange — streaming, and several servers

A client connects to a path and gets an Exchange to push on; the server hands a Receiver per client and an OnData to push the other way.

GlobsServer server = GlobsServer.create("localhost", 0).with(executor).build();
server.onPath("/path", onData -> new MyReceiver(onData), ExchangeData.TYPE);

GlobMultiClient client = GlobMultiClient.create();
client.add("localhost", server.getPort());          // one Endpoint per server
client.waitForActifServer(1, 1000);

Exchange exchange = client.connect("/path", myDataReceiver, ExchangeData.TYPE,
        GlobClient.AckOption.WITH_ACK_AFTER_CLIENT_CALL,
        GlobClient.SendOption.SEND_TO_ALL);
exchange.send(ExchangeData.create("d", 1, System.nanoTime())).join();

send returns a CompletableFuture<Boolean>, which is how the acknowledgement is surfaced. Two knobs decide what it means:

AckOption The ack is sent
NO_ACK never — nothing is tracked, the future is not waited on
WITH_ACK_BEFORE_READ_DATA on reception, before the payload is even decoded
WITH_ACK_BEFORE_CLIENT_CALL declared, but the server sends no ack for it today
WITH_ACK_AFTER_CLIENT_CALL once the receiver has returned

A receiver that throws, or a payload that does not decode against the declared GlobType, comes back to the sender as an error on the same request id rather than as a dropped connection.

SendOption Where a message goes when several servers are registered
SEND_TO_ALL to every active server
SEND_TO_FIRST to the first registered one
SEND_TO_ANY to one at random
SEND_TO_ACTIVE to the one marked active (Endpoint.setActive())

Servers can be added and removed while traffic runs — GlobMultiClient.add / Endpoint.unregister, with waitForActifServer(count, timeoutMs) to synchronize on the current membership. GlobSingleClient is the one-server case with the same interface.

MultiClientSendReplyWithDisruptor in the test sources is the variant driving the send loop through an LMAX disruptor.

Building

mvn -o test                                  # JUnit 5; the tests bind real sockets on localhost
mvn -o test -Dtest=MultiClientTest

AsyncSimpleServerTest and PerfTest also print latency percentiles (dropwizard metrics) — they are as much benchmarks as tests.

License

Apache License 2.0 — see https://www.apache.org/licenses/LICENSE-2.0.txt.

Links

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages