HDFS源码分析之DataXceiverServer
DataXceiverServer是Hadoop分布式文件系统HDFS的从节点--数据节点DataNode上的一个后台工作线程,它类似于一个小型的服务器,被用来接收数据读写请求,并为每个请求创建一个工作线程以进行请求的响应。那么,有以下几个问题:
1、DataXceiverServer是什么?
2、DataXceiverServer是如何初始化的?
3、DataXceiverServer是如何工作的?
带着这些问题,本文将带着你进入DataNode的DataXceiverServer世界。
一、DataXceiverServer是什么?
DataXceiverServer是数据节点DataNode上一个用于接收数据读写请求的后台工作线程,为每个数据读写请求创建一个单独的线程去处理。它的成员变量如下:
- // PeerServer是一个接口,实现了它的TcpPeerServer封装饿了一个ServerSocket,提供了Java Socket服务端的功能
- private final PeerServer peerServer;
- // 该DataXceiverServer所属DataNode实例datanode
- private final DataNode datanode;
- // Peer所在线程的映射集合peers
- private final HashMap<Peer, Thread> peers = new HashMap<Peer, Thread>();
- // Peer与DataXceiver的映射集合peersXceiver
- private final HashMap<Peer, DataXceiver> peersXceiver = new HashMap<Peer, DataXceiver>();
- // DataXceiverServer是否已关闭的标志位closed
- private boolean closed = false;
- /**
- * Maximal number of concurrent xceivers per node.
- * Enforcing the limit is required in order to avoid data-node
- * running out of memory.
- *
- * 每个节点并行的最大DataXceivers数目。
- * 为了避免dataNode运行内存溢出,执行这个限制是必须的。
- * 定义是默认值为4096.
- */
- int maxXceiverCount =
- DFSConfigKeys.DFS_DATANODE_MAX_RECEIVER_THREADS_DEFAULT;
- // 集群数据块平衡节流器balanceThrottler
- final BlockBalanceThrottler balanceThrottler;
- /**
- * We need an estimate for block size to check if the disk partition has
- * enough space. Newer clients pass the expected block size to the DataNode.
- * For older clients we just use the server-side default block size.
- *
- * 我们需要估计块大小以检测磁盘分区是否有足够的空间。
- * 新客户端传递预期块大小给DataNode。
- * 对于旧客户端而言我们仅仅使用服务器端默认的块大小。
- */
- final long estimateBlockSize;
其中,PeerServer类型的peerServer,实际上是DataXceiverServer实现功能最重要的一个类,在DataXceiverServer实例构造时,实际上传入的是实现了PeerServer接口的TcpPeerServer类,该类内部封装了一个ServerSocket,提供了Java Socket服务端的功能,用于监听来自客户端或其他DataNode的数据读写请求。
DataXceiverServer内部还存在对于其载体DataNode的实例datanode,这样该线程就能随时获得DataNode状态、提供的一些列服务等;
peers和peersXceiver是DataXceiverServer内部关于peer的两个数据结构,一个是Peer与其所在线程映射集合peers,另一个则是Peer与DataXceiver的映射集合peersXceiver,均是HashMap类型。Peer是什么呢?实际上就是对Socket的封装;
closed为DataXceiverServer是否已关闭的标志位;
maxXceiverCount为每个DataNode节点并行的最大DataXceivers数目,为了避免dataNode运行内存溢出,执行这个限制是必须的;
balanceThrottler是DataXceiverServer内部一个关于集群中数据库平衡的节流器的实现,它实现了对于数据块移动时带宽、数量的控制。
二、DataXceiverServer是如何初始化的?
在数据节点DataNode进程启动的startDataNode()方法中,会调用initDataXceiver()方法,完成DataXceiverServer的初始化,代码如下:
- private void initDataXceiver(Configuration conf) throws IOException {
- // find free port or use privileged port provided
- // 找一个自由端口或使用已提供的特权端口
- // 构造TcpPeerServer实例tcpPeerServer,它实现了PeerServer接口,提供了ServerSocket的功能
- TcpPeerServer tcpPeerServer;
- if (secureResources != null) {// 如果secureResources存在,根据secureResources创建tcpPeerServer
- tcpPeerServer = new TcpPeerServer(secureResources);
- } else {// 否则,根据配置信息创建tcpPeerServer
- tcpPeerServer = new TcpPeerServer(dnConf.socketWriteTimeout,
- DataNode.getStreamingAddr(conf));
- }
- // 设置数据接收缓冲区大小,默认为128KB
- tcpPeerServer.setReceiveBufferSize(HdfsConstants.DEFAULT_DATA_SOCKET_SIZE);
- // 获取Socket地址InetSocketAddress,赋值给DataNode成员变量streamingAddr
- streamingAddr = tcpPeerServer.getStreamingAddr();
- LOG.info("Opened streaming server at " + streamingAddr);
- // 构造名字为dataXceiverServer的线程组threadGroup
- this.threadGroup = new ThreadGroup("dataXceiverServer");
- // 构造DataXceiverServer实例xserver,传入tcpPeerServer
- xserver = new DataXceiverServer(tcpPeerServer, conf, this);
- // 构造dataXceiverServer守护线程,并将xserver加入线程组threadGroup
- this.dataXceiverServer = new Daemon(threadGroup, xserver);
- // 将线程组里的所有线程设置为设置为守护线程,方便虚拟机退出时自动销毁
- this.threadGroup.setDaemon(true); // auto destroy when empty
- // 如果系统配置的参数dfs.client.read.shortcircuit为true(默认为false),
- // 或者配置的参数dfs.client.domain.socket.data.traffic为true(默认为false),
- //
- if (conf.getBoolean(DFSConfigKeys.DFS_CLIENT_READ_SHORTCIRCUIT_KEY,
- DFSConfigKeys.DFS_CLIENT_READ_SHORTCIRCUIT_DEFAULT) ||
- conf.getBoolean(DFSConfigKeys.DFS_CLIENT_DOMAIN_SOCKET_DATA_TRAFFIC,
- DFSConfigKeys.DFS_CLIENT_DOMAIN_SOCKET_DATA_TRAFFIC_DEFAULT)) {
- DomainPeerServer domainPeerServer =
- getDomainPeerServer(conf, streamingAddr.getPort());
- if (domainPeerServer != null) {
- this.localDataXceiverServer = new Daemon(threadGroup,
- new DataXceiverServer(domainPeerServer, conf, this));
- LOG.info("Listening on UNIX domain socket: " +
- domainPeerServer.getBindPath());
- }
- }
- // 构造短路注册实例
- this.shortCircuitRegistry = new ShortCircuitRegistry(conf);
- }
整个初始化工作很简单:
1、创建构造DataXceiverServer需要的TcpPeerServer实例tcpPeerServer,它内部封装了ServerSocket,是DataXceiverServer功能实现的最主要依托;
2、从tcpPeerServer中获取Socket地址InetSocketAddress,赋值给DataNode成员变量streamingAddr
3、然后构造DataXceiverServer实例xserver,传入tcpPeerServer;
4、构造dataXceiverServer守护线程,并将xserver加入之前创建的线程组threadGroup;
5、将线程组里的所有线程设置为设置为守护线程,方便虚拟机退出时自动销毁。
下面,我们再看下DataXceiverServer的构造方法,代码如下:
- DataXceiverServer(PeerServer peerServer, Configuration conf,
- DataNode datanode) {
- // 根据传入的peerServer设置同名成员变量
- this.peerServer = peerServer;
- // 设置DataNode实例datanode
- this.datanode = datanode;
- // 设置DataNode中DataXceiver的最大数目maxXceiverCount
- // 取参数dfs.datanode.max.transfer.threads,参数未配置的话,默认值为4096
- this.maxXceiverCount =
- conf.getInt(DFSConfigKeys.DFS_DATANODE_MAX_RECEIVER_THREADS_KEY,
- DFSConfigKeys.DFS_DATANODE_MAX_RECEIVER_THREADS_DEFAULT);
- // 设置估计块大小estimateBlockSize
- // 取参数dfs.blocksize,参数未配置的话,默认值是128*1024*1024,即128M
- this.estimateBlockSize = conf.getLongBytes(DFSConfigKeys.DFS_BLOCK_SIZE_KEY,
- DFSConfigKeys.DFS_BLOCK_SIZE_DEFAULT);
- //set up parameter for cluster balancing
- // 设置集群平衡节流器
- // 带宽取参数dfs.datanode.balance.bandwidthPerSec,参数未配置默认为1024*1024
- // 最大线程数取参数dfs.datanode.balance.max.concurrent.moves,参数未配置默认为5
- this.balanceThrottler = new BlockBalanceThrottler(
- conf.getLong(DFSConfigKeys.DFS_DATANODE_BALANCE_BANDWIDTHPERSEC_KEY,
- DFSConfigKeys.DFS_DATANODE_BALANCE_BANDWIDTHPERSEC_DEFAULT),
- conf.getInt(DFSConfigKeys.DFS_DATANODE_BALANCE_MAX_NUM_CONCURRENT_MOVES_KEY,
- DFSConfigKeys.DFS_DATANODE_BALANCE_MAX_NUM_CONCURRENT_MOVES_DEFAULT));
- }
在构造DataXceiverServer时,会根据传入的peerServer设置同名成员变量、设置DataNode实例datanode等,并初始化两个重要的指标,第一个是设置DataNode中DataXceiver的最大数目maxXceiverCount,它取参数dfs.datanode.max.transfer.threads,参数未配置的话,默认值为4096;第二个便是设置估计块大小estimateBlockSize,它取参数取参数dfs.blocksize,参数未配置的话,默认值是128*1024*1024,即128M,最后,设置集群平衡节流器,带宽取参数dfs.datanode.balance.bandwidthPerSec,参数未配置默认为1024*1024,最大线程数取参数dfs.datanode.balance.max.concurrent.moves,参数未配置默认为5。
三、DataXceiverServer是如何工作的?
既然是一个线程,那么它的工作主要就体现在run()方法内,下面,我们来看下它的run()方法:
- @Override
- // 核心方法
- public void run() {
- Peer peer = null;
- while (datanode.shouldRun && !datanode.shutdownForUpgrade) {// 如果标志位shouldRun为true,且没有为升级而执行shutdown
- try {
- // 阻塞,直到接收到客户端或者其他DataNode的连接请求
- peer = peerServer.accept();
- // Make sure the xceiver count is not exceeded
- // 确保DataXceiver数目没有超过最大限制
- /**
- * DataNode的getXceiverCount方法计算得到,返回线程组的活跃线程数目
- * threadGroup == null ? 0 : threadGroup.activeCount();
- */
- int curXceiverCount = datanode.getXceiverCount();
- if (curXceiverCount > maxXceiverCount) {
- throw new IOException("Xceiver count " + curXceiverCount
- + " exceeds the limit of concurrent xcievers: "
- + maxXceiverCount);
- }
- // 创建一个后台线程,DataXceiver,并加入到线程组datanode.threadGroup
- new Daemon(datanode.threadGroup,
- DataXceiver.create(peer, datanode, this))
- .start();
- } catch (SocketTimeoutException ignored) {
- // wake up to see if should continue to run
- // 等待唤醒看看是否能够继续运行
- } catch (AsynchronousCloseException ace) {// 异步的关闭异常
- // another thread closed our listener socket - that's expected during shutdown,
- // but not in other circumstances
- // 正如我们所预料的,只有在关机的过程中,通过其他线程关闭我们的侦听套接字,其他情况下则不会发生
- if (datanode.shouldRun && !datanode.shutdownForUpgrade) {
- LOG.warn(datanode.getDisplayName() + ":DataXceiverServer: ", ace);
- }
- } catch (IOException ie) {
- IOUtils.cleanup(null, peer);
- LOG.warn(datanode.getDisplayName() + ":DataXceiverServer: ", ie);
- } catch (OutOfMemoryError ie) {
- IOUtils.cleanup(null, peer);
- // DataNode can run out of memory if there is too many transfers.
- // Log the event, Sleep for 30 seconds, other transfers may complete by
- // then.
- // 数据节点可能由于存在太多的数据传输导致内存溢出,记录该事件,并等待30秒,其他的数据传输可能到时就完成了
- LOG.warn("DataNode is out of memory. Will retry in 30 seconds.", ie);
- try {
- Thread.sleep(30 * 1000);
- } catch (InterruptedException e) {
- // ignore
- }
- } catch (Throwable te) {
- LOG.error(datanode.getDisplayName()
- + ":DataXceiverServer: Exiting due to: ", te);
- datanode.shouldRun = false;
- }
- }
- // Close the server to stop reception of more requests.
- // 关闭服务器停止接收更多请求
- try {
- peerServer.close();
- closed = true;
- } catch (IOException ie) {
- LOG.warn(datanode.getDisplayName()
- + " :DataXceiverServer: close exception", ie);
- }
- // if in restart prep stage, notify peers before closing them.
- // 如果在重新启动前准备阶段,在关闭前通知peers
- if (datanode.shutdownForUpgrade) {
- restartNotifyPeers();
- // Each thread needs some time to process it. If a thread needs
- // to send an OOB message to the client, but blocked on network for
- // long time, we need to force its termination.
- LOG.info("Shutting down DataXceiverServer before restart");
- // Allow roughly up to 2 seconds.
- for (int i = 0; getNumPeers() > 0 && i < 10; i++) {
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- // ignore
- }
- }
- }
- // Close all peers.
- // 关闭所有的peers
- closeAllPeers();
- }
通过run()方法我们得知,当datanode正常运转的时候,DataXceiverServer线程主要负责在一个while循环中利用TcpPeerServer(也就是ServerSocket)的accept()方法阻塞,直到接收到客户端或者其他DataNode的连接请求,然后:
1、获得peer,即Socket的封装;
2、判断当前DataNode上DataXceiver线程数量是否超过阈值,如果超过的话,直接抛出IOException,利用IOUtils的cleanup()方法关闭peer后继续循环,否则继续3;
3、创建一个后台线程DataXceiver,并将其加入到datanode的线程组threadGroup中,并启动该线程,响应数据读写请求;
上面主流程还是非常简单的。我们先看下accept()方法在TcpPeerServer中的实现:
- @Override
- public Peer accept() throws IOException, SocketTimeoutException {
- Peer peer = peerFromSocket(serverSocket.accept());
- return peer;
- }
它是通过调用peerFromSocket()方法来实现的,方法的入参是一个Socket,通过ServerSocket类型的serverSocket的accept()方法获得。peerFromSocket()方法代码如下:
- public static Peer peerFromSocket(Socket socket)
- throws IOException {
- Peer peer = null;
- boolean success = false;
- try {
- // TCP_NODELAY is crucial here because of bad interactions between
- // Nagle's Algorithm and Delayed ACKs. With connection keepalive
- // between the client and DN, the conversation looks like:
- // 1. Client -> DN: Read block X
- // 2. DN -> Client: data for block X
- // 3. Client -> DN: Status OK (successful read)
- // 4. Client -> DN: Read block Y
- // The fact that step #3 and #4 are both in the client->DN direction
- // triggers Nagling. If the DN is using delayed ACKs, this results
- // in a delay of 40ms or more.
- //
- // TCP_NODELAY disables nagling and thus avoids this performance
- // disaster.
- // 设置TCP无延迟
- socket.setTcpNoDelay(true);
- // 获得SocketChannel
- SocketChannel channel = socket.getChannel();
- // 利用socket创建peer,如果通道channel为null,则创建基本的BasicInetPeer,否则创建NioInetPeer
- if (channel == null) {
- peer = new BasicInetPeer(socket);
- } else {
- peer = new NioInetPeer(socket);
- }
- // 标志位success设置为true
- success = true;
- return peer;
- } finally {
- if (!success) {// 如果创建不成功,peer不为空的话,关闭之
- if (peer != null) peer.close();
- // 关闭socket
- socket.close();
- }
- }
- }
其中,Peer是对Socket、输入输出流等的封装,有BasicInetPeer和NioInetPeer两种。BasicInetPeer代表了基本的Peer,封装了Socket、OutputStream、InputStream三者;而NioInetPeer代表了一种我们可通过使用非阻塞IO进行Socket通讯的一种Peer,封装了Socket、SocketInputStream、SocketOutputStream三者,具体代码不再列举,读者可自行查阅。
紧接着,我们说下对于异常的处理。整个过程中一共出现了5种异常,包括对它们的处理如下所示:
1、SocketTimeoutException:Socket连接超时异常,忽略直接进入下一个循环即可,继续阻塞侦听请求;
2、AsynchronousCloseException:异步关闭异常,这里我们需要通过判断DataNode的状态来决定是否继续进行循环,继续阻塞侦听请求;
3、IOException:此时,对应于上述主流程的第2步,我们需要关闭Socket后进入下一个循环,继续阻塞侦听请求;
4、OutOfMemoryError:内存溢出错误,这种情况下数据节点可能由于存在太多的数据传输导致内存溢出,记录该事件,并等待30秒,其他的数据传输可能到时就完成了。我们需要做的就是,首先需要利用IOUtils的cleanup()方法关闭peer,记录警告日志信息,然后线程休眠30秒,等待DataNode其他数据读写服务完成后,进入下一个循环,继续阻塞侦听请求;
5、Throwable:无话可说,DataNode就是为提高数据存储、读写服务而生的,既然出现了Throwable,那么DataNode也应该停止了,记录error日志信息,设置datanode的shouldRun为false,退出循环。
从上面的异常可以看出,DataNode中DataXceiverServer的设计是很严谨与合理的,DataNode能够提供大吞吐量的数据读写服务与此不无关系。
当Throwable发生,退出循环后,我们就需要做一些列的关闭操作,关闭peerServer,设置DataXceiverServer标志位closed为true,关闭所有的peers等。其中关闭所有的peers的closeAllPeers()方法如下:
- // Close all peers and clear the map.
- synchronized void closeAllPeers() {
- // 记录info日志信息
- LOG.info("Closing all peers.");
- // 循环关闭所有的peer
- for (Peer p : peers.keySet()) {
- IOUtils.cleanup(LOG, p);
- }
- // 清空peer数据集合
- peers.clear();
- peersXceiver.clear();
- }
代码非常简单,不再赘述。
并且,如果在重新启动前准备阶段,在关闭peers之前,需要先通知它们,通知的方式就是通过调用restartNotifyPeers()方法,获取peers的每个peer所在线程,通过interrupt()方法打断它们,代码如下:
- // Notify all peers of the shutdown and restart.
- // datanode.shouldRun should still be true and datanode.restarting should
- // be set true before calling this method.
- synchronized void restartNotifyPeers() {
- assert (datanode.shouldRun == true && datanode.shutdownForUpgrade);
- for (Peer p : peers.keySet()) {
- // interrupt each and every DataXceiver thread.
- // 中断每个DataXceiver线程
- peers.get(p).interrupt();
- }
- }
并且,每个线程需要一些时间去完成自己。如果一个线程需要发送OOB至客户端,但是在网络上被阻塞的了一段时间,我们需要强迫使其停止。此时,我们需要大约2秒的时间等待它们的完成。
另外,这里我们需要说下集群数据块平衡节流器balanceThrottler的实现,其成员变量、构造方法代码如下:
- /** A manager to make sure that cluster balancing does not
- * take too much resources.
- *
- * It limits the number of block moves for balancing and
- * the total amount of bandwidth they can use.
- *
- * 确保集群平衡不占用太多资源的一种手段或管理者。
- * 它限制了为集群平衡所做的块移动的数量及它们所占用的总宽带,是一种节流器的概念。
- */
- static class BlockBalanceThrottler extends DataTransferThrottler {
- private int numThreads;// 表示当前移动数据块的线程数numThreads
- private int maxThreads;// 表示移动数据块的最大线程数maxThreads
- /**Constructor
- * 构造方法
- * @param bandwidth Total amount of bandwidth can be used for balancing
- */
- private BlockBalanceThrottler(long bandwidth, int maxThreads) {
- super(bandwidth);
- // 设置移动数据块的最大线程数maxThreads
- this.maxThreads = maxThreads;
- LOG.info("Balancing bandwith is "+ bandwidth + " bytes/s");
- LOG.info("Number threads for balancing is "+ maxThreads);
- }
- }
可以看到,它内部有三个非常重要的指标,表示当前移动数据块的线程数的numThreads, 和表示移动数据块的最大线程数maxThreads,还有数据传输的带宽bandwidth。在其构造方法内,会调用父类的构造方法设置带宽bandwidth,并在子类中设置移动数据块的最大线程数maxThreads。那么它是如何实现集群内数据库移动节流控制的呢?答案就在方法acquire()中,代码如下:
- /** Check if the block move can start.
- * 检测block移动是否可以开始
- *
- * Return true if the thread quota is not exceeded and
- * the counter is incremented; False otherwise.
- */
- synchronized boolean acquire() {
- / 当前线程数numThreads大于等于最大线程数maxThreads时,返回false,block不可以移动
- if (numThreads >= maxThreads) {
- return false;
- }
- // 否则,当前线程数numThreads累加,并返回true,block可以移动
- numThreads++;
- return true;
- }
在移动数据块block之前,会调用acquire()方法,确认一个数据块是否可以移动。实际上是当前线程数numThreads大于等于最大线程数maxThreads时,返回false,block不可以移动;否则,当前线程数numThreads累加,并返回true,block可以移动。就是这么简单,而当数据块移动完毕后,则调用release()方法,标志移动已完成,线程计数器减一,代码如下:
- /** Mark that the move is completed. The thread counter is decremented. */
- // 标志移动已完成,线程计数器减一
- synchronized void release() {
- / 当前线程数numThreads减1
- numThreads--;
- }
另外,DataXceiverServer中还提供了一些功能性方法,比如:
1、实现了杀死DataXceiverServer线程的kill()方法,代码如下:
- void kill() {
- // DataXceiverServer线程被kill时,需要确定datanode的标志位shouldRun为false,或者标志位shutdownForUpgradetrue
- // 也就意味着,当datanode不应该继续运行,或者为了升级而关闭时,DataXceiverServer线程才可以被kill
- assert (datanode.shouldRun == false || datanode.shutdownForUpgrade) :
- "shoudRun should be set to false or restarting should be true"
- + " before killing";
- try {
- // 关闭peerServer,即关闭ServerSocket
- this.peerServer.close();
- // 设置标志位closed为true
- this.closed = true;
- } catch (IOException ie) {
- LOG.warn(datanode.getDisplayName() + ":DataXceiverServer.kill(): ", ie);
- }
- }
2、添加一个Peer的addPeer()方法,代码如下:
- synchronized void addPeer(Peer peer, Thread t, DataXceiver xceiver)
- throws IOException {
- // 首先判断DataXceiverServer线程的标志位closed,为true时,说明服务线程已被关闭,不能再提供Socket通讯服务
- if (closed) {
- throw new IOException("Server closed.");
- }
- // 将peer与其所在线程t的映射关系加入到peers中
- peers.put(peer, t);
- // 将peer与其所属DataXceiver xceiver映射关系加入到peersXceiver中
- peersXceiver.put(peer, xceiver);
- }
3、关闭一个Peer的closePeer()方法,代码如下:
- // 关闭Peer
- synchronized void closePeer(Peer peer) {
- // 从数据结构peers、peersXceiver移除peer对应记录
- peers.remove(peer);
- peersXceiver.remove(peer);
- // 利用IOUtils的cleanup关闭peer,即关闭socket
- IOUtils.cleanup(null, peer);
- }
4、关闭所有Peer的closeAllPeers()方法,代码如下:
- // Close all peers and clear the map.
- synchronized void closeAllPeers() {
- // 记录info日志信息
- LOG.info("Closing all peers.");
- // 利用IOUtils的cleanup()方法循环关闭所有的peer,即关闭socket
- for (Peer p : peers.keySet()) {
- IOUtils.cleanup(LOG, p);
- }
- // 清空peer数据集合peers、peersXceiver
- peers.clear();
- peersXceiver.clear();
- }
上述代码非常简单,并且注释详细,读者可自行阅读与分析,这里不再做详细介绍。
部分未叙述细节放至DataXceiver的分析文章中去讲,敬请留意!
总结
DataXceiverServer是数据节点DataNode上一个用于接收数据读写请求的后台工作线程,为每个数据读写请求创建一个单独的线程去处理。它提供了一请求一线程的模式,并对线程数目做了控制,对接收数据读写请求时发生的各种异常做了很好的容错处理,特别是针对内存溢出异常,允许等待短暂时间再继续提供服务,避免内存使用高峰期等等。而且,线程组与后台线程的应用,也大大简化可这些线程的管理工作;对数据读写请求处理线程的数目,集群内数据块移动线程数目都做了严格控制,避免资源无节制耗费等。这些设计很好的支撑了HDFS大吞吐量数据的性能要求,可以说,是一个很好的设计方案,值得我们在其他类似需求的系统中借鉴。
HDFS源码分析之DataXceiverServer的更多相关文章
- HDFS源码分析DataXceiver之整体流程
在<HDFS源码分析之DataXceiverServer>一文中,我们了解到在DataNode中,有一个后台工作的线程DataXceiverServer.它被用于接收来自客户端或其他数据节 ...
- HDFS源码分析之UnderReplicatedBlocks(一)
http://blog.csdn.net/lipeng_bigdata/article/details/51160359 UnderReplicatedBlocks是HDFS中关于块复制的一个重要数据 ...
- HDFS源码分析数据块校验之DataBlockScanner
DataBlockScanner是运行在数据节点DataNode上的一个后台线程.它为所有的块池管理块扫描.针对每个块池,一个BlockPoolSliceScanner对象将会被创建,其运行在一个单独 ...
- HDFS源码分析数据块复制监控线程ReplicationMonitor(二)
HDFS源码分析数据块复制监控线程ReplicationMonitor(二)
- HDFS源码分析数据块复制监控线程ReplicationMonitor(一)
ReplicationMonitor是HDFS中关于数据块复制的监控线程,它的主要作用就是计算DataNode工作,并将复制请求超时的块重新加入到待调度队列.其定义及作为线程核心的run()方法如下: ...
- HDFS源码分析之UnderReplicatedBlocks(二)
UnderReplicatedBlocks还提供了一个数据块迭代器BlockIterator,用于遍历其中的数据块.它是UnderReplicatedBlocks的内部类,有三个成员变量,如下: // ...
- HDFS源码分析之LightWeightGSet
LightWeightGSet是名字节点NameNode在内存中存储全部数据块信息的类BlocksMap需要的一个重要数据结构,它是一个占用较低内存的集合的实现,它使用一个数组array存储元素,使用 ...
- HDFS源码分析数据块汇报之损坏数据块检测checkReplicaCorrupt()
无论是第一次,还是之后的每次数据块汇报,名字名字节点都会对汇报上来的数据块进行检测,看看其是否为损坏的数据块.那么,损坏数据块是如何被检测的呢?本文,我们将研究下损坏数据块检测的checkReplic ...
- HDFS源码分析之数据块及副本状态BlockUCState、ReplicaState
关于数据块.副本的介绍,请参考文章<HDFS源码分析之数据块Block.副本Replica>. 一.数据块状态BlockUCState 数据块状态用枚举类BlockUCState来表示,代 ...
随机推荐
- 【Chrome】Octotree Chrome插件离线安装
插件下载地址:http://www.cnplugins.com/devtool/octotree/download.html Octotree 是国外程序员Buu Nguyen 做的一个 Chrome ...
- Python Challenge 第十四关
14关页面上是两张图,一张是一个卷面包,一张类似条形码的东西.没任何提示,就看源代码,果然,有一行注释: <!-- remember: 100*100 = (100+99+99+98) + (. ...
- html5---音频视频基础一
//html5 音频和视频 :标签 a: audio,video b: source :视频容器 a:容器文件,类似于压缩了一组文件 -音频轨道 -视频轨道 -元数据:封面,标题,字幕等 -格式:.a ...
- Codeforces 632F Magic Matrix(bitset)
题目链接 Magic Matrix 考虑第三个条件,如果不符合的话说明$a[i][k] < a[i][j]$ 或 $a[j][k] < a[i][j]$ 于是我们把所有的$(a[i][j ...
- 详解Tomcat系列(一)-从源码分析Tomcat的启动
在整个Tomcat系列文章讲解之前, 我想说的是虽然整个Tomcat体系比较复杂, 但是Tomcat中的代码并不难读, 只要认真花点功夫, 一定能啃下来. 由于篇幅的原因, 很难把Tomcat所有的知 ...
- selenium遇到不可编辑input和隐藏input如何赋值
js = 'document.getElementsByName("m:csr_mt_gnccspjclfbxd:bmshldID")[0].type="text&quo ...
- SVN源码服务器搭建-详细教程
一.引言 笔者曾经试图在网上搜索一篇关于SVN源代码服务器搭建方面的中文技术文章,可惜,所找到的,要么是不完整,要么就是对笔者没什么帮助的文章,TortoiseSvn的帮助文档固然强大,但因为是英文, ...
- (转)python装饰器二
Python装饰器进阶之二 保存被装饰方法的元数据 什么是方法的元数据 举个栗子 def hello(): print('Hello, World.') print(dir(hello)) 结果如下: ...
- 【音乐App】—— Vue-music 项目学习笔记:歌单及排行榜开发
前言:以下内容均为学习慕课网高级实战课程的实践爬坑笔记. 项目github地址:https://github.com/66Web/ljq_vue_music,欢迎Star. 歌单及详情页 排行榜及详情 ...
- 【温故知新】——BABYLON.js学习之路·前辈经验(一)
前言:公司用BABYLON作为主要的前端引擎,同事们在长时间的项目实践中摸索到有关BABYLON的学习路径和问题解决方法,这里只作为温故知新. 一.快速学习BABYLON 1. 阅读Babylon[基 ...