All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twitter.finagle.netty4.Netty4Transporter.scala Maven / Gradle / Ivy

The newest version!
package com.twitter.finagle.netty4

import com.twitter.finagle.{Failure, Stack}
import com.twitter.finagle.client.{LatencyCompensation, Transporter}
import com.twitter.finagle.codec.{FrameDecoder, FrameEncoder}
import com.twitter.finagle.netty4.transport.ChannelTransport
import com.twitter.finagle.netty4.channel._
import com.twitter.finagle.transport.Transport
import com.twitter.util.{Future, Promise, NonFatal}
import io.netty.bootstrap.Bootstrap
import io.netty.channel._
import io.netty.channel.socket.nio.NioSocketChannel
import java.lang.{Boolean => JBool, Integer => JInt}
import java.net.SocketAddress
import java.nio.channels.UnresolvedAddressException

private[finagle] object Netty4Transporter {
  /**
   * A [[com.twitter.finagle.Stack.Param]] used to configure the ability to
   * exert backpressure by only reading from the Channel when the [[Transport]] is
   * read.
   */
  private[finagle] case class Backpressure(backpressure: Boolean) {
    def mk(): (Backpressure, Stack.Param[Backpressure]) = (this, Backpressure.param)
  }

  private[finagle] object Backpressure {
    implicit val param: Stack.Param[Backpressure] =
      Stack.Param(Backpressure(backpressure = true))
  }

  // this is marked as rejected for historical reasons--we marked it as a WriteException
  // in the netty3 implementation, and we don't want to change behavior when moving to
  // netty4.
  private[this] val CancelledConnectionEstablishment =
    Failure.rejected("connection establishment was cancelled")

  private[this] def build[In, Out](
    init: ChannelInitializer[Channel],
    params: Stack.Params
  ): Transporter[In, Out] = new Transporter[In, Out] {
    def apply(addr: SocketAddress): Future[Transport[In, Out]] = {
      val Transport.Options(noDelay, reuseAddr) = params[Transport.Options]
      val LatencyCompensation.Compensation(compensation) = params[LatencyCompensation.Compensation]
      val Transporter.ConnectTimeout(connectTimeout) = params[Transporter.ConnectTimeout]
      val Transport.BufferSizes(sendBufSize, recvBufSize) = params[Transport.BufferSizes]
      val Backpressure(backpressure) = params[Backpressure]
      val param.Allocator(allocator) = params[param.Allocator]

      // max connect timeout is ~24.8 days
      val compensatedConnectTimeoutMs =
        (compensation + connectTimeout).inMillis.min(Int.MaxValue)

      val bootstrap =
        new Bootstrap()
          .group(WorkerPool)
          .channel(classOf[NioSocketChannel])
          .option(ChannelOption.ALLOCATOR, allocator)
          .option[JBool](ChannelOption.TCP_NODELAY, noDelay)
          .option[JBool](ChannelOption.SO_REUSEADDR, reuseAddr)
          .option[JBool](ChannelOption.AUTO_READ, !backpressure) // backpressure! no reads on transport => no reads on the socket
          .option[JInt](ChannelOption.CONNECT_TIMEOUT_MILLIS, compensatedConnectTimeoutMs.toInt)
          .handler(init)

      val Transport.Liveness(_, _, keepAlive) = params[Transport.Liveness]
      keepAlive.foreach(bootstrap.option[JBool](ChannelOption.SO_KEEPALIVE, _))
      sendBufSize.foreach(bootstrap.option[JInt](ChannelOption.SO_SNDBUF, _))
      recvBufSize.foreach(bootstrap.option[JInt](ChannelOption.SO_RCVBUF, _))

      val nettyConnectF = bootstrap.connect(addr)

      val transportP = Promise[Transport[In, Out]]()
      // try to cancel the connect attempt if the transporter's promise is interrupted.
      transportP.setInterruptHandler { case _ => nettyConnectF.cancel(true /* mayInterruptIfRunning */) }

      nettyConnectF.addListener(new ChannelFutureListener {
        def operationComplete(channelF: ChannelFuture): Unit = {
          if (channelF.isCancelled()) transportP.setException(CancelledConnectionEstablishment)
          else if (channelF.cause != null) transportP.setException(channelF.cause match {
            case e: UnresolvedAddressException => e
            case NonFatal(e) => Failure.rejected(e)
          })
          else transportP.setValue(new ChannelTransport[In, Out](channelF.channel()))
        }
      })

      transportP
    }
  }

  /**
   * transporter constructor for protocols that need direct access to the netty pipeline
   * (ie; finagle-http)
   */
  def apply[In, Out](
    pipeCb: ChannelPipeline => Unit,
    params: Stack.Params
  ): Transporter[In, Out] = {
    val init = new RawNetty4ClientChannelInitializer[In, Out](params, pipeCb)

    build(init, params)
  }

  /**
   * transporter constructor for protocols which are entirely implemented in
   * dispatchers (ie; finagle-mux, finagle-mysql)
   */
  def apply[In, Out](
    enc: Option[FrameEncoder[In]],
    decoderFactory: Option[() => FrameDecoder[Out]],
    params: Stack.Params
  ): Transporter[In, Out] = {
    val init = new Netty4ClientChannelInitializer[In, Out](params, enc, decoderFactory)

    build(init, params)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy