@Override
  public void shutdown() {
    logger.info("Shutting down NettyServer");
    if (bossGroup != null && workerGroup != null && (!bossGroup.isTerminated() || !workerGroup.isTerminated())) {
      long shutdownBeginTime = System.currentTimeMillis();
      // Shutdown boss first to stop accepting new connections, then close existing connections.
      bossGroup.shutdownGracefully();
      workerGroup.shutdownGracefully();
      try {
        if (!(workerGroup.awaitTermination(30, TimeUnit.SECONDS) && bossGroup.awaitTermination(30, TimeUnit.SECONDS))) {
          logger.error("NettyServer shutdown failed after waiting for 30 seconds");
          nettyMetrics.nettyServerShutdownError.inc();
        }
      } catch (InterruptedException e) {
        logger.error("NettyServer termination await was interrupted. Shutdown may have been unsuccessful", e);
        nettyMetrics.nettyServerShutdownError.inc();
      } finally {
        long shutdownTime = System.currentTimeMillis() - shutdownBeginTime;
        logger.info("NettyServer shutdown took {} ms", shutdownTime);
        nettyMetrics.nettyServerShutdownTimeInMs.update(shutdownTime);
      }
    }
  }
Creates a new instance of NettyServer.
@param nettyConfig the {@link NettyConfig} instance that defines the configuration parameters for the NettyServer.
@param nettyMetrics the {@link NettyMetrics} instance to use to record metrics.
@param channelInitializers a {@link Map} from port number to the {@link ChannelInitializer} used to initialize
                           a new channel on that port.
ambry-rest/src/main/java/com/github/ambry/rest/NettyServer.java