spark的block管理是通过BlockTransferService定义的方法从远端获取block、将block存储到远程节点。shuffleclient生成过程就会引入blockTransferService。

类的定义如下:

定义了目标节点的主机名和端口号,还定义了批量获取,批量保存,单个block的同步获取和保存。初始化服务和关闭服务方法。

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.spark.network import java.io.Closeable
import java.nio.ByteBuffer import scala.concurrent.{Future, Promise}
import scala.concurrent.duration.Duration
import scala.reflect.ClassTag import org.apache.spark.internal.Logging
import org.apache.spark.network.buffer.{ManagedBuffer, NioManagedBuffer}
import org.apache.spark.network.shuffle.{BlockFetchingListener, ShuffleClient}
import org.apache.spark.storage.{BlockId, StorageLevel}
import org.apache.spark.util.ThreadUtils private[spark]
abstract class BlockTransferService extends ShuffleClient with Closeable with Logging { /**
* Initialize the transfer service by giving it the BlockDataManager that can be used to fetch
* local blocks or put local blocks.
*
* 通过BlockDataManager来初始化BlockTransferService,可以获取和保存blocks
*/
def init(blockDataManager: BlockDataManager): Unit /**
* Tear down the transfer service.
* 关闭服务
*/
def close(): Unit /**
* Port number the service is listening on, available only after [[init]] is invoked.
*/
def port: Int /**
* Host name the service is listening on, available only after [[init]] is invoked.
*/
def hostName: String /**
* 从远程节点获取blocks信息。
* Fetch a sequence of blocks from a remote node asynchronously,
* available only after [[init]] is invoked.
*
* 可以批量按顺序获取blcok,批量获取到block信息。当读取到一个block信息时候触发listener无需等待全部block fetched
*
* Note that this API takes a sequence so the implementation can batch requests, and does not
* return a future so the underlying implementation can invoke onBlockFetchSuccess as soon as
* the data of a block is fetched, rather than waiting for all blocks to be fetched.
*/
override def fetchBlocks(
host: String,
port: Int,
execId: String,
blockIds: Array[String],
listener: BlockFetchingListener): Unit /**
* Upload a single block to a remote node, available only after [[init]] is invoked.
* 上传single block 到一个远端节点
*/
def uploadBlock(
hostname: String,
port: Int,
execId: String,
blockId: BlockId,
blockData: ManagedBuffer,
level: StorageLevel,
classTag: ClassTag[_]): Future[Unit] /**
* A special case of [[fetchBlocks]], as it fetches only one block and is blocking.
* 获取1个block信息。并且同步阻塞等待。
* It is also only available after [[init]] is invoked.
* 获取block。主机、端口、execId、blockId
*/
def fetchBlockSync(host: String, port: Int, execId: String, blockId: String): ManagedBuffer = {
// A monitor for the thread to wait on.
val result = Promise[ManagedBuffer]()
fetchBlocks(host, port, execId, Array(blockId),
new BlockFetchingListener {
override def onBlockFetchFailure(blockId: String, exception: Throwable): Unit = {
result.failure(exception)
} override def onBlockFetchSuccess(blockId: String, data: ManagedBuffer): Unit = {
val ret = ByteBuffer.allocate(data.size.toInt)
ret.put(data.nioByteBuffer())
ret.flip()
result.success(new NioManagedBuffer(ret))
}
})
ThreadUtils.awaitResult(result.future, Duration.Inf)
} /**
* Upload a single block to a remote node, available only after [[init]] is invoked.
* 上传一个block,并且同步阻塞等待。
* This method is similar to [[uploadBlock]], except this one blocks the thread
* until the upload finishes.
*/
def uploadBlockSync(
hostname: String,
port: Int,
execId: String,
blockId: BlockId,
blockData: ManagedBuffer,
level: StorageLevel,
classTag: ClassTag[_]): Unit = {
val future = uploadBlock(hostname, port, execId, blockId, blockData, level, classTag)
ThreadUtils.awaitResult(future, Duration.Inf)
}
}

BlockTransferService默认为NettyBlockTransferService,基于Netty的网络应用框架,提供网络连接。

有两个重要方法fetchBlocks、uploadBlock。即获取和保存block信息。

fetchBlocks:

//获取blocks数据,需要用主机名称,端口号,excutorid和blockids
override def fetchBlocks(
host: String,
port: Int,
execId: String,
blockIds: Array[String],
listener: BlockFetchingListener): Unit = {
logTrace(s"Fetch blocks from $host:$port (executor id $execId)")
try {
val blockFetchStarter = new RetryingBlockFetcher.BlockFetchStarter {
override def createAndStart(blockIds: Array[String], listener: BlockFetchingListener) {
//clientFactory维护了一个client数组,如果指定主机和端口的连接,获取或者创建一个与目标主机和端口的socket连接
val client = clientFactory.createClient(host, port)
new OneForOneBlockFetcher(client, appId, execId, blockIds.toArray, listener).start()
}
} val maxRetries = transportConf.maxIORetries()
if (maxRetries > 0) {
// Note this Fetcher will correctly handle maxRetries == 0; we avoid it just in case there's
// a bug in this code. We should remove the if statement once we're sure of the stability.
new RetryingBlockFetcher(transportConf, blockFetchStarter, blockIds, listener).start()
} else {
blockFetchStarter.createAndStart(blockIds, listener)
}
} catch {
case e: Exception =>
logError("Exception while beginning fetchBlocks", e)
blockIds.foreach(listener.onBlockFetchFailure(_, e))
}
}

通过TransportClientFactory创建一个读取客户端,实现如下:

  public TransportClient createClient(String remoteHost, int remotePort)
throws IOException, InterruptedException {
// Get connection from the connection pool first.
// If it is not found or not active, create a new one.
// Use unresolved address here to avoid DNS resolution each time we creates a client.
final InetSocketAddress unresolvedAddress =
InetSocketAddress.createUnresolved(remoteHost, remotePort); // Create the ClientPool if we don't have it yet.
ClientPool clientPool = connectionPool.get(unresolvedAddress);
if (clientPool == null) {
connectionPool.putIfAbsent(unresolvedAddress, new ClientPool(numConnectionsPerPeer));
clientPool = connectionPool.get(unresolvedAddress);
} int clientIndex = rand.nextInt(numConnectionsPerPeer);
TransportClient cachedClient = clientPool.clients[clientIndex]; if (cachedClient != null && cachedClient.isActive()) {
// Make sure that the channel will not timeout by updating the last use time of the
// handler. Then check that the client is still alive, in case it timed out before
// this code was able to update things.
TransportChannelHandler handler = cachedClient.getChannel().pipeline()
.get(TransportChannelHandler.class);
synchronized (handler) {
handler.getResponseHandler().updateTimeOfLastRequest();
} if (cachedClient.isActive()) {
logger.trace("Returning cached connection to {}: {}",
cachedClient.getSocketAddress(), cachedClient);
return cachedClient;
}
}

意思是维护一个client数组,当所需的客户端不存在的时候,创建一个新的网络连接,然后将谅解保存到client数组中。

final long preResolveHost = System.nanoTime();
final InetSocketAddress resolvedAddress = new InetSocketAddress(remoteHost, remotePort);
final long hostResolveTimeMs = (System.nanoTime() - preResolveHost) / 1000000;
if (hostResolveTimeMs > 2000) {
logger.warn("DNS resolution for {} took {} ms", resolvedAddress, hostResolveTimeMs);
} else {
logger.trace("DNS resolution for {} took {} ms", resolvedAddress, hostResolveTimeMs);
}

uploadBlock:

override def uploadBlock(
hostname: String,
port: Int,
execId: String,
blockId: BlockId,
blockData: ManagedBuffer,
level: StorageLevel,
classTag: ClassTag[_]): Future[Unit] = {
val result = Promise[Unit]()
val client = clientFactory.createClient(hostname, port) // StorageLevel and ClassTag are serialized as bytes using our JavaSerializer.
// Everything else is encoded using our binary protocol.
val metadata = JavaUtils.bufferToArray(serializer.newInstance().serialize((level, classTag))) // Convert or copy nio buffer into array in order to serialize it.
val array = JavaUtils.bufferToArray(blockData.nioByteBuffer())
//通过Netty发送message,构造的Channel对象,new UploadBlock(appId, execId, blockId.toString, metadata, array).toByteBuffer为message
client.sendRpc(new UploadBlock(appId, execId, blockId.toString, metadata, array).toByteBuffer,
new RpcResponseCallback {
override def onSuccess(response: ByteBuffer): Unit = {
logTrace(s"Successfully uploaded block $blockId")
result.success((): Unit)
} override def onFailure(e: Throwable): Unit = {
logError(s"Error while uploading block $blockId", e)
result.failure(e)
}
}) result.future
}

同样通过TransportClientFactory创建一个读取客户端,通过

client.sendRpc(new UploadBlock(appId, execId, blockId.toString, metadata, array).toByteBuffer,
new RpcResponseCallback {
override def onSuccess(response: ByteBuffer): Unit = {
logTrace(s"Successfully uploaded block $blockId")
result.success((): Unit)
} override def onFailure(e: Throwable): Unit = {
logError(s"Error while uploading block $blockId", e)
result.failure(e)
}
}) result.future
}

方法将数据保存到远端节点。其中new UploadBlock(appId, execId, blockId.toString, metadata, array).toByteBuffer为消息体内容,

new RpcResponseCallback {
override def onSuccess(response: ByteBuffer): Unit = {
logTrace(s"Successfully uploaded block $blockId")
result.success((): Unit)
} override def onFailure(e: Throwable): Unit = {
logError(s"Error while uploading block $blockId", e)
result.failure(e)
}
}为回掉函数。

BlockTransferService 实现的更多相关文章

  1. Spark——SparkContext简单分析

    本篇文章就要根据源码分析SparkContext所做的一些事情,用过Spark的开发者都知道SparkContext是编写Spark程序用到的第一个类,足以说明SparkContext的重要性:这里先 ...

  2. Spark数据传输及ShuffleClient(源码阅读五)

    我们都知道Spark的每个task运行在不同的服务器节点上,map输出的结果直接存储到map任务所在服务器的存储体系中,reduce任务有可能不在同一台机器上运行,所以需要远程将多个map任务的中间结 ...

  3. 王家林 大数据Spark超经典视频链接全集[转]

    压缩过的大数据Spark蘑菇云行动前置课程视频百度云分享链接 链接:http://pan.baidu.com/s/1cFqjQu SCALA专辑 Scala深入浅出经典视频 链接:http://pan ...

  4. shuffle过程中的信息传递

    依据Spark1.4版 Spark中的shuffle大概是这么个过程:map端把map输出写成本地文件,reduce端去读取这些文件,然后执行reduce操作. 那么,问题来了: reducer是怎么 ...

  5. spark storage之SparkEnv

    此文旨在对spark storage模块进行分析,整理自己所看所得,等以后再整理. ok,首先看看SparkContext中sparkEnv相关代码: private[spark] def creat ...

  6. What’s new in Spark 1.2.0

    What's new in Spark 1.2.0 1.2.0 was released on 12/18, 2014 在2014年5月30日公布了Spark 1.0 和9月11日公布了Spark1. ...

  7. Spark性能调优之代码方面的优化

    Spark性能调优之代码方面的优化 1.避免创建重复的RDD     对性能没有问题,但会造成代码混乱   2.尽可能复用同一个RDD,减少产生RDD的个数   3.对多次使用的RDD进行持久化(ca ...

  8. Spark源码阅读之存储体系--存储体系概述与shuffle服务

    一.概述 根据<深入理解Spark:核心思想与源码分析>一书,结合最新的spark源代码master分支进行源码阅读,对新版本的代码加上自己的一些理解,如有错误,希望指出. 1.块管理器B ...

  9. Spark Shuffle模块——Suffle Read过程分析

    在阅读本文之前.请先阅读Spark Sort Based Shuffle内存分析 Spark Shuffle Read调用栈例如以下: 1. org.apache.spark.rdd.Shuffled ...

随机推荐

  1. jspersonft有关Table数据绑定(一)

    一:前言 在公司来就学着做报表,觉得这个报表学着还是很有意义的.jspersonft我在网上搜了一些有关的资料但是不是很多,现在就是学一点就记载一点.好记性不如烂笔头嘛! 二:在jspersonft定 ...

  2. 【Foreign】异色弧 [树状数组]

    异色弧 Time Limit: 20 Sec  Memory Limit: 256 MB Description Input Output 仅一行一个整数表示答案. Sample Input 8 1 ...

  3. 【比赛】百度之星2017 初赛Round A

    第一题 题意:给定多组数据P,每次询问P进制下,有多少数字B满足条件:只要数位之和是B的倍数,该数字就是B的倍数. 题解:此题是参考10进制下3和9倍数的特殊性质. 对于10进制,ab=10*a+b= ...

  4. python常用模块补充hashlib configparser logging,subprocess模块

    一.hashlib模板 Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等. 什么是摘要算法呢?摘要算法又称哈希算法.散列算法.它通过一个函数,把任意长度的数据转换为一个长度固定 ...

  5. [转载]基于Redis的Bloomfilter去重(附Python代码)

    前言: “去重”是日常工作中会经常用到的一项技能,在爬虫领域更是常用,并且规模一般都比较大.去重需要考虑两个点:去重的数据量.去重速度.为了保持较快的去重速度,一般选择在内存中进行去重. 数据量不大时 ...

  6. 阿里云OSS Web端直传 服务器签名C#版

    最近用到队里OSS的文件上传,然后阿里官方给的四个服务器签名有Java PHP Python Go四个版本,就是没C#(话说写个C#有多难?) 百度了一下好像也没有,既然这样只能自己动手照着Java版 ...

  7. ios iphone ipad上iframe的宽度会扩大的解决办法

    这个问题,我从网上查了下,好像是属于ios的bug,android,windows都没有问题. 解决办法,就是在iframe加载完成后,设置 iframe里面body的宽度为多少PX. $(" ...

  8. [ Mariadb ] 记录一次MySQL数据库时区的问题

    操作系统:Centos 7数据库:5.5.52-MariaDB 根本问题:由于系统时区不对,造成数据库的时区和数据的时间不正确. 处理办法: # 查看系统时区, [root@mongodb ~]# t ...

  9. js面向对象编程(一):封装(转载)

    一. 生成对象的原始模式 假定我们把猫看成一个对象,它有"名字"和"颜色"两个属性. var Cat = { name : '', color : '' } 现 ...

  10. python接口自动化11-post传data参数案例【转载】

    前言: 前面登录博客园的是传json参数,有些登录不是传json的,如jenkins的登录,本篇以jenkins登录为案例,传data参数. 一.登录jenkins抓包 1.登录jenkins,输入账 ...