code: public CompletableFuture<Void> shutdown() {
  		final CompletableFuture<Void> newShutdownFuture = new CompletableFuture<>();
  		if (clientShutdownFuture.compareAndSet(null, newShutdownFuture)) {
  
  			final List<CompletableFuture<Void>> connectionFutures = new ArrayList<>();
  
  			for (Map.Entry<InetSocketAddress, EstablishedConnection> conn : establishedConnections.entrySet()) {
  				if (establishedConnections.remove(conn.getKey(), conn.getValue())) {
  					connectionFutures.add(conn.getValue().close());
  				}
  			}
  
  			for (Map.Entry<InetSocketAddress, PendingConnection> conn : pendingConnections.entrySet()) {
  				if (pendingConnections.remove(conn.getKey()) != null) {
  					connectionFutures.add(conn.getValue().close());
  				}
  			}
  
  			CompletableFuture.allOf(
  					connectionFutures.toArray(new CompletableFuture<?>[connectionFutures.size()])
  			).whenComplete((result, throwable) -> {
  
  				if (throwable != null) {
  					LOG.warn("Problem while shutting down the connections at the {}: {}", clientName, throwable);
  				}
  
  				if (bootstrap != null) {
  					EventLoopGroup group = bootstrap.group();
  					if (group != null && !group.isShutdown()) {
  						group.shutdownGracefully(0L, 0L, TimeUnit.MILLISECONDS)
  								.addListener(finished -> {
  									if (finished.isSuccess()) {
  										newShutdownFuture.complete(null);
  									} else {
  										newShutdownFuture.completeExceptionally(finished.cause());
  									}
  								});
  					} else {
  						newShutdownFuture.complete(null);
  					}
  				} else {
  					newShutdownFuture.complete(null);
  				}
  			});
  
  			return newShutdownFuture;
  		}
  		return clientShutdownFuture.get();
  	}
docs: Shuts down the client and closes all connections.
  
  <p>After a call to this method, all returned futures will be failed.
  
  @return A {@link CompletableFuture} that will be completed when the shutdown process is done.