diff --git a/core/src/main/java/io/grpc/LoadBalancer2.java b/core/src/main/java/io/grpc/LoadBalancer2.java index b566eca2d4..2945efbb09 100644 --- a/core/src/main/java/io/grpc/LoadBalancer2.java +++ b/core/src/main/java/io/grpc/LoadBalancer2.java @@ -55,20 +55,21 @@ import javax.annotation.concurrent.ThreadSafe; * from the Channel Executor. It receives the results from the {@link NameResolver}, updates * of subchannels' connectivity states, and the channel's request for the LoadBalancer to * shutdown. - *
{@link Helper} is implemented by gRPC library and provided to {@link Factory}. It provides - * functionalities that a {@code LoadBalancer2} implementation would typically need.
{@link Helper Helper} is implemented by gRPC library and provided to {@link Factory + * Factory}. It provides functionalities that a {@code LoadBalancer2} implementation would typically + * need. * *
Channel Executor is an internal executor of the channel, which is used to serialize all the * callback methods on the {@link LoadBalancer2} interface, thus the balancer implementation doesn't * need to worry about synchronization among them. However, the actual thread of the Channel - * Executor is typically the network thread, thus following rules must be followed to prevent + * Executor is typically the network thread, thus the following rules must be followed to prevent * blocking or even dead-locking in a network * *
{@link Helper#runSerialized} allows you to schedule a task to be run in the Channel Executor. + *
{@link Helper#runSerialized Helper.runSerialized()} allows you to schedule a task to be run in + * the Channel Executor. * *
A typical {@link SubchannelPicker} holds a snapshot of these states. It may have its own - * states, e.g., a picker from a round-robin load-balancer may keep a pointer to the next - * Subchannel, which are typically mutated by multiple threads. The picker should only mutate its - * own state, and should not mutate or re-acquire the states of the LoadBalancer. This way - * the picker only needs to synchronize its own states, which is typically trivial. + *
A typical {@link SubchannelPicker SubchannelPicker} holds a snapshot of these states. It may + * have its own states, e.g., a picker from a round-robin load-balancer may keep a pointer to the + * next Subchannel, which are typically mutated by multiple threads. The picker should only mutate + * its own state, and should not mutate or re-acquire the states of the LoadBalancer. This way the + * picker only needs to synchronize its own states, which is typically trivial to implement. * *
When the LoadBalancer states changes, e.g., Subchannels has become or stopped being READY, and * we want subsequent RPCs to use the latest list of READY Subchannels, LoadBalancer would create - * a new picker, which holds a snapshot of the latest Subchannel list. + * a new picker, which holds a snapshot of the latest Subchannel list. Refer to the javadoc of + * {@link #handleSubchannelState handleSubchannelState()} how to do this properly. * *
No synchronization should be necessary between LoadBalancer and its pickers if you follow * the pattern above. It may be possible to implement in a different way, but that would usually @@ -133,6 +136,20 @@ public abstract class LoadBalancer2 { /** * Handles a state change on a Subchannel. * + *
The initial state of a Subchannel is IDLE. You won't get a notification for the initial IDLE + * state. + * + *
If the new state is not SHUTDOWN, this method should create a new picker and call {@link + * Helper#updatePicker Helper.updatePicker()}. Failing to do so may result in unnecessary delays + * of RPCs. Please refer to {@link PickResult#withSubchannel PickResult.withSubchannel()}'s + * javadoc for more information. + * + *
SHUTDOWN can only happen in two cases. One is that LoadBalancer called {@link + * Subchannel#shutdown} earlier, thus it should have already discarded this Subchannel. The other + * is that Channel is doing a {@link ManagedChannel#shutdownNow forced shutdown} or has already + * terminated, thus there won't be further requests to LoadBalancer. Therefore, SHUTDOWN can be + * safely ignored. + * * @param subchannel the involved Subchannel * @param stateInfo the new state */ @@ -162,13 +179,25 @@ public abstract class LoadBalancer2 { } /** - * A balancing decision made by {@link SubchannelPicker} for an RPC. + * A balancing decision made by {@link SubchannelPicker SubchannelPicker} for an RPC. + * + *
The outcome of the decision will be one of the following: + *
Only Subchannels returned by {@link Helper#createSubchannel} will work. DO NOT try to - * use your own implementations of Subchannels, as they won't work. + *
Only Subchannels returned by {@link Helper#createSubchannel Helper.createSubchannel()} + * will work. DO NOT try to use your own implementations of Subchannels, as they won't work. + * + *
When the RPC tries to use the return Subchannel, which is briefly after this method + * returns, the state of the Subchannel will decide where the RPC would go: + * + *
All buffered RPCs will stay buffered until the next call of {@link + * Helper#updatePicker Helper.updatePicker()}, which will trigger a new picking process. + * + *
Note that Subchannel's state may change at the same time the picker is making the + * decision, which means the decision may be made with (to-be) outdated information. For + * example, a picker may return a Subchannel known to be READY, but it has become IDLE when is + * about to be used by the RPC, which makes the RPC to be buffered. The LoadBalancer will soon + * learn about the Subchannels' transition from READY to IDLE, create a new picker and allow the + * RPC to use another READY transport if there is any. + * + *
You will want to avoid running into a situation where there are READY Subchannels out + * there but some RPCs are still buffered for longer than a brief time. + *
In order to prevent unnecessary delay of RPCs, the rules of thumb are: + *
The LoadBalancer is responsible for closing unused Subchannels, and closing all * Subchannels within {@link #shutdown}. @@ -261,11 +344,12 @@ public abstract class LoadBalancer2 { /** * Set a new picker to the channel. * - *
When a new picker is provided via {@link Helper#updatePicker}, the channel will apply the - * picker on all buffered RPCs, by calling {@link SubchannelPicker#pickSubchannel}. + *
When a new picker is provided via {@code updatePicker()}, the channel will apply the + * picker on all buffered RPCs, by calling {@link SubchannelPicker#pickSubchannel + * SubchannelPicker.pickSubchannel()}. * - *
The channel will hold the picker and use it for all RPCs, until {@link #updatePicker} is - * called again and a new picker replaces the old one. If {@link #updatePicker} has never been + *
The channel will hold the picker and use it for all RPCs, until {@code updatePicker()} is + * called again and a new picker replaces the old one. If {@code updatePicker()} has never been * called, the channel will buffer all RPCs until a picker is provided. */ public abstract void updatePicker(SubchannelPicker picker); @@ -288,7 +372,7 @@ public abstract class LoadBalancer2 { } /** - * A logical connection to a server, or a group of equivalent servers represented by an {@link + * A logical connection to a server, or a group of equivalent servers represented by an {@link * EquivalentAddressGroup}. * *
It maintains at most one physical connection (aka transport) for sending new RPCs, while @@ -296,12 +380,14 @@ public abstract class LoadBalancer2 { * *
If there isn't an active transport yet, and an RPC is assigned to the Subchannel, it will * create a new transport. It won't actively create transports otherwise. {@link - * #requestConnection} can be used to ask Subchannel to create a transport if there isn't any. + * #requestConnection requestConnection()} can be used to ask Subchannel to create a transport if + * there isn't any. */ @ThreadSafe public abstract static class Subchannel { /** - * Shuts down the Subchannel. No new RPCs will be accepted. + * Shuts down the Subchannel. After this method is called, this Subchannel should no longer + * be returned by the latest {@link SubchannelPicker picker}, and can be safely discarded. */ public abstract void shutdown(); @@ -316,7 +402,7 @@ public abstract class LoadBalancer2 { public abstract EquivalentAddressGroup getAddresses(); /** - * The same attributes passed to {@link io.grpc.LoadBalancer2.Helper#createSubchannel}. + * The same attributes passed to {@link Helper#createSubchannel Helper.createSubchannel()}. * LoadBalancer can use it to attach additional information here, e.g., the shard this * Subchannel belongs to. */ @@ -326,7 +412,7 @@ public abstract class LoadBalancer2 { @ThreadSafe public abstract static class Factory { /** - * Creates a {@link LoadBalancer} that will be used inside a channel. + * Creates a {@link LoadBalancer2} that will be used inside a channel. */ public abstract LoadBalancer2 newLoadBalancer(Helper helper); }