gRPC channel / stub reuse

Creating new gRPC channels and stubs involves establishing connections and negotiating capabilities with the server. Reusing them eliminates this overhead for subsequent calls. By keeping connections alive, you avoid the initial handshake delay for each RPC, leading to faster communication.

However, you should ensure that reusing the channel doesn’t create a bottleneck in your application.

  • grpc reuse stub
  • grpc reuse connection
  • grpc reuse channel
  • grpc stub thread-safe
  • grpc channel thread-safe

We can reuse both channels and stubs, they’re thread-safe:

  • Channels: a channel manages the underlying TCP connection to the server. You can reuse a single channel for multiple stubs as long as they connect to the same server.
  • Stubs: stubs are service-specific client-side objects that map gRPC service methods to Java methods. You can reuse a stub for all calls to the same service methods on the same channel.

If you have a really low level of parallelism in your application, you could potentially use only one channel/stub. However, when your task implies dealing with multiple parallel data sending processes, it’s better to create a resource pool of gRPC stubs.

Channel/stub pooling

As long as stubs and channels are thread-safe, you can keep them in pool even if they’re in use by some thread. In other words, you can create a ConcurrentHashMap of let’s say 10 stubs and when any thread wants to get a stub you just return “the next one”: 1-th, 2-nd, 3-rd, … 10-th, and then from the beginning: 1-th, 2-nd, … and so on:

ConcurrentHashMap<Integer, GrpcStub> stubPool = new ConcurrentHashMap<>();
AtomicInteger lastIndex = new AtomicInteger(0);
int poolSize = 10;

public GrpcStub getStub() {
    int nextIdx = syncIndex.getAndUpdate(i -> (i + 1) % poolSize);
    // do not delete from pool, just return.
    // it's ok for gRPC stub to be used by multiple threads at the same time
    return syncPool.get(nextIdx);
}

Remember to initialize the pool.

Reconnecting to the server

When reusing a channel/stub over a long time, you can run into the case when gRPC server restarts. You should recreate your channel/stub in this case. You can make a wrapper above the gRPC stub, that would store channel and stub, and have the following get() method:

  • It should check, whether it’s required to reconnect
  • Remember double-checked locking mechanism, as long as you’re in concurrent environment
  • If it’s required to reconnect, close current channel and connection and create new channel/stub
Set<ConnectivityState> allowedChannelStates = new HashSet<>(Arrays.asList(
    ConnectivityState.CONNECTING, ConnectivityState.IDLE, ConnectivityState.READY)
);

public GrpcStub get() {
    if (isNeedToReconnect()) {
        synchronized (mutex) {
            if (isNeedToReconnect()) {
                close();
                connect();
            }
        }
    }
    return stub;
}

private boolean isNeedToReconnect() {
    ManagedChannel channel = ((ManagedChannel) stub.getChannel());
    ConnectivityState current = channel.getState(true);
    return !allowedChannelStates.contains(current);
}

That’s all.

Telegram channel

If you still have any questions, feel free to ask me in the comments under this article or write me at promark33@gmail.com.

If I saved your day, you can support me 🤝