HADOOP-9150. Avoid unnecessary DNS resolution attempts for logical URIs. Contributed by Todd Lipcon.
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2@1462301 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
ad1695cb1e
commit
3e696b4273
|
@ -35,6 +35,9 @@ Release 2.0.5-beta - UNRELEASED
|
|||
|
||||
OPTIMIZATIONS
|
||||
|
||||
HADOOP-9150. Avoid unnecessary DNS resolution attempts for logical URIs
|
||||
(todd)
|
||||
|
||||
BUG FIXES
|
||||
|
||||
HADOOP-9294. GetGroupsTestBase fails on Windows. (Chris Nauroth via suresh)
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.io.Closeable;
|
|||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -211,12 +212,46 @@ public abstract class FileSystem extends Configured implements Closeable {
|
|||
public abstract URI getUri();
|
||||
|
||||
/**
|
||||
* Resolve the uri's hostname and add the default port if not in the uri
|
||||
* Return a canonicalized form of this FileSystem's URI.
|
||||
*
|
||||
* The default implementation simply calls {@link #canonicalizeUri(URI)}
|
||||
* on the filesystem's own URI, so subclasses typically only need to
|
||||
* implement that method.
|
||||
*
|
||||
* @see #canonicalizeUri(URI)
|
||||
*/
|
||||
protected URI getCanonicalUri() {
|
||||
return canonicalizeUri(getUri());
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonicalize the given URI.
|
||||
*
|
||||
* This is filesystem-dependent, but may for example consist of
|
||||
* canonicalizing the hostname using DNS and adding the default
|
||||
* port if not specified.
|
||||
*
|
||||
* The default implementation simply fills in the default port if
|
||||
* not specified and if the filesystem has a default port.
|
||||
*
|
||||
* @return URI
|
||||
* @see NetUtils#getCanonicalUri(URI, int)
|
||||
*/
|
||||
protected URI getCanonicalUri() {
|
||||
return NetUtils.getCanonicalUri(getUri(), getDefaultPort());
|
||||
protected URI canonicalizeUri(URI uri) {
|
||||
if (uri.getPort() == -1 && getDefaultPort() > 0) {
|
||||
// reconstruct the uri with the default port set
|
||||
try {
|
||||
uri = new URI(uri.getScheme(), uri.getUserInfo(),
|
||||
uri.getHost(), getDefaultPort(),
|
||||
uri.getPath(), uri.getQuery(), uri.getFragment());
|
||||
} catch (URISyntaxException e) {
|
||||
// Should never happen!
|
||||
throw new AssertionError("Valid URI became unparseable: " +
|
||||
uri);
|
||||
}
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -581,7 +616,7 @@ public abstract class FileSystem extends Configured implements Closeable {
|
|||
}
|
||||
if (uri != null) {
|
||||
// canonicalize uri before comparing with this fs
|
||||
uri = NetUtils.getCanonicalUri(uri, getDefaultPort());
|
||||
uri = canonicalizeUri(uri);
|
||||
thatAuthority = uri.getAuthority();
|
||||
if (thisAuthority == thatAuthority || // authorities match
|
||||
(thisAuthority != null &&
|
||||
|
|
|
@ -96,15 +96,17 @@ public class FilterFileSystem extends FileSystem {
|
|||
return fs.getUri();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a qualified URI whose scheme and authority identify this
|
||||
* FileSystem.
|
||||
*/
|
||||
|
||||
@Override
|
||||
protected URI getCanonicalUri() {
|
||||
return fs.getCanonicalUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URI canonicalizeUri(URI uri) {
|
||||
return fs.canonicalizeUri(uri);
|
||||
}
|
||||
|
||||
/** Make sure that a path specifies a FileSystem. */
|
||||
@Override
|
||||
public Path makeQualified(Path path) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.net.URI;
|
|||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.permission.FsPermission;
|
||||
import org.apache.hadoop.net.NetUtils;
|
||||
import org.apache.hadoop.security.NetUtilsTestResolver;
|
||||
import org.apache.hadoop.util.Progressable;
|
||||
import org.junit.BeforeClass;
|
||||
|
@ -312,6 +313,11 @@ public class TestFileSystemCanonicalization {
|
|||
return defaultPort;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URI canonicalizeUri(URI uri) {
|
||||
return NetUtils.getCanonicalUri(uri, getDefaultPort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSDataInputStream open(Path f, int bufferSize) throws IOException {
|
||||
throw new IOException("not supposed to be here");
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.apache.hadoop.test;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
@ -266,15 +267,29 @@ public abstract class GenericTestUtils {
|
|||
*/
|
||||
public static class DelegateAnswer implements Answer<Object> {
|
||||
private final Object delegate;
|
||||
private final Log log;
|
||||
|
||||
public DelegateAnswer(Object delegate) {
|
||||
this(null, delegate);
|
||||
}
|
||||
|
||||
public DelegateAnswer(Log log, Object delegate) {
|
||||
this.log = log;
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
return invocation.getMethod().invoke(
|
||||
delegate, invocation.getArguments());
|
||||
try {
|
||||
if (log != null) {
|
||||
log.info("Call to " + invocation + " on " + delegate,
|
||||
new Exception("TRACE"));
|
||||
}
|
||||
return invocation.getMethod().invoke(
|
||||
delegate, invocation.getArguments());
|
||||
} catch (InvocationTargetException ite) {
|
||||
throw ite.getCause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ import org.apache.hadoop.hdfs.security.token.block.InvalidBlockTokenException;
|
|||
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
|
||||
import org.apache.hadoop.hdfs.server.namenode.NameNode;
|
||||
import org.apache.hadoop.io.Text;
|
||||
import org.apache.hadoop.net.NetUtils;
|
||||
import org.apache.hadoop.security.AccessControlException;
|
||||
import org.apache.hadoop.security.token.SecretManager.InvalidToken;
|
||||
import org.apache.hadoop.security.token.Token;
|
||||
|
@ -938,6 +939,17 @@ public class DistributedFileSystem extends FileSystem {
|
|||
return dfs.getCanonicalServiceName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URI canonicalizeUri(URI uri) {
|
||||
if (HAUtil.isLogicalUri(getConf(), uri)) {
|
||||
// Don't try to DNS-resolve logical URIs, since the 'authority'
|
||||
// portion isn't a proper hostname
|
||||
return uri;
|
||||
} else {
|
||||
return NetUtils.getCanonicalUri(uri, getDefaultPort());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function that returns if the NameNode is in safemode or not. In HA
|
||||
* mode, this API will return only ActiveNN's safemode status.
|
||||
|
|
|
@ -162,6 +162,11 @@ public class HftpFileSystem extends FileSystem
|
|||
return SecurityUtil.buildTokenService(nnSecureUri).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URI canonicalizeUri(URI uri) {
|
||||
return NetUtils.getCanonicalUri(uri, getDefaultPort());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the protocol scheme for the FileSystem.
|
||||
* <p/>
|
||||
|
|
|
@ -239,6 +239,11 @@ public class WebHdfsFileSystem extends FileSystem
|
|||
return this.uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URI canonicalizeUri(URI uri) {
|
||||
return NetUtils.getCanonicalUri(uri, getDefaultPort());
|
||||
}
|
||||
|
||||
/** @return the home directory. */
|
||||
public static String getHomeDirectoryString(final UserGroupInformation ugi) {
|
||||
return "/user/" + ugi.getShortUserName();
|
||||
|
|
|
@ -26,8 +26,11 @@ import java.io.IOException;
|
|||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
|
@ -35,6 +38,7 @@ import org.apache.commons.logging.Log;
|
|||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
|
||||
import org.apache.hadoop.fs.FileContext;
|
||||
import org.apache.hadoop.fs.FileSystem;
|
||||
import org.apache.hadoop.fs.Path;
|
||||
import org.apache.hadoop.hdfs.server.namenode.NameNode;
|
||||
|
@ -48,10 +52,13 @@ import org.apache.hadoop.util.StringUtils;
|
|||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
import org.junit.After;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import sun.net.spi.nameservice.NameService;
|
||||
|
||||
public class TestDFSClientFailover {
|
||||
|
||||
private static final Log LOG = LogFactory.getLog(TestDFSClientFailover.class);
|
||||
|
@ -201,4 +208,74 @@ public class TestDFSClientFailover {
|
|||
"Could not find any configured addresses for URI " + uri));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spy on the Java DNS infrastructure.
|
||||
* This likely only works on Sun-derived JDKs, but uses JUnit's
|
||||
* Assume functionality so that any tests using it are skipped on
|
||||
* incompatible JDKs.
|
||||
*/
|
||||
private NameService spyOnNameService() {
|
||||
try {
|
||||
Field f = InetAddress.class.getDeclaredField("nameServices");
|
||||
f.setAccessible(true);
|
||||
Assume.assumeNotNull(f);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<NameService> nsList = (List<NameService>) f.get(null);
|
||||
|
||||
NameService ns = nsList.get(0);
|
||||
Log log = LogFactory.getLog("NameServiceSpy");
|
||||
|
||||
ns = Mockito.mock(NameService.class,
|
||||
new GenericTestUtils.DelegateAnswer(log, ns));
|
||||
nsList.set(0, ns);
|
||||
return ns;
|
||||
} catch (Throwable t) {
|
||||
LOG.info("Unable to spy on DNS. Skipping test.", t);
|
||||
// In case the JDK we're testing on doesn't work like Sun's, just
|
||||
// skip the test.
|
||||
Assume.assumeNoException(t);
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the client doesn't ever try to DNS-resolve the logical URI.
|
||||
* Regression test for HADOOP-9150.
|
||||
*/
|
||||
@Test
|
||||
public void testDoesntDnsResolveLogicalURI() throws Exception {
|
||||
NameService spyNS = spyOnNameService();
|
||||
|
||||
FileSystem fs = HATestUtil.configureFailoverFs(cluster, conf);
|
||||
String logicalHost = fs.getUri().getHost();
|
||||
Path qualifiedRoot = fs.makeQualified(new Path("/"));
|
||||
|
||||
// Make a few calls against the filesystem.
|
||||
fs.getCanonicalServiceName();
|
||||
fs.listStatus(qualifiedRoot);
|
||||
|
||||
// Ensure that the logical hostname was never resolved.
|
||||
Mockito.verify(spyNS, Mockito.never()).lookupAllHostAddr(Mockito.eq(logicalHost));
|
||||
}
|
||||
|
||||
/**
|
||||
* Same test as above, but for FileContext.
|
||||
*/
|
||||
@Test
|
||||
public void testFileContextDoesntDnsResolveLogicalURI() throws Exception {
|
||||
NameService spyNS = spyOnNameService();
|
||||
FileSystem fs = HATestUtil.configureFailoverFs(cluster, conf);
|
||||
String logicalHost = fs.getUri().getHost();
|
||||
Configuration haClientConf = fs.getConf();
|
||||
|
||||
FileContext fc = FileContext.getFileContext(haClientConf);
|
||||
Path root = new Path("/");
|
||||
fc.listStatus(root);
|
||||
fc.listStatus(fc.makeQualified(root));
|
||||
fc.getDefaultFileSystem().getCanonicalServiceName();
|
||||
|
||||
// Ensure that the logical hostname was never resolved.
|
||||
Mockito.verify(spyNS, Mockito.never()).lookupAllHostAddr(Mockito.eq(logicalHost));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue