Publish Globs into etcd and subscribe to their changes. Meant for propagating configuration and slowly-changing state between microservices — not as a data cache (a Redis is the tool for that).
A GlobType describes both the value and the key: the fields annotated PathIndex form the etcd key, in
the order they declare, under a first segment that is the type's name.
- Java 21
org.globsframework:globs,globs-gsonand/orglobs-bin-serialisation,io.etcd:jetcd-core- a reachable etcd cluster
<dependency>
<groupId>org.globsframework</groupId>
<artifactId>globs-etcd</artifactId>
<version>5.0.0</version>
</dependency>public static class Data1 {
public static final GlobType TYPE;
public static final StringField shop;
public static final StringField workerName;
public static final IntegerField num;
public static final StringField someData;
static {
GlobTypeBuilder typeBuilder = GlobTypeBuilderFactory.create("Data1");
shop = typeBuilder.declareStringField("shop", FieldNumber.create(1), PathIndex.create(1));
workerName = typeBuilder.declareStringField("workerName", FieldNumber.create(2), PathIndex.create(2));
num = typeBuilder.declareIntegerField("num", FieldNumber.create(3), PathIndex.create(3));
someData = typeBuilder.declareStringField("someData", FieldNumber.create(4));
TYPE = typeBuilder.build();
}
}gives the key …/Data1/mg.free.fr/w1/1. A listener can therefore watch a whole GlobType or any prefix
of the key, in that order — there is no way to listen on the last part alone.
The value is serialized either as JSON (EtcDSharedDataAccess.createJson(client)) or in the binary TLV
format of globs-bin-serialisation
(createBin(client)) — the binary form requires every field to carry a FieldNumber annotation. Both take
an optional key prefix and separator.
Client client = Client.builder().endpoints(ETCD).build();
SharedDataAccess etcDSharedDataAccess = EtcDSharedDataAccess.createBin(client);
CompletableFuture<Glob> done = new CompletableFuture<>();
etcDSharedDataAccess.listen(Data1.TYPE, new SharedDataAccess.Listener() {
public void put(Glob glob) {
etcDSharedDataAccess.get(glob.getType(), glob).join();
done.complete(glob);
}
public void delete(Glob glob) {
}
}, Data1.TYPE.instantiate()
.set(Data1.shop, "mg.free.fr")
.set(Data1.workerName, "w1")
.set(Data1.num, 1));
MutableGlob data = Data1.TYPE.instantiate()
.set(Data1.shop, "mg.free.fr")
.set(Data1.workerName, "w1")
.set(Data1.num, 1)
.set(Data1.someData, "blabla");
etcDSharedDataAccess.register(data).get(1, TimeUnit.MINUTES);
Glob join = done.join();
Assert.assertEquals("blabla", join.get(Data1.someData));
etcDSharedDataAccess.end();The rest of SharedDataAccess:
| Method | |
|---|---|
get(type, path) / getUnder(type, path) |
one value, or every value under a key prefix |
getAndListenUnder(type, path, initialLoad, listener) |
the current content then the changes, with no gap between the two |
listen / listenUnder |
changes only; both return a ListenerCtrl to close |
delete(type, values) |
remove a key |
registerWithLease(glob, duration) / createLease / createAutoLease |
a value that disappears when its holder stops renewing — UnLeaser.touch() renews, createAutoLease renews for you |
registerForLeaderShip(glob, listener) |
leader election: the listener is told youAreTheLeader() / youAreNotTheLeaderAnyMore() |
InMemorySharedDataAccess implements the same interface without etcd — for tests, and for a single-process
deployment.
mvn -o testThe test suite talks to a real etcd at http://localhost:2379 (EtcDSharedDataAccessTest.ETCD); start one,
or point the constant at yours, before running it.
Apache License 2.0 — see https://www.apache.org/licenses/LICENSE-2.0.txt.