Spark源码精读一线程池之消息派发线程池

消息派发器中的线程池“dispatcher-event-loop”

这个线程池,是用来派发Spark中Endpoint的消息的,比如OnStart,RpcMessage等。
这个线程池创建的时机是在Dispatcher对象创建的初始化过程完成的。
这个线程池的线程数选择是计算机的CPU核数,而且采用的是Fixed线程池。

  /** Thread pool used for dispatching messages. */
  private val threadpool: ThreadPoolExecutor = {
    val numThreads = nettyEnv.conf.getInt("spark.rpc.netty.dispatcher.numThreads",
      math.max(2, Runtime.getRuntime.availableProcessors()))
    val pool = ThreadUtils.newDaemonFixedThreadPool(numThreads, "dispatcher-event-loop")
    for (i <- 0 until numThreads) {
      pool.execute(new MessageLoop)
    }
    pool
  }

派发消息的线程的执行策略,当每一个线程启动且都在运行它们的run方法时:

  1. 一开始,所有的派发线程都通过val data = receivers.take()操作阻塞在共享阻塞队列上
  2. 当有端点通过注册操作将端点通过receivers.offer()操作到Dispatcher的端点阻塞队列时,所有的派发线程被唤醒。由于take操作是加锁操作,有且只有一个线程能获取到该锁takeLock,意味着,唤醒的线程中有且只有一个线程能够获取到注册的端点。按照时间顺序,第一个端点是在创建完Netty服务端之后注册的endpoint-verifier端点。
  3. 获取到端点的线程继续执行data.inbox.process(Dispatcher.this)。表示,每一个端点处理自己的邮箱inbox内存放的消息InboxMessage
  4. 从该邮箱inbox中获取到消息InboxMessage,消息存放到该集合的时机在创建端点EndpointData的时候(如果分不清EndpointData,RpcEndpoint,NettyRpcEndpointRef之间的关系,看Spark源码精读一心跳机制)。
  5. 端点的消息匹配逻辑。这里讲述的是注册endpoint-verifier端点。此时处理的消息是OnStart。即启动endpoint-verifier。其他消息,比如RpcMessage,可以看Spark源码精读一心跳机制

混洗数据客户端线程池“shuffle-client”

这个线程池其实是Netty中的线程池。不再赘述其实现逻辑,详细看Netty网络编程模型

Netty的超时调度器“netty-rpc-env-timeout”

  val timeoutScheduler = ThreadUtils.newDaemonSingleThreadScheduledExecutor("netty-rpc-env-timeout")

检测一个Rpc请求是否超时。
该检测实现逻辑在NettyRpcEnv的ask函数中:

  1. 创建一个对象名为promise的不可变Promise对象val promise = Promise[Any]()。这个Promise是一个object,可视作Java类的单例对象。由于在该Promise中,定义了apply方法def apply[T](): Promise[T] = new impl.Promise.DefaultPromise[T](),因此,可简写成Promise[Any]()否则"Promise.type does not take parameters",这是语法。 在实例化DefaultPromise对象时,通过updateState(null, Nil) // The promise is incomplete and has no callbacks设置了初始值。
  2. 定义一个本地环境的许可p
  3. 通过dispatcher投递消息跟许可p
  4. 当执行完dispatcher.postLocalMessage(message, p)后,promise的值被设置成Success(true)。通过context.reply(true)回调onSuccess函数完成该promise的设值操作。
  5. 通过该超时调度器定时执行onFailure函数
    val timeoutCancelable = timeoutScheduler.schedule(new Runnable {
        override def run(): Unit = {
          onFailure(new TimeoutException(s"Cannot receive any reply in ${timeout.duration}"))
        }
      }, timeout.duration.toNanos, TimeUnit.NANOSECONDS)
    
  6. 如果promise的值为Success(True),则取消定时任务onFailure的执行。
    // 发送消息的操作完成,取消定时任务onFailure
    promise.future.onComplete { v =>
        timeoutCancelable.cancel(true)
    }(ThreadUtils.sameThread)
    

Netty的RPC连接线程池“netty-rpc-connection”

  // Because TransportClientFactory.createClient is blocking, we need to run it in this thread pool
  // to implement non-blocking send/ask.
  // TODO: a non-blocking TransportClientFactory.createClient in future
  private[netty] val clientConnectionExecutor = ThreadUtils.newDaemonCachedThreadPool(
    "netty-rpc-connection",
    conf.getInt("spark.rpc.connect.threads", 64))

混洗数据服务端的线程池“shuffle-server”

Netty服务端流程详见Netty的编程模型

    // 定义函数变量 函数变量名: 函数类型 = { 函数表达式 }
    // 这个函数类型组成 入参类型 => 函数结果二元组
	val startNettyRpcEnv: Int => (NettyRpcEnv, Int) = { actualPort =>
        // 由startService=startNettyRpcEnv触发
        // val (service, port) = startService(tryPort)
        nettyEnv.startServer(config.bindAddress, actualPort)
        (nettyEnv, nettyEnv.address.port)
      }
    try {
      Utils.startServiceOnPort(config.port, startNettyRpcEnv, sparkConf, config.name)._1
    } catch {
      case NonFatal(e) =>
        nettyEnv.shutdown()
        throw e
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值