Clean up docs
This commit is contained in:
parent
8d4ef982d0
commit
6357d30ea0
@ -6,22 +6,20 @@ and managing connections between them.
|
||||
## Service Registry
|
||||
|
||||
The service registry is a class that keeps track of the services
|
||||
that are currently running, and their connection information.
|
||||
that are currently running, and their connection information.
|
||||
|
||||
There are two implementations:
|
||||
The service register implementation is based on [Zookeeper](https://zookeeper.apache.org/),
|
||||
which is a distributed coordination service. This lets services register
|
||||
themselves and announce their liveness, and then discover each other.
|
||||
|
||||
* A simple implementation that effectively hard-codes the services.
|
||||
This is sufficient when running in docker, although loses some
|
||||
smart features. It is fundamentally incompatible with running
|
||||
the system bare-metal, and does not permit multiple instances
|
||||
of a service to run.
|
||||
It supports multiple instances of a service running, and
|
||||
supports running the system bare-metal, where it will assign
|
||||
ports to the services from a range.
|
||||
|
||||
* A more advanced implementation that is based on [Zookeeper](https://zookeeper.apache.org/),
|
||||
which is a distributed coordination service. This implementation
|
||||
lets services register themselves and announce their liveness,
|
||||
and then discover each other. It supports multiple instances of
|
||||
a service running, and supports running the system bare-metal,
|
||||
where it will assign ports to the services from a range.
|
||||
* REST services are registered on a per-node basis, and are always non-partitioned.
|
||||
* gRPC services are registered on a per-api basis, and can be partitioned
|
||||
or non-partitioned. This means that if a gRPC api is moved between nodes,
|
||||
the clients will not need to be reconfigured.
|
||||
|
||||
To be discoverable, the caller must first register their
|
||||
services:
|
||||
@ -30,24 +28,25 @@ services:
|
||||
// Register one or more services
|
||||
serviceRegistry.registerService(
|
||||
ServiceKey.forRest(serviceId, nodeId),
|
||||
instanceUuid, // unique
|
||||
externalAddress); // bind-address
|
||||
instanceUuid, // unique
|
||||
externalAddress); // bind-address
|
||||
|
||||
// Non-partitioned GRPC service
|
||||
serviceRegistry.registerService(
|
||||
serviceRegistry.registerService(
|
||||
ServiceKey.forServiceDescriptor(descriptor, ServicePartition.any()),
|
||||
instanceUuid,
|
||||
externalAddress);
|
||||
instanceUuid,
|
||||
externalAddress);
|
||||
|
||||
// Partitioned GRPC service
|
||||
serviceRegistry.registerService(
|
||||
serviceRegistry.registerService(
|
||||
ServiceKey.forServiceDescriptor(descriptor, ServicePartition.partition(5)),
|
||||
instanceUuid,
|
||||
externalAddress);
|
||||
instanceUuid,
|
||||
externalAddress);
|
||||
|
||||
// (+ any other services)
|
||||
```
|
||||
|
||||
Then, the caller must announce their instance. Before this is done,
|
||||
Then, the caller must announce their instance. Before this is done,
|
||||
the service is not discoverable.
|
||||
|
||||
```java
|
||||
@ -55,7 +54,7 @@ registry.announceInstance(instanceUUID);
|
||||
```
|
||||
|
||||
All of this is done automatically by the `Service` base class
|
||||
in the [service](../service/) module.
|
||||
in the [service](../service/) module.
|
||||
|
||||
To discover a service, the caller can query the registry:
|
||||
|
||||
@ -67,18 +66,9 @@ It's also possible to subscribe to changes in the registry, so that
|
||||
the caller can be notified when a service comes or goes, with `registry.registerMonitor()`.
|
||||
|
||||
However the `GrpcChannelPoolFactory` is a more convenient way to access the services,
|
||||
it will let the caller create a pool of channels to the services, and manage their
|
||||
it will let the caller create a pool of channels to the services, and manage their
|
||||
lifecycle, listen to lifecycle notifications and so on.
|
||||
|
||||
The ChannelPools exist in two flavors, one for partitioned services, and one for non-partitioned services.
|
||||
|
||||
|
||||
### Central Classes
|
||||
|
||||
* [ServiceRegistryIf](src/main/java/nu/marginalia/service/discovery/ServiceRegistryIf.java)
|
||||
* [ZkServiceRegistry](src/main/java/nu/marginalia/service/discovery/ZkServiceRegistry.java)
|
||||
* [FixedServiceRegistry](src/main/java/nu/marginalia/service/discovery/FixedServiceRegistry.java)
|
||||
|
||||
## gRPC Channel Pool
|
||||
|
||||
From the [GrpcChannelPoolFactory](src/main/java/nu/marginalia/service/client/GrpcChannelPoolFactory.java), two types of channel pools can be created
|
||||
@ -86,8 +76,74 @@ that are aware of the service registry:
|
||||
|
||||
* [GrpcMultiNodeChannelPool](src/main/java/nu/marginalia/service/client/GrpcMultiNodeChannelPool.java) - This pool permits 1-n style communication with partitioned services
|
||||
* [GrpcSingleNodeChannelPool](src/main/java/nu/marginalia/service/client/GrpcSingleNodeChannelPool.java) - This pool permits 1-1 style communication with non-partitioned services.
|
||||
if multiple instances are running, it will use one of them and fall back
|
||||
to another if the first is not available.
|
||||
if multiple instances are running, it will use one of them and fall back
|
||||
to another if the first is not available.
|
||||
|
||||
The pools manage the lifecycle of the gRPC channels, and will permit the caller
|
||||
to access Stub interfaces for the services.
|
||||
The pools can generate calls to the gRPC services, and will manage the lifecycle of the channels.
|
||||
|
||||
The API is designed to be simple to use, and will permit the caller to access the Stub interfaces
|
||||
for the services through a fluent API.
|
||||
|
||||
### Example Usage of the GrpcSingleNodeChannelPool
|
||||
|
||||
```java
|
||||
// create a pool for a non-partitioned service
|
||||
channelPool = factory.createSingle(
|
||||
ServiceKey.forGrpcApi(MathApiGrpc.class, ServicePartition.any()),
|
||||
MathApiGrpc::newBlockingStub);
|
||||
|
||||
// blocking call
|
||||
Response response = channelPool
|
||||
.call(MathApiGrpc.MathApiBlockingStub::dictionaryLookup)
|
||||
.run(request);
|
||||
|
||||
// sequential blocking calls
|
||||
List<Response> response = channelPool
|
||||
.call(MathApiGrpc.MathApiBlockingStub::dictionaryLookup)
|
||||
.runFor(request1, request2);
|
||||
|
||||
|
||||
// async call
|
||||
Future<Response> response = channelPool
|
||||
.call(MathApiGrpc.MathApiBlockingStub::dictionaryLookup)
|
||||
.async(myExecutor)
|
||||
.run(request);
|
||||
|
||||
// multiple async calls
|
||||
Future<List<Response>> response = channelPool
|
||||
.call(MathApiGrpc.MathApiBlockingStub::dictionaryLookup)
|
||||
.async(myExecutor)
|
||||
.runFor(request1, request2);
|
||||
```
|
||||
|
||||
### Example Usage of the GrpcSingleNodeChannelPool
|
||||
|
||||
```java
|
||||
// create a pool for a partitioned service
|
||||
channelPool = factory.createMulti(
|
||||
ServiceKey.forGrpcApi(MathApiGrpc.class, ServicePartition.multi()),
|
||||
MathApiGrpc::newBlockingStub);
|
||||
|
||||
// blocking call
|
||||
List<Response> response = channelPool
|
||||
.call(MathApiGrpc.MathApiBlockingStub::dictionaryLookup)
|
||||
.run(request);
|
||||
|
||||
// async call
|
||||
Future<List<Response>> response = channelPool
|
||||
.call(MathApiGrpc.MathApiBlockingStub::dictionaryLookup)
|
||||
.async(myExecutor)
|
||||
.runEach(request);
|
||||
|
||||
// async call, will fail or succeed as a group
|
||||
Future<List<Response>> response = channelPool
|
||||
.call(MathApiGrpc.MathApiBlockingStub::dictionaryLookup)
|
||||
.async(myExecutor)
|
||||
.runAll(request1, request2);
|
||||
```
|
||||
|
||||
|
||||
### Central Classes
|
||||
|
||||
* [ServiceRegistryIf](src/main/java/nu/marginalia/service/discovery/ServiceRegistryIf.java)
|
||||
* [ZkServiceRegistry](src/main/java/nu/marginalia/service/discovery/ZkServiceRegistry.java)
|
@ -100,6 +100,7 @@ public class GrpcMultiNodeChannelPool<STUB> {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
/** Run the given method on each node, returning a future of a list of results */
|
||||
public CompletableFuture<List<T>> runAll(I arg) {
|
||||
var futures = getEligibleNodes().stream()
|
||||
.map(GrpcMultiNodeChannelPool.this::getPoolForNode)
|
||||
@ -113,6 +114,7 @@ public class GrpcMultiNodeChannelPool<STUB> {
|
||||
.thenApply(v -> futures.stream().map(CompletableFuture::join).toList());
|
||||
}
|
||||
|
||||
/** Run the given method on each node, returning a list of futures. */
|
||||
public List<CompletableFuture<T>> runEach(I arg) {
|
||||
return getEligibleNodes().stream()
|
||||
.map(GrpcMultiNodeChannelPool.this::getPoolForNode)
|
||||
|
Loading…
Reference in New Issue
Block a user