code: public TransportChannelHandler initializePipeline(
        SocketChannel channel,
        RpcHandler channelRpcHandler) {
      try {
        TransportChannelHandler channelHandler = createChannelHandler(channel, channelRpcHandler);
        ChunkFetchRequestHandler chunkFetchHandler =
          createChunkFetchHandler(channelHandler, channelRpcHandler);
        ChannelPipeline pipeline = channel.pipeline()
          .addLast("encoder", ENCODER)
          .addLast(TransportFrameDecoder.HANDLER_NAME, NettyUtils.createFrameDecoder())
          .addLast("decoder", DECODER)
          .addLast("idleStateHandler",
            new IdleStateHandler(0, 0, conf.connectionTimeoutMs() / 1000))
          // NOTE: Chunks are currently guaranteed to be returned in the order of request, but this
          // would require more logic to guarantee if this were not part of the same event loop.
          .addLast("handler", channelHandler);
        // Use a separate EventLoopGroup to handle ChunkFetchRequest messages for shuffle rpcs.
        if (chunkFetchWorkers != null) {
          pipeline.addLast(chunkFetchWorkers, "chunkFetchHandler", chunkFetchHandler);
        }
        return channelHandler;
      } catch (RuntimeException e) {
        logger.error("Error while initializing Netty pipeline", e);
        throw e;
      }
    }
docs: Initializes a client or server Netty Channel Pipeline which encodes/decodes messages and
  has a {@link org.apache.spark.network.server.TransportChannelHandler} to handle request or
  response messages.
  
  @param channel The channel to initialize.
  @param channelRpcHandler The RPC handler to use for the channel.
  
  @return Returns the created TransportChannelHandler, which includes a TransportClient that can
  be used to communicate on this channel. The TransportClient is directly associated with a
  ChannelHandler to ensure all users of the same channel get the same TransportClient object.