/** * 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.hadoop.hdfs.server.datanode; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hdfs.protocol.FSConstants; import org.apache.hadoop.hdfs.server.balancer.Balancer; import org.apache.hadoop.hdfs.server.datanode.metrics.DatanodeThreadLivenessReporter; import org.apache.hadoop.hdfs.server.datanode.metrics.DatanodeThreadLivenessReporter.BackgroundThread; import org.apache.hadoop.hdfs.util.InjectionEvent; import org.apache.hadoop.util.Daemon; import org.apache.hadoop.util.DataTransferThrottler; import org.apache.hadoop.util.InjectionHandler; import org.apache.hadoop.util.StringUtils; /** * Server used for receiving/sending a block of data. * This is created to listen for requests from clients or * other DataNodes. This small server does not use the * Hadoop IPC mechanism. */ class DataXceiverServer implements Runnable, FSConstants { public static final Log LOG = DataNode.LOG; static final Log ClientTraceLog = DataNode.ClientTraceLog; ServerSocket ss; DataNode datanode; // Record all sockets opend for data transfer Map<Socket, Socket> childSockets = Collections.synchronizedMap( new HashMap<Socket, Socket>()); /** * Maximal number of concurrent xceivers per node. * Enforcing the limit is required in order to avoid data-node * running out of memory. */ static final int MAX_XCEIVER_COUNT = 256; int maxXceiverCount = MAX_XCEIVER_COUNT; /** 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; private int maxNumThreads; /**Constructor * * @param bandwidth Total amount of bandwidth can be used for balancing */ private BlockBalanceThrottler(long bandwidth, int maxNumThreads) { super(bandwidth); this.maxNumThreads = maxNumThreads; LOG.info("Balancing bandwith is "+ bandwidth + " bytes/s and max number of threads is " + maxNumThreads); } /** Check if the block move can start. * * Return true if the thread quota is not exceeded and * the counter is incremented; False otherwise. */ synchronized boolean acquire() { if (numThreads >= maxNumThreads) { return false; } numThreads++; return true; } /** Mark that the move is completed. The thread counter is decremented. */ synchronized void release() { numThreads--; } } BlockBalanceThrottler balanceThrottler; /** * We need an estimate for block size to check if the disk partition has * enough space. For now we set it to be the default block size set * in the server side configuration, which is not ideal because the * default block size should be a client-size configuration. * A better solution is to include in the header the estimated block size, * i.e. either the actual block size or the default block size. */ long estimateBlockSize; DataXceiverServer(ServerSocket ss, Configuration conf, DataNode datanode) { this.ss = ss; this.datanode = datanode; this.maxXceiverCount = conf.getInt("dfs.datanode.max.xcievers", MAX_XCEIVER_COUNT); this.estimateBlockSize = conf.getLong("dfs.block.size", DEFAULT_BLOCK_SIZE); //set up parameter for cluster balancing this.balanceThrottler = new BlockBalanceThrottler( conf.getLong("dfs.balance.bandwidthPerSec", 1024L*1024), Math.max(Balancer.MAX_NUM_CONCURRENT_MOVES, conf.getInt("dfs.balance.maxNumThreads", Balancer.MAX_NUM_CONCURRENT_MOVES))); } /** */ public void run() { while (datanode.shouldRun) { try { datanode .updateAndReportThreadLiveness(BackgroundThread.DATA_XCEIVER_SERVER); ss.setSoTimeout(1000); InjectionHandler.processEvent( InjectionEvent.DATAXEIVER_SERVER_PRE_ACCEPT); Socket s = ss.accept(); new Daemon(datanode.threadGroup, new DataXceiver(s, datanode, this)).start(); InjectionHandler.processEvent( InjectionEvent.AVATARXEIVER_RUNTIME_FAILURE); } catch (SocketTimeoutException ignored) { // wake up to see if should continue to run } catch (IOException ie) { datanode.myMetrics.dataXceiverConnFailures.inc(); LOG.warn(datanode.getDatanodeInfo() + ":DataXceiveServer IO error", ie); } catch (Throwable te) { datanode.shouldRun = false; datanode.myMetrics.dataXceiverConnFailures.inc(); LOG.error(datanode.getDatanodeInfo() + ":DataXceiveServer exiting", te); } } try { ss.close(); } catch (IOException ie) { LOG.warn(datanode.getDatanodeInfo() + ":DataXceiveServer: " + StringUtils.stringifyException(ie)); } } void kill() { assert datanode.shouldRun == false : "shoudRun should be set to false before killing"; try { this.ss.close(); } catch (IOException ie) { LOG.warn(datanode.getDatanodeInfo() + ":DataXceiveServer.kill(): " + StringUtils.stringifyException(ie)); } // close all the sockets that were accepted earlier synchronized (childSockets) { for (Iterator<Socket> it = childSockets.values().iterator(); it.hasNext();) { Socket thissock = it.next(); try { thissock.close(); } catch (IOException e) { } } } } }