forked from apache/hadoop
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HDFS-9039. Separate client and server side methods of o.a.h.hdfs.Name…
…NodeProxies. Contributed by Mingliang Liu.
- Loading branch information
Haohui Mai
committed
Sep 23, 2015
1 parent
cc2b473
commit 63d9f15
Showing
13 changed files
with
398 additions
and
293 deletions.
There are no files selected for viewing
366 changes: 366 additions & 0 deletions
366
...roject/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/NameNodeProxiesClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,366 @@ | ||
/** | ||
* 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; | ||
|
||
import java.io.IOException; | ||
import java.lang.reflect.Constructor; | ||
import java.lang.reflect.InvocationHandler; | ||
import java.lang.reflect.Proxy; | ||
import java.net.InetSocketAddress; | ||
import java.net.URI; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
|
||
import org.apache.hadoop.classification.InterfaceAudience; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import com.google.common.annotations.VisibleForTesting; | ||
import com.google.common.base.Preconditions; | ||
|
||
import org.apache.hadoop.conf.Configuration; | ||
import org.apache.hadoop.hdfs.client.HdfsClientConfigKeys; | ||
import org.apache.hadoop.hdfs.client.impl.DfsClientConf; | ||
import org.apache.hadoop.hdfs.protocol.ClientProtocol; | ||
import org.apache.hadoop.hdfs.protocol.HdfsConstants; | ||
import org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolPB; | ||
import org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB; | ||
import org.apache.hadoop.hdfs.server.namenode.SafeModeException; | ||
import org.apache.hadoop.hdfs.server.namenode.ha.AbstractNNFailoverProxyProvider; | ||
import org.apache.hadoop.hdfs.server.namenode.ha.WrappedFailoverProxyProvider; | ||
import org.apache.hadoop.io.Text; | ||
import org.apache.hadoop.io.retry.DefaultFailoverProxyProvider; | ||
import org.apache.hadoop.io.retry.FailoverProxyProvider; | ||
import org.apache.hadoop.io.retry.LossyRetryInvocationHandler; | ||
import org.apache.hadoop.io.retry.RetryPolicies; | ||
import org.apache.hadoop.io.retry.RetryPolicy; | ||
import org.apache.hadoop.io.retry.RetryProxy; | ||
import org.apache.hadoop.io.retry.RetryUtils; | ||
import org.apache.hadoop.ipc.ProtobufRpcEngine; | ||
import org.apache.hadoop.ipc.RPC; | ||
import org.apache.hadoop.net.NetUtils; | ||
import org.apache.hadoop.security.SecurityUtil; | ||
import org.apache.hadoop.security.UserGroupInformation; | ||
|
||
/** | ||
* Create proxy objects with {@link ClientProtocol} to communicate with a remote | ||
* NN. Generally use {@link NameNodeProxiesClient#createProxyWithClientProtocol( | ||
* Configuration, URI, AtomicBoolean)}, which will create either an HA- or | ||
* non-HA-enabled client proxy as appropriate. | ||
* | ||
* For creating proxy objects with other protocols, please see | ||
* {@link NameNodeProxies#createProxy(Configuration, URI, Class)}. | ||
*/ | ||
@InterfaceAudience.Private | ||
public class NameNodeProxiesClient { | ||
|
||
private static final Logger LOG = LoggerFactory.getLogger( | ||
NameNodeProxiesClient.class); | ||
|
||
/** | ||
* Wrapper for a client proxy as well as its associated service ID. | ||
* This is simply used as a tuple-like return type for created NN proxy. | ||
*/ | ||
public static class ProxyAndInfo<PROXYTYPE> { | ||
private final PROXYTYPE proxy; | ||
private final Text dtService; | ||
private final InetSocketAddress address; | ||
|
||
public ProxyAndInfo(PROXYTYPE proxy, Text dtService, | ||
InetSocketAddress address) { | ||
this.proxy = proxy; | ||
this.dtService = dtService; | ||
this.address = address; | ||
} | ||
|
||
public PROXYTYPE getProxy() { | ||
return proxy; | ||
} | ||
|
||
public Text getDelegationTokenService() { | ||
return dtService; | ||
} | ||
|
||
public InetSocketAddress getAddress() { | ||
return address; | ||
} | ||
} | ||
|
||
/** | ||
* Creates the namenode proxy with the ClientProtocol. This will handle | ||
* creation of either HA- or non-HA-enabled proxy objects, depending upon | ||
* if the provided URI is a configured logical URI. | ||
* | ||
* @param conf the configuration containing the required IPC | ||
* properties, client failover configurations, etc. | ||
* @param nameNodeUri the URI pointing either to a specific NameNode | ||
* or to a logical nameservice. | ||
* @param fallbackToSimpleAuth set to true or false during calls to indicate | ||
* if a secure client falls back to simple auth | ||
* @return an object containing both the proxy and the associated | ||
* delegation token service it corresponds to | ||
* @throws IOException if there is an error creating the proxy | ||
* @see {@link NameNodeProxies#createProxy(Configuration, URI, Class)}. | ||
*/ | ||
public static ProxyAndInfo<ClientProtocol> createProxyWithClientProtocol( | ||
Configuration conf, URI nameNodeUri, AtomicBoolean fallbackToSimpleAuth) | ||
throws IOException { | ||
AbstractNNFailoverProxyProvider<ClientProtocol> failoverProxyProvider = | ||
createFailoverProxyProvider(conf, nameNodeUri, ClientProtocol.class, | ||
true, fallbackToSimpleAuth); | ||
|
||
if (failoverProxyProvider == null) { | ||
InetSocketAddress nnAddr = DFSUtilClient.getNNAddress(nameNodeUri); | ||
Text dtService = SecurityUtil.buildTokenService(nnAddr); | ||
ClientProtocol proxy = createNonHAProxyWithClientProtocol(nnAddr, conf, | ||
UserGroupInformation.getCurrentUser(), true, fallbackToSimpleAuth); | ||
return new ProxyAndInfo<>(proxy, dtService, nnAddr); | ||
} else { | ||
return createHAProxy(conf, nameNodeUri, ClientProtocol.class, | ||
failoverProxyProvider); | ||
} | ||
} | ||
|
||
/** | ||
* Generate a dummy namenode proxy instance that utilizes our hacked | ||
* {@link LossyRetryInvocationHandler}. Proxy instance generated using this | ||
* method will proactively drop RPC responses. Currently this method only | ||
* support HA setup. null will be returned if the given configuration is not | ||
* for HA. | ||
* | ||
* @param config the configuration containing the required IPC | ||
* properties, client failover configurations, etc. | ||
* @param nameNodeUri the URI pointing either to a specific NameNode | ||
* or to a logical nameservice. | ||
* @param xface the IPC interface which should be created | ||
* @param numResponseToDrop The number of responses to drop for each RPC call | ||
* @param fallbackToSimpleAuth set to true or false during calls to indicate | ||
* if a secure client falls back to simple auth | ||
* @return an object containing both the proxy and the associated | ||
* delegation token service it corresponds to. Will return null of the | ||
* given configuration does not support HA. | ||
* @throws IOException if there is an error creating the proxy | ||
*/ | ||
public static <T> ProxyAndInfo<T> createProxyWithLossyRetryHandler( | ||
Configuration config, URI nameNodeUri, Class<T> xface, | ||
int numResponseToDrop, AtomicBoolean fallbackToSimpleAuth) | ||
throws IOException { | ||
Preconditions.checkArgument(numResponseToDrop > 0); | ||
AbstractNNFailoverProxyProvider<T> failoverProxyProvider = | ||
createFailoverProxyProvider(config, nameNodeUri, xface, true, | ||
fallbackToSimpleAuth); | ||
|
||
if (failoverProxyProvider != null) { // HA case | ||
int delay = config.getInt( | ||
HdfsClientConfigKeys.Failover.SLEEPTIME_BASE_KEY, | ||
HdfsClientConfigKeys.Failover.SLEEPTIME_BASE_DEFAULT); | ||
int maxCap = config.getInt( | ||
HdfsClientConfigKeys.Failover.SLEEPTIME_MAX_KEY, | ||
HdfsClientConfigKeys.Failover.SLEEPTIME_MAX_DEFAULT); | ||
int maxFailoverAttempts = config.getInt( | ||
HdfsClientConfigKeys.Failover.MAX_ATTEMPTS_KEY, | ||
HdfsClientConfigKeys.Failover.MAX_ATTEMPTS_DEFAULT); | ||
int maxRetryAttempts = config.getInt( | ||
HdfsClientConfigKeys.Retry.MAX_ATTEMPTS_KEY, | ||
HdfsClientConfigKeys.Retry.MAX_ATTEMPTS_DEFAULT); | ||
InvocationHandler dummyHandler = new LossyRetryInvocationHandler<>( | ||
numResponseToDrop, failoverProxyProvider, | ||
RetryPolicies.failoverOnNetworkException( | ||
RetryPolicies.TRY_ONCE_THEN_FAIL, maxFailoverAttempts, | ||
Math.max(numResponseToDrop + 1, maxRetryAttempts), delay, | ||
maxCap)); | ||
|
||
@SuppressWarnings("unchecked") | ||
T proxy = (T) Proxy.newProxyInstance( | ||
failoverProxyProvider.getInterface().getClassLoader(), | ||
new Class[]{xface}, dummyHandler); | ||
Text dtService; | ||
if (failoverProxyProvider.useLogicalURI()) { | ||
dtService = HAUtilClient.buildTokenServiceForLogicalUri(nameNodeUri, | ||
HdfsConstants.HDFS_URI_SCHEME); | ||
} else { | ||
dtService = SecurityUtil.buildTokenService( | ||
DFSUtilClient.getNNAddress(nameNodeUri)); | ||
} | ||
return new ProxyAndInfo<>(proxy, dtService, | ||
DFSUtilClient.getNNAddress(nameNodeUri)); | ||
} else { | ||
LOG.warn("Currently creating proxy using " + | ||
"LossyRetryInvocationHandler requires NN HA setup"); | ||
return null; | ||
} | ||
} | ||
|
||
/** Creates the Failover proxy provider instance*/ | ||
@VisibleForTesting | ||
public static <T> AbstractNNFailoverProxyProvider<T> createFailoverProxyProvider( | ||
Configuration conf, URI nameNodeUri, Class<T> xface, boolean checkPort, | ||
AtomicBoolean fallbackToSimpleAuth) throws IOException { | ||
Class<FailoverProxyProvider<T>> failoverProxyProviderClass = null; | ||
AbstractNNFailoverProxyProvider<T> providerNN; | ||
try { | ||
// Obtain the class of the proxy provider | ||
failoverProxyProviderClass = getFailoverProxyProviderClass(conf, | ||
nameNodeUri); | ||
if (failoverProxyProviderClass == null) { | ||
return null; | ||
} | ||
// Create a proxy provider instance. | ||
Constructor<FailoverProxyProvider<T>> ctor = failoverProxyProviderClass | ||
.getConstructor(Configuration.class, URI.class, Class.class); | ||
FailoverProxyProvider<T> provider = ctor.newInstance(conf, nameNodeUri, | ||
xface); | ||
|
||
// If the proxy provider is of an old implementation, wrap it. | ||
if (!(provider instanceof AbstractNNFailoverProxyProvider)) { | ||
providerNN = new WrappedFailoverProxyProvider<>(provider); | ||
} else { | ||
providerNN = (AbstractNNFailoverProxyProvider<T>)provider; | ||
} | ||
} catch (Exception e) { | ||
final String message = "Couldn't create proxy provider " + | ||
failoverProxyProviderClass; | ||
LOG.debug(message, e); | ||
if (e.getCause() instanceof IOException) { | ||
throw (IOException) e.getCause(); | ||
} else { | ||
throw new IOException(message, e); | ||
} | ||
} | ||
|
||
// Check the port in the URI, if it is logical. | ||
if (checkPort && providerNN.useLogicalURI()) { | ||
int port = nameNodeUri.getPort(); | ||
if (port > 0 && | ||
port != HdfsClientConfigKeys.DFS_NAMENODE_RPC_PORT_DEFAULT) { | ||
// Throwing here without any cleanup is fine since we have not | ||
// actually created the underlying proxies yet. | ||
throw new IOException("Port " + port + " specified in URI " | ||
+ nameNodeUri + " but host '" + nameNodeUri.getHost() | ||
+ "' is a logical (HA) namenode" | ||
+ " and does not use port information."); | ||
} | ||
} | ||
providerNN.setFallbackToSimpleAuth(fallbackToSimpleAuth); | ||
return providerNN; | ||
} | ||
|
||
/** Gets the configured Failover proxy provider's class */ | ||
@VisibleForTesting | ||
public static <T> Class<FailoverProxyProvider<T>> getFailoverProxyProviderClass( | ||
Configuration conf, URI nameNodeUri) throws IOException { | ||
if (nameNodeUri == null) { | ||
return null; | ||
} | ||
String host = nameNodeUri.getHost(); | ||
String configKey = HdfsClientConfigKeys.Failover.PROXY_PROVIDER_KEY_PREFIX | ||
+ "." + host; | ||
try { | ||
@SuppressWarnings("unchecked") | ||
Class<FailoverProxyProvider<T>> ret = (Class<FailoverProxyProvider<T>>) | ||
conf.getClass(configKey, null, FailoverProxyProvider.class); | ||
return ret; | ||
} catch (RuntimeException e) { | ||
if (e.getCause() instanceof ClassNotFoundException) { | ||
throw new IOException("Could not load failover proxy provider class " | ||
+ conf.get(configKey) + " which is configured for authority " | ||
+ nameNodeUri, e); | ||
} else { | ||
throw e; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Creates an explicitly HA-enabled proxy object. | ||
* | ||
* @param conf the configuration object | ||
* @param nameNodeUri the URI pointing either to a specific NameNode or to a | ||
* logical nameservice. | ||
* @param xface the IPC interface which should be created | ||
* @param failoverProxyProvider Failover proxy provider | ||
* @return an object containing both the proxy and the associated | ||
* delegation token service it corresponds to | ||
* @throws IOException | ||
*/ | ||
@SuppressWarnings("unchecked") | ||
public static <T> ProxyAndInfo<T> createHAProxy( | ||
Configuration conf, URI nameNodeUri, Class<T> xface, | ||
AbstractNNFailoverProxyProvider<T> failoverProxyProvider) | ||
throws IOException { | ||
Preconditions.checkNotNull(failoverProxyProvider); | ||
// HA case | ||
DfsClientConf config = new DfsClientConf(conf); | ||
T proxy = (T) RetryProxy.create(xface, failoverProxyProvider, | ||
RetryPolicies.failoverOnNetworkException( | ||
RetryPolicies.TRY_ONCE_THEN_FAIL, config.getMaxFailoverAttempts(), | ||
config.getMaxRetryAttempts(), config.getFailoverSleepBaseMillis(), | ||
config.getFailoverSleepMaxMillis())); | ||
|
||
Text dtService; | ||
if (failoverProxyProvider.useLogicalURI()) { | ||
dtService = HAUtilClient.buildTokenServiceForLogicalUri(nameNodeUri, | ||
HdfsConstants.HDFS_URI_SCHEME); | ||
} else { | ||
dtService = SecurityUtil.buildTokenService( | ||
DFSUtilClient.getNNAddress(nameNodeUri)); | ||
} | ||
return new ProxyAndInfo<>(proxy, dtService, | ||
DFSUtilClient.getNNAddress(nameNodeUri)); | ||
} | ||
|
||
public static ClientProtocol createNonHAProxyWithClientProtocol( | ||
InetSocketAddress address, Configuration conf, UserGroupInformation ugi, | ||
boolean withRetries, AtomicBoolean fallbackToSimpleAuth) | ||
throws IOException { | ||
RPC.setProtocolEngine(conf, ClientNamenodeProtocolPB.class, | ||
ProtobufRpcEngine.class); | ||
|
||
final RetryPolicy defaultPolicy = | ||
RetryUtils.getDefaultRetryPolicy( | ||
conf, | ||
HdfsClientConfigKeys.Retry.POLICY_ENABLED_KEY, | ||
HdfsClientConfigKeys.Retry.POLICY_ENABLED_DEFAULT, | ||
HdfsClientConfigKeys.Retry.POLICY_SPEC_KEY, | ||
HdfsClientConfigKeys.Retry.POLICY_SPEC_DEFAULT, | ||
SafeModeException.class.getName()); | ||
|
||
final long version = RPC.getProtocolVersion(ClientNamenodeProtocolPB.class); | ||
ClientNamenodeProtocolPB proxy = RPC.getProtocolProxy( | ||
ClientNamenodeProtocolPB.class, version, address, ugi, conf, | ||
NetUtils.getDefaultSocketFactory(conf), | ||
org.apache.hadoop.ipc.Client.getTimeout(conf), defaultPolicy, | ||
fallbackToSimpleAuth).getProxy(); | ||
|
||
if (withRetries) { // create the proxy with retries | ||
Map<String, RetryPolicy> methodNameToPolicyMap = new HashMap<>(); | ||
ClientProtocol translatorProxy = | ||
new ClientNamenodeProtocolTranslatorPB(proxy); | ||
return (ClientProtocol) RetryProxy.create( | ||
ClientProtocol.class, | ||
new DefaultFailoverProxyProvider<>(ClientProtocol.class, | ||
translatorProxy), | ||
methodNameToPolicyMap, | ||
defaultPolicy); | ||
} else { | ||
return new ClientNamenodeProtocolTranslatorPB(proxy); | ||
} | ||
} | ||
|
||
} |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.