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

com.twitter.zk.Connector.scala Maven / Gradle / Ivy

The newest version!
package com.twitter.zk

import java.util.concurrent.atomic.AtomicReference

import scala.annotation.tailrec

import org.apache.zookeeper.ZooKeeper

import com.twitter.logging.Logger
import com.twitter.util.Future

trait Connector {

  import Connector.EventHandler

  val name = "zk-connector"
  protected lazy val log = Logger.get(name)

  private[this] val listeners = new AtomicReference[List[EventHandler]](Nil)

  protected[this] val sessionBroker: EventBroker = new EventBroker

  // a broker may only be used for 1:1 communication, so we fan-out event notifications
  sessionBroker.recv foreach { event =>
    val listening = listeners.get()
    log.debug("propagating event to %d listeners %s", listening.size, event)
    val stateEvent = StateEvent(event)
    listening.foreach { listener =>
      if (listener.isDefinedAt(stateEvent)) {
        try {
          listener(stateEvent)
        } catch {
          case e: Throwable => log.error(e, "Exception in connection event listener")
        }
      } else log.debug("listener does not handle %s", event)
    }
  }

  @tailrec
  final def onSessionEvent(f: EventHandler) {
    val list = listeners.get()
    if (!listeners.compareAndSet(list, f :: list)) onSessionEvent(f)
  }

  /** Connect to a ZooKeeper cluster and yield a handle once the connection is complete. */
  def apply(): Future[ZooKeeper]

  /** Disconnect from the ZooKeeper server. */
  def release(): Future[Unit]
}

object Connector {
  type EventHandler = PartialFunction[StateEvent, Unit]

  /**
   * Dispatches requests across several connectors.
   *
   * Session events from all Connnectors are published on the session broker; however consumers
   * of these events cannot know which specific connection the event was fired on.
   */
  case class RoundRobin(connectors: Connector*) extends Connector {
    require(connectors.length > 0)
    override val name = "round-robin-zk-connector:%d".format(connectors.length)

    private[this] var index = 0
    protected[this] def nextConnector() = {
      val i = synchronized {
        if (index == Int.MaxValue ) {
          index = 0
        }
        index = index + 1
        index % connectors.length
      }
      log.trace("connector %d of %d", i+1, connectors.length)
      connectors(i)
    }

    connectors foreach {
      _ onSessionEvent { case event => sessionBroker.send(event()).sync() }
    }

    def apply(): Future[ZooKeeper] = nextConnector().apply()

    /** Disconnect from all ZooKeeper servers. */
    def release(): Future[Unit] = Future.join {
      log.trace("release")
      connectors map { _.release() }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy