/** Add a future which reconnects the server. * * @param channel The channel to use * @param bootstrap The channel bootstrap to use */ private void addReconnectionOnCloseFuture(@Nonnull Channel channel, @Nonnull Bootstrap bootstrap) { channel.closeFuture().addListener((r) -> { log.info("addReconnectionOnCloseFuture[{}]: disconnected", node); // Remove the current completion future, forcing clients to wait for reconnection. connectionFuture = new CompletableFuture<>(); // Exceptionally complete all requests that were waiting for a completion. outstandingRequests.forEach((reqId, reqCompletableFuture) -> { reqCompletableFuture.completeExceptionally( new NetworkException("Disconnected", node)); // And also remove them. outstandingRequests.remove(reqId); }); // If we aren't shutdown, reconnect. if (!shutdown) { Sleep.sleepUninterruptibly(parameters.getConnectionRetryRate()); log.info("addReconnectionOnCloseFuture[{}]: reconnecting...", node); // Asynchronously connect again. connectAsync(bootstrap); } }); }
/** Handle when a channel is connected. * * @param future The future that is completed when the channel is connected/ * @param bootstrap The bootstrap to connect a new channel (used on reconnect). */ private void channelConnectionFutureHandler(@Nonnull ChannelFuture future, @Nonnull Bootstrap bootstrap) { if (future.isSuccess()) { // Register a future to reconnect in case we get disconnected addReconnectionOnCloseFuture(future.channel(), bootstrap); log.info("connectAsync[{}]: Channel connected.", node); } else { // Otherwise, the connection failed. If we're not shutdown, try reconnecting after // a sleep period. if (!shutdown) { Sleep.sleepUninterruptibly(parameters.getConnectionRetryRate()); log.info("connectAsync[{}]: Channel connection failed, reconnecting...", node); // Call connect, which will retry the call again. // Note that this is not recursive, because it is called in the // context of the handler future. connectAsync(bootstrap); } } }