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

com.twitter.finagle.client.StackClient.scala Maven / Gradle / Ivy

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

import com.twitter.finagle._
import com.twitter.finagle.param._
import com.twitter.finagle.factory.{
  BindingFactory, RefcountedFactory, StatsFactoryWrapper, TimeoutFactory}
import com.twitter.finagle.filter.{ExceptionSourceFilter, MonitorFilter}
import com.twitter.finagle.loadbalancer.LoadBalancerFactory
import com.twitter.finagle.service._
import com.twitter.finagle.stack.Endpoint
import com.twitter.finagle.stack.nilStack
import com.twitter.finagle.stats.ClientStatsReceiver
import com.twitter.finagle.tracing.{ClientDestTracingFilter, TracingFilter}
import com.twitter.finagle.transport.Transport
import com.twitter.finagle.util.Showable
import com.twitter.util.Var

private[finagle] object StackClient {
  /**
   * Canonical Roles for each Client-related Stack modules.
   */
  object Role extends Stack.Role("StackClient"){
    val loadBalancer = Stack.Role("LoadBalancer")
    val pool = Stack.Role("Pool")
    val requestDraining = Stack.Role("RequestDraining")
    val prepFactory = Stack.Role("PrepFactory")
    /** PrepConn is special in that it's the first role before the `Endpoint` role */
    val prepConn = Stack.Role("PrepConn")
  }

  /**
   * A [[com.twitter.finagle.Stack]] representing an endpoint.
   * Note that this is terminated by a [[com.twitter.finagle.service.FailingFactory]]:
   * users are expected to terminate it with a concrete service factory.
   *
   * @see [[com.twitter.finagle.service.ExpiringService]]
   * @see [[com.twitter.finagle.service.FailFastFactory]]
   * @see [[com.twitter.finagle.client.DefaultPool]]
   * @see [[com.twitter.finagle.service.TimeoutFilter]]
   * @see [[com.twitter.finagle.service.FailureAccrualFactory]]
   * @see [[com.twitter.finagle.service.StatsServiceFactory]]
   * @see [[com.twitter.finagle.tracing.ClientDestTracingFilter]]
   * @see [[com.twitter.finagle.filter.MonitorFilter]]
   * @see [[com.twitter.finagle.filter.ExceptionSourceFilter]]
   */
  def endpointStack[Req, Rep]: Stack[ServiceFactory[Req, Rep]] = {
    // Ensure that we have performed global initialization.
    com.twitter.finagle.Init()

    val stk = new StackBuilder[ServiceFactory[Req, Rep]](nilStack[Req, Rep])
    stk.push(Role.prepConn, identity[ServiceFactory[Req, Rep]](_))
    stk.push(ExpiringService.module)
    stk.push(FailFastFactory.module)
    stk.push(DefaultPool.module)
    stk.push(TimeoutFilter.module)
    stk.push(FailureAccrualFactory.module)
    stk.push(StatsServiceFactory.module)
    stk.push(StatsFilter.module)
    stk.push(ClientDestTracingFilter.module)
    stk.push(MonitorFilter.module)
    stk.push(ExceptionSourceFilter.module)
    stk.result
  }

  /**
   * Creates a default finagle client [[com.twitter.finagle.Stack]].
   * The stack can be configured via [[com.twitter.finagle.Stack.Param]]'s
   * in the finagle package object ([[com.twitter.finagle.param]]) and specific
   * params defined in the companion objects of the respective modules.
   *
   * @see [[com.twitter.finagle.client.StackClient#endpointStack]]
   * @see [[com.twitter.finagle.loadbalancer.LoadBalancerFactory]]
   * @see [[com.twitter.finagle.factory.RefcountedFactory]]
   * @see [[com.twitter.finagle.factory.TimeoutFactory]]
   * @see [[com.twitter.finagle.factory.StatsFactoryWrapper]]
   * @see [[com.twitter.finagle.tracing.TracingFilter]]
   */
  def newStack[Req, Rep]: Stack[ServiceFactory[Req, Rep]] = {
    val stk = new StackBuilder(endpointStack[Req, Rep])
    stk.push(LoadBalancerFactory.module)
    stk.push(Role.requestDraining, (fac: ServiceFactory[Req, Rep]) =>
      new RefcountedFactory(fac))
    stk.push(TimeoutFactory.module)
    stk.push(StatsFactoryWrapper.module)
    stk.push(TracingFilter.module)
    stk.push(Role.prepFactory, identity[ServiceFactory[Req, Rep]](_))
    stk.result
  }
}

/**
 * A [[com.twitter.finagle.Stack]]-based client.
 */
private[finagle] abstract class StackClient[Req, Rep](
  val stack: Stack[ServiceFactory[Req, Rep]],
  val params: Stack.Params
) extends Client[Req, Rep] { self =>
  protected type In
  protected type Out

   /**
    * A convenient type alias for a client dispatcher.
    */
  protected type Dispatcher = Transport[In, Out] => Service[Req, Rep]

  /**
   * Creates a new StackClient with the default stack (StackClient#newStack)
   * and [[com.twitter.finagle.stats.ClientStatsReceiver]].
   */
  def this() = this(
    StackClient.newStack[Req, Rep],
    Stack.Params.empty + Stats(ClientStatsReceiver)
  )

  /**
   * Defines a typed [[com.twitter.finagle.client.Transporter]] for this client.
   * Concrete StackClient implementations are expected to specify this.
   */
  protected val newTransporter: Stack.Params => Transporter[In, Out]

  /**
   * Defines a dispatcher, a function which reconciles the stream based
   * `Transport` with a Request/Response oriented `Service`.
   * Together with a `Transporter`, it forms the foundation of a
   * finagle client. Concrete implementations are expected to specify this.
   *
   * @see [[com.twitter.finagle.dispatch.GenSerialServerDispatcher]]
   */
  protected val newDispatcher: Stack.Params => Dispatcher

  /**
   * Creates a new StackClient with `f` applied to `stack`.
   */
  def transformed(f: Stack[ServiceFactory[Req, Rep]] => Stack[ServiceFactory[Req, Rep]]) =
    copy(stack = f(stack))

  /**
   * Creates a new StackClient with `p` added to the `params`
   * used to configure this StackClient's `stack`.
   */
  def configured[P: Stack.Param](p: P): StackClient[Req, Rep] =
    copy(params = params+p)

  /**
   * A copy constructor in lieu of defining StackClient as a
   * case class.
   */
  def copy(
    stack: Stack[ServiceFactory[Req, Rep]] = self.stack,
    params: Stack.Params = self.params
  ): StackClient[Req, Rep] =
    new StackClient[Req, Rep](stack, params) {
      protected type In = self.In
      protected type Out = self.Out
      protected val newTransporter = self.newTransporter
      protected val newDispatcher = self.newDispatcher
      override protected val endpointer = self.endpointer
    }

  /**
   * A stackable module that creates new `Transports` (via transporter)
   * when applied.
   */
  protected val endpointer = new Stack.Simple[ServiceFactory[Req, Rep]] {
    val role = Endpoint
    val description = "Send requests over the wire"
    def make(next: ServiceFactory[Req, Rep])(implicit prms: Stack.Params) = {
      val Transporter.EndpointAddr(addr) = get[Transporter.EndpointAddr]
      val transporter = newTransporter(prms)
      val dispatcher = newDispatcher(prms)
      ServiceFactory(() => transporter(addr) map dispatcher)
    }
  }

  /** @inheritdoc */
  def newClient(dest: Name, label0: String): ServiceFactory[Req, Rep] = {
    val Stats(stats) = params[Stats]
    val Label(label1) = params[Label]
    // For historical reasons, we have two sources for identifying
    // a client. The most recently set `label0` takes precedence.
    val clientLabel = (label0, label1) match {
      case ("", "") => Showable.show(dest)
      case ("", l1) => l1
      case (l0, l1) => l0
    }

    val clientStack = stack ++ (endpointer +: nilStack)
    val clientParams = params +
      Label(clientLabel) +
      Stats(stats.scope(clientLabel))

    ClientRegistry.register(clientLabel, this)

    dest match {
      case Name.Bound(addr) =>
        clientStack.make(clientParams +
          LoadBalancerFactory.ErrorLabel(Showable.show(dest)) +
          LoadBalancerFactory.Dest(addr))

      case Name.Path(path) =>
        val errorLabel = BindingFactory.showWithDtabLocal(path)
        val clientParams1 = clientParams + LoadBalancerFactory.ErrorLabel(errorLabel)

        val newStack: Var[Addr] => ServiceFactory[Req, Rep] =
          addr => clientStack.make(clientParams1 + LoadBalancerFactory.Dest(addr))

        new BindingFactory(path, newStack, stats.scope("interpreter"))
    }
  }
}

/**
 * A [[com.twitter.finagle.Stack Stack]]-based client which preserves
 * `Like` client semantics. This makes it appropriate for implementing rich
 * clients, since the rich type can be preserved without having to drop down
 * to StackClient[Req, Rep] when making changes.
 */
private[finagle]
abstract class StackClientLike[Req, Rep, Repr <: StackClientLike[Req, Rep, Repr]](
  client: StackClient[Req, Rep]
) extends Client[Req, Rep] {
  val stack = client.stack

  protected def newInstance(client: StackClient[Req, Rep]): Repr

  /**
   * Creates a new `Repr` with an underlying StackClient where `p` has been
   * added to the `params` used to configure this StackClient's `stack`.
   */
  def configured[P: Stack.Param](p: P): Repr =
    newInstance(client.configured(p))

  /**
   * Creates a new `Repr` with an underlying StackClient where `f` has been
   * applied to `stack`.
   */
  protected def transformed(f: Stack[ServiceFactory[Req, Rep]] => Stack[ServiceFactory[Req, Rep]]): Repr =
    newInstance(client.transformed(f))

  /** @inheritdoc */
  def newClient(dest: Name, label: String) = client.newClient(dest, label)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy