|
@@ -57,12 +57,16 @@ import java.io.OutputStream;
|
|
|
import java.net.InetAddress;
|
|
|
import java.net.InetSocketAddress;
|
|
|
import java.net.Socket;
|
|
|
+import java.net.SocketAddress;
|
|
|
import java.net.URI;
|
|
|
+import java.net.UnknownHostException;
|
|
|
+import java.util.ArrayList;
|
|
|
import java.util.Collections;
|
|
|
import java.util.EnumSet;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
+import java.util.Random;
|
|
|
|
|
|
import javax.net.SocketFactory;
|
|
|
|
|
@@ -123,6 +127,7 @@ import org.apache.hadoop.io.Text;
|
|
|
import org.apache.hadoop.ipc.Client;
|
|
|
import org.apache.hadoop.ipc.RPC;
|
|
|
import org.apache.hadoop.ipc.RemoteException;
|
|
|
+import org.apache.hadoop.net.DNS;
|
|
|
import org.apache.hadoop.net.NetUtils;
|
|
|
import org.apache.hadoop.security.AccessControlException;
|
|
|
import org.apache.hadoop.security.UserGroupInformation;
|
|
@@ -132,7 +137,9 @@ import org.apache.hadoop.security.token.TokenRenewer;
|
|
|
import org.apache.hadoop.util.DataChecksum;
|
|
|
import org.apache.hadoop.util.Progressable;
|
|
|
|
|
|
+import com.google.common.base.Joiner;
|
|
|
import com.google.common.base.Preconditions;
|
|
|
+import com.google.common.net.InetAddresses;
|
|
|
|
|
|
/********************************************************
|
|
|
* DFSClient can connect to a Hadoop Filesystem and
|
|
@@ -168,6 +175,8 @@ public class DFSClient implements java.io.Closeable {
|
|
|
final LeaseRenewer leaserenewer;
|
|
|
final SocketCache socketCache;
|
|
|
final Conf dfsClientConf;
|
|
|
+ private Random r = new Random();
|
|
|
+ private SocketAddress[] localInterfaceAddrs;
|
|
|
|
|
|
/**
|
|
|
* DFSClient configuration
|
|
@@ -361,6 +370,68 @@ public class DFSClient implements java.io.Closeable {
|
|
|
if (LOG.isDebugEnabled()) {
|
|
|
LOG.debug("Short circuit read is " + shortCircuitLocalReads);
|
|
|
}
|
|
|
+ String localInterfaces[] =
|
|
|
+ conf.getTrimmedStrings(DFSConfigKeys.DFS_CLIENT_LOCAL_INTERFACES);
|
|
|
+ localInterfaceAddrs = getLocalInterfaceAddrs(localInterfaces);
|
|
|
+ if (LOG.isDebugEnabled() && 0 != localInterfaces.length) {
|
|
|
+ LOG.debug("Using local interfaces [" +
|
|
|
+ Joiner.on(',').join(localInterfaces)+ "] with addresses [" +
|
|
|
+ Joiner.on(',').join(localInterfaceAddrs) + "]");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Return the socket addresses to use with each configured
|
|
|
+ * local interface. Local interfaces may be specified by IP
|
|
|
+ * address, IP address range using CIDR notation, interface
|
|
|
+ * name (e.g. eth0) or sub-interface name (e.g. eth0:0).
|
|
|
+ * The socket addresses consist of the IPs for the interfaces
|
|
|
+ * and the ephemeral port (port 0). If an IP, IP range, or
|
|
|
+ * interface name matches an interface with sub-interfaces
|
|
|
+ * only the IP of the interface is used. Sub-interfaces can
|
|
|
+ * be used by specifying them explicitly (by IP or name).
|
|
|
+ *
|
|
|
+ * @return SocketAddresses for the configured local interfaces,
|
|
|
+ * or an empty array if none are configured
|
|
|
+ * @throws UnknownHostException if a given interface name is invalid
|
|
|
+ */
|
|
|
+ private static SocketAddress[] getLocalInterfaceAddrs(
|
|
|
+ String interfaceNames[]) throws UnknownHostException {
|
|
|
+ List<SocketAddress> localAddrs = new ArrayList<SocketAddress>();
|
|
|
+ for (String interfaceName : interfaceNames) {
|
|
|
+ if (InetAddresses.isInetAddress(interfaceName)) {
|
|
|
+ localAddrs.add(new InetSocketAddress(interfaceName, 0));
|
|
|
+ } else if (NetUtils.isValidSubnet(interfaceName)) {
|
|
|
+ for (InetAddress addr : NetUtils.getIPs(interfaceName, false)) {
|
|
|
+ localAddrs.add(new InetSocketAddress(addr, 0));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ for (String ip : DNS.getIPs(interfaceName, false)) {
|
|
|
+ localAddrs.add(new InetSocketAddress(ip, 0));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return localAddrs.toArray(new SocketAddress[localAddrs.size()]);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Select one of the configured local interfaces at random. We use a random
|
|
|
+ * interface because other policies like round-robin are less effective
|
|
|
+ * given that we cache connections to datanodes.
|
|
|
+ *
|
|
|
+ * @return one of the local interface addresses at random, or null if no
|
|
|
+ * local interfaces are configured
|
|
|
+ */
|
|
|
+ SocketAddress getRandomLocalInterfaceAddr() {
|
|
|
+ if (localInterfaceAddrs.length == 0) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ final int idx = r.nextInt(localInterfaceAddrs.length);
|
|
|
+ final SocketAddress addr = localInterfaceAddrs[idx];
|
|
|
+ if (LOG.isDebugEnabled()) {
|
|
|
+ LOG.debug("Using local interface " + addr);
|
|
|
+ }
|
|
|
+ return addr;
|
|
|
}
|
|
|
|
|
|
/**
|