001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.hadoop.hdfs.server.namenode.ha;
019    
020    import java.io.Closeable;
021    import java.io.IOException;
022    import java.net.InetSocketAddress;
023    import java.net.URI;
024    
025    import org.apache.hadoop.conf.Configuration;
026    import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
027    import org.apache.hadoop.hdfs.DFSConfigKeys;
028    import org.apache.hadoop.hdfs.NameNodeProxies;
029    import org.apache.hadoop.hdfs.server.namenode.NameNode;
030    import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocols;
031    import org.apache.hadoop.io.retry.FailoverProxyProvider;
032    import org.apache.hadoop.ipc.RPC;
033    import org.apache.hadoop.security.UserGroupInformation;
034    
035    import com.google.common.base.Preconditions;
036    
037    /**
038     * A NNFailoverProxyProvider implementation which works on IP failover setup.
039     * Only one proxy is used to connect to both servers and switching between
040     * the servers is done by the environment/infrastructure, which guarantees
041     * clients can consistently reach only one node at a time.
042     *
043     * Clients with a live connection will likely get connection reset after an
044     * IP failover. This case will be handled by the 
045     * FailoverOnNetworkExceptionRetry retry policy. I.e. if the call is
046     * not idempotent, it won't get retried.
047     *
048     * A connection reset while setting up a connection (i.e. before sending a
049     * request) will be handled in ipc client.
050     *
051     * The namenode URI must contain a resolvable host name.
052     */
053    public class IPFailoverProxyProvider<T> extends
054        AbstractNNFailoverProxyProvider<T> {
055      private final Configuration conf;
056      private final Class<T> xface;
057      private final URI nameNodeUri;
058      private ProxyInfo<T> nnProxyInfo = null;
059      
060      public IPFailoverProxyProvider(Configuration conf, URI uri,
061          Class<T> xface) {
062        Preconditions.checkArgument(
063            xface.isAssignableFrom(NamenodeProtocols.class),
064            "Interface class %s is not a valid NameNode protocol!");
065        this.xface = xface;
066        this.nameNodeUri = uri;
067    
068        this.conf = new Configuration(conf);
069        int maxRetries = this.conf.getInt(
070            DFSConfigKeys.DFS_CLIENT_FAILOVER_CONNECTION_RETRIES_KEY,
071            DFSConfigKeys.DFS_CLIENT_FAILOVER_CONNECTION_RETRIES_DEFAULT);
072        this.conf.setInt(
073            CommonConfigurationKeysPublic.IPC_CLIENT_CONNECT_MAX_RETRIES_KEY,
074            maxRetries);
075        
076        int maxRetriesOnSocketTimeouts = this.conf.getInt(
077            DFSConfigKeys.DFS_CLIENT_FAILOVER_CONNECTION_RETRIES_ON_SOCKET_TIMEOUTS_KEY,
078            DFSConfigKeys.DFS_CLIENT_FAILOVER_CONNECTION_RETRIES_ON_SOCKET_TIMEOUTS_DEFAULT);
079        this.conf.setInt(
080            CommonConfigurationKeysPublic.IPC_CLIENT_CONNECT_MAX_RETRIES_ON_SOCKET_TIMEOUTS_KEY,
081            maxRetriesOnSocketTimeouts);
082      }
083        
084      @Override
085      public Class<T> getInterface() {
086        return xface;
087      }
088    
089      @Override
090      public synchronized ProxyInfo<T> getProxy() {
091        // Create a non-ha proxy if not already created.
092        if (nnProxyInfo == null) {
093          try {
094            // Create a proxy that is not wrapped in RetryProxy
095            InetSocketAddress nnAddr = NameNode.getAddress(nameNodeUri);
096            nnProxyInfo = new ProxyInfo<T>(NameNodeProxies.createNonHAProxy(
097                conf, nnAddr, xface, UserGroupInformation.getCurrentUser(), 
098                false).getProxy(), nnAddr.toString());
099          } catch (IOException ioe) {
100            throw new RuntimeException(ioe);
101          }
102        }
103        return nnProxyInfo;
104      }
105    
106      /** Nothing to do for IP failover */
107      @Override
108      public void performFailover(T currentProxy) {
109      }
110    
111      /**
112       * Close the proxy,
113       */
114      @Override
115      public synchronized void close() throws IOException {
116        if (nnProxyInfo == null) {
117          return;
118        }
119        if (nnProxyInfo.proxy instanceof Closeable) {
120          ((Closeable)nnProxyInfo.proxy).close();
121        } else {
122          RPC.stopProxy(nnProxyInfo.proxy);
123        }
124      }
125    
126      /**
127       * Logical URI is not used for IP failover.
128       */
129      @Override
130      public boolean useLogicalURI() {
131        return false;
132      }
133    }