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

com.qifun.bcp.BcpClient.scala Maven / Gradle / Ivy

/*
 * scala-bcp
 * Copyright 2014 深圳岂凡网络有限公司 (Shenzhen QiFun Network Corp., LTD)
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.qifun.bcp

import java.nio.channels.AsynchronousSocketChannel
import java.security.SecureRandom
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import scala.PartialFunction
import scala.concurrent.duration._
import scala.concurrent.stm.InTxn
import scala.concurrent.stm.Ref
import scala.concurrent.stm.Txn
import scala.concurrent.stm.atomic
import scala.reflect.classTag
import scala.util.control.Exception.Catcher
import com.dongxiguo.fastring.Fastring.Implicits._
import com.qifun.bcp.Bcp._
import com.qifun.bcp.BcpSession._
import com.qifun.statelessFuture.Future
import com.qifun.statelessFuture.util.Blocking
import com.qifun.statelessFuture.util.CancellablePromise
import com.qifun.statelessFuture.util.Sleep
import java.util.concurrent.CancellationException

object BcpClient {

  private implicit val (logger, formatter, appender) = ZeroLoggerFactory.newLogger(this)

  private[BcpClient] final class Stream(socket: AsynchronousSocketChannel) extends BcpSession.Stream(socket) {
    // 客户端专有的数据结构,比如Timer
    val busyPromise = Ref.make[CancellablePromise[Unit]]
    val connectionState: Ref[ConnectionState] = Ref(ConnectionIdle)
  }

  private[BcpClient] final class Connection extends BcpSession.Connection[Stream] {
  }

  private val sessionIdGenerator = new SecureRandom

}

/**
 * BCP协议客户端
 *
 * 每个BCP客户端管理多个TCP连接,最多不超过[[Bcp.MaxConnectionsPerSession]]。
 *
 * BCP客户端根据网络状况决定要增加TCP连接还是减少TCP连接。
 *
 * 对于每个TCP连接,有空闲、繁忙和迟缓三种状态:
 *
 *  - 如果BCP客户端从某个TCP连接发出的所有[[Bcp.AcknowledgeRequired]],都收到了对应的[[Bcp.Acknowledge]],
 *    那么这个TCP连接是空闲状态。
 *  - 如果某个TCP连接,原本是空闲状态,接着发送了一个[[Bcp.AcknowledgeRequired]],
 *    那么这个TCP连接变成繁忙状态。
 *  - 如果某个TCP连接的繁忙状态保持了[[Bcp.BusyTimeout]],还没有变回空闲状态,
 *    那么这个TCP连接变成迟缓状态。
 *
 * 每当一个BCP客户端的所有TCP连接都变成迟缓状态,且BCP客户端管理的TCP连接数尚未达到上限,BCP客户端建立新TCP连接。
 *
 * 如果一个BCP客户端管理的TCP连接数量大于一,且这个BCP客户端所属的空闲TCP连接数量大于零,那么这个BCP客户端是过剩状态。
 *
 * 如果BCP客户端连续[[Bcp.IdleTimeout]],都在过剩状态,那么这个BCP客户端关掉一个TCP连接。
 *
 */
abstract class BcpClient(sessionId: Array[Byte]) extends BcpSession[BcpClient.Stream, BcpClient.Connection] {

  def this() = this{
    val id = Array.ofDim[Byte](NumBytesSessionId)
    BcpClient.sessionIdGenerator.nextBytes(id)
    id
  }
  // TODO 添加一个构造函数,崩溃时重置功能

  // TODO 添加一个renew接口,Unavailable太长时间时重置功能

  import BcpClient.{ logger, formatter, appender }

  private val reconnectPromise = Ref.make[CancellablePromise[Unit]]
  private val idlePromise = Ref.make[CancellablePromise[Unit]]
  private val nextConnectionId = Ref(0)
  private val isConnecting = Ref(false)
  private val isShutedDown = Ref(false)

  override private[bcp] final def newConnection = new BcpClient.Connection

  protected def connect(): Future[AsynchronousSocketChannel]

  protected def executor: ScheduledExecutorService

  override private[bcp] final def internalExecutor: ScheduledExecutorService = executor

  override private[bcp] final def release()(implicit txn: InTxn) {
    isShutedDown() = true
    val oldReconnectPromise = reconnectPromise()
    reconnectPromise() = null;
    if (oldReconnectPromise != null) {
      Txn.afterCommit(_ => oldReconnectPromise.cancel())
    }
    val oldIdlePromise = idlePromise()
    idlePromise() = null;
    if (oldIdlePromise != null) {
      Txn.afterCommit(_ => oldIdlePromise.cancel())
    }
  }

  private def busyComplete(busyConnection: BcpClient.Connection, thisTimer: CancellablePromise[Unit]): Unit = {
    atomic { implicit txn =>
      if (busyConnection.stream() != null && busyConnection.stream().busyPromise() == thisTimer) {
        busyConnection.stream().connectionState() = ConnectionSlow
        increaseConnection()
        busyConnection.stream().busyPromise() = null
      }
    }
  }

  override private[bcp] final def busy(busyConnection: BcpClient.Connection)(implicit txn: InTxn): Unit = {
    logger.info("the connection is busy!")
    val oldReconnectPromise = reconnectPromise()
    if (oldReconnectPromise != null) {
      reconnectPromise() = null
      Txn.afterCommit(_ => oldReconnectPromise.cancel())
    }
    val oldBusyPromise = busyConnection.stream().busyPromise()
    if (oldBusyPromise == null) {
      implicit def catcher: Catcher[Unit] = {
        case _: CancellationException =>
          logger.finer("The busy timer is cancelled!")
        case e: Exception =>
          logger.warning(e)
      }
      val newBusyPromise = CancellablePromise[Unit]
      busyConnection.stream().busyPromise() = newBusyPromise
      Txn.afterCommit { _ =>
        newBusyPromise.foreach(_ => busyComplete(busyConnection, newBusyPromise))
        Sleep.start(newBusyPromise, executor, BusyTimeout)
      }
      busyConnection.stream().connectionState() = ConnectionBusy
      // bcp-client 不是过剩状态
      if (!(connections.size > 1 &&
        connections.exists(connection =>
          connection._2.stream() != null && connection._2.stream().connectionState() == ConnectionIdle))) {
        val oldIdlePromise = idlePromise()
        if (oldIdlePromise != null) {
          idlePromise() = null
          Txn.afterCommit(_ => oldIdlePromise.cancel())
        }
      }
    }
  }

  override private[bcp] final def idle(connection: BcpClient.Connection)(implicit txn: InTxn): Unit = {
    logger.info("the connection is idle!")
    val busyPromise = connection.stream().busyPromise()
    if (busyPromise != null) {
      connection.stream().busyPromise() = null
      Txn.afterCommit(_ => busyPromise.cancel())
    }
    connection.stream().connectionState() = ConnectionIdle
    checkFinishConnection()
  }

  override private[bcp] final def close(closeConnection: BcpClient.Connection)(implicit txn: InTxn): Unit = {
    val connectionSize = connections.size
    if (closeConnection.stream() != null) {
      val busyPromise = closeConnection.stream().busyPromise()
      if (busyPromise != null) {
        closeConnection.stream().busyPromise() = null
        Txn.afterCommit(_ => busyPromise.cancel())
      }
    }
    if (connections.forall(connection =>
      connection._2 == closeConnection || connection._2.stream() == null) &&
      connectionSize < MaxConnectionsPerSession) {
      startReconnectTimer()
    }
    if (connectionSize >= MaxConnectionsPerSession &&
      connections.forall(connection =>
        connection._2 == closeConnection || connection._2.stream() == null)) {
      internalInterrupt()
    }
  }

  private def afterConnect(socket: AsynchronousSocketChannel, connectionId: Int): Future[Unit] = Future {
    logger.fine(fast"bcp client connect server success, socket: ${socket}")
    val stream = new BcpClient.Stream(socket)
    atomic { implicit txn =>
      if (!isShutedDown()) {
        Txn.afterCommit { _ =>
          BcpIo.enqueueHead(stream, ConnectionHead(sessionId, false, connectionId))
          logger.fine(
            fast"bcp client send head to server success, sessionId: ${sessionId.toSeq} , connectionId: ${connectionId}")
        }
        addStream(connectionId, stream)
        isConnecting() = false
      } else {
        Txn.afterCommit { _ =>
          socket.close()
        }
      }
    }
    stream.flush()
  }

  private def tryIncreaseConnection(connectionId: Int) {
    val connectFuture = Future {
      val socket = connect().await
      afterConnect(socket, connectionId).await
    }
    implicit def catcher: Catcher[Unit] = {
      case e: Exception => {
        logger.severe(e)
        atomic { implicit txn =>
          if (!isShutedDown()) {
            startReconnectTimer()
          }
        }
      }
    }
    for (_ <- connectFuture) {
      logger.fine("Increase connection success.")
    }
  }

  private final def increaseConnection()(implicit txn: InTxn) {
    if (!isConnecting() &&
      connections.size < MaxConnectionsPerSession &&
      connections.count(_._2.stream() != null) < MaxActiveConnectionsPerSession &&
      connections.forall(connection =>
        connection._2.stream() == null || connection._2.stream().connectionState() == ConnectionSlow)) {
      isConnecting() = true
      val connectionId = nextConnectionId() + 1
      nextConnectionId() = nextConnectionId() + 1
      Txn.afterCommit { _ =>
        tryIncreaseConnection(connectionId)
      }
    }
  }

  def idleComplete(thisTimer: CancellablePromise[Unit]): Unit = {
    atomic { implicit txn =>
      if (idlePromise() == thisTimer) {
        connections.find(connection =>
          (connection._2.stream() != null) &&
            (connection._2.stream().connectionState() == ConnectionIdle)) match {
          case Some((connectionId, toFinishConnection)) =>
            finishConnection(connectionId, toFinishConnection)
          case None =>
        }
        idlePromise() == null
      }
    }
  }

  private final def checkFinishConnection()(implicit txn: InTxn) {
    if (connections.size > 1 &&
      connections.exists(connection =>
        connection._2.stream() != null && connection._2.stream().connectionState() == ConnectionIdle)) { // 过剩状态
      if (idlePromise() == null) {
        implicit def catcher: Catcher[Unit] = {
          case _: CancellationException =>
            logger.finer("The finish connection is cancelled!")
          case e: Exception =>
            logger.warning(e)
        }
        val newIdlePromise = CancellablePromise[Unit]
        idlePromise() = newIdlePromise
        Txn.afterCommit { _ =>
          newIdlePromise.foreach { _ => idleComplete(newIdlePromise) }
          Sleep.start(newIdlePromise, executor, IdleTimeout)
        }

      }
    }
  }

  private def reconnectComplete(thisTimer: CancellablePromise[Unit]): Unit = {
    atomic { implicit txn =>
      if (reconnectPromise() == thisTimer) {
        reconnectPromise() = null
        increaseConnection()
      }
    }
  }

  private final def startReconnectTimer()(implicit txn: InTxn): Unit = {
    if (reconnectPromise() == null) {
      implicit def catcher: Catcher[Unit] = {
        case _: CancellationException =>
          logger.finer("The reconnect timer is cancelled!")
        case e: Exception =>
          logger.warning(e)
      }
      val newReconnectPromise = CancellablePromise[Unit]
      reconnectPromise() = newReconnectPromise
      Txn.afterCommit { _ =>
        newReconnectPromise.foreach { _ => reconnectComplete(newReconnectPromise) }
        Sleep.start(newReconnectPromise, executor, ReconnectTimeout)
      }
      reconnectPromise() = newReconnectPromise
    }
  }

  final def start() {
    atomic { implicit txn: InTxn =>
      increaseConnection()
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy