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    import java.util.ArrayList;
025    import java.util.Collection;
026    import java.util.List;
027    import java.util.Map;
028    
029    import org.apache.commons.logging.Log;
030    import org.apache.commons.logging.LogFactory;
031    import org.apache.hadoop.conf.Configuration;
032    import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
033    import org.apache.hadoop.hdfs.DFSConfigKeys;
034    import org.apache.hadoop.hdfs.DFSUtil;
035    import org.apache.hadoop.hdfs.HAUtil;
036    import org.apache.hadoop.hdfs.NameNodeProxies;
037    import org.apache.hadoop.hdfs.server.namenode.ha.AbstractNNFailoverProxyProvider;
038    import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocols;
039    import org.apache.hadoop.ipc.RPC;
040    import org.apache.hadoop.security.UserGroupInformation;
041    
042    import com.google.common.base.Preconditions;
043    
044    /**
045     * A FailoverProxyProvider implementation which allows one to configure two URIs
046     * to connect to during fail-over. The first configured address is tried first,
047     * and on a fail-over event the other address is tried.
048     */
049    public class ConfiguredFailoverProxyProvider<T> extends
050        AbstractNNFailoverProxyProvider<T> {
051      
052      private static final Log LOG =
053          LogFactory.getLog(ConfiguredFailoverProxyProvider.class);
054      
055      private final Configuration conf;
056      private final List<AddressRpcProxyPair<T>> proxies =
057          new ArrayList<AddressRpcProxyPair<T>>();
058      private final UserGroupInformation ugi;
059      private final Class<T> xface;
060      
061      private int currentProxyIndex = 0;
062    
063      public ConfiguredFailoverProxyProvider(Configuration conf, URI uri,
064          Class<T> xface) {
065        Preconditions.checkArgument(
066            xface.isAssignableFrom(NamenodeProtocols.class),
067            "Interface class %s is not a valid NameNode protocol!");
068        this.xface = xface;
069        
070        this.conf = new Configuration(conf);
071        int maxRetries = this.conf.getInt(
072            DFSConfigKeys.DFS_CLIENT_FAILOVER_CONNECTION_RETRIES_KEY,
073            DFSConfigKeys.DFS_CLIENT_FAILOVER_CONNECTION_RETRIES_DEFAULT);
074        this.conf.setInt(
075            CommonConfigurationKeysPublic.IPC_CLIENT_CONNECT_MAX_RETRIES_KEY,
076            maxRetries);
077        
078        int maxRetriesOnSocketTimeouts = this.conf.getInt(
079            DFSConfigKeys.DFS_CLIENT_FAILOVER_CONNECTION_RETRIES_ON_SOCKET_TIMEOUTS_KEY,
080            DFSConfigKeys.DFS_CLIENT_FAILOVER_CONNECTION_RETRIES_ON_SOCKET_TIMEOUTS_DEFAULT);
081        this.conf.setInt(
082            CommonConfigurationKeysPublic.IPC_CLIENT_CONNECT_MAX_RETRIES_ON_SOCKET_TIMEOUTS_KEY,
083            maxRetriesOnSocketTimeouts);
084        
085        try {
086          ugi = UserGroupInformation.getCurrentUser();
087          
088          Map<String, Map<String, InetSocketAddress>> map = DFSUtil.getHaNnRpcAddresses(
089              conf);
090          Map<String, InetSocketAddress> addressesInNN = map.get(uri.getHost());
091          
092          if (addressesInNN == null || addressesInNN.size() == 0) {
093            throw new RuntimeException("Could not find any configured addresses " +
094                "for URI " + uri);
095          }
096          
097          Collection<InetSocketAddress> addressesOfNns = addressesInNN.values();
098          for (InetSocketAddress address : addressesOfNns) {
099            proxies.add(new AddressRpcProxyPair<T>(address));
100          }
101    
102          // The client may have a delegation token set for the logical
103          // URI of the cluster. Clone this token to apply to each of the
104          // underlying IPC addresses so that the IPC code can find it.
105          HAUtil.cloneDelegationTokenForLogicalUri(ugi, uri, addressesOfNns);
106        } catch (IOException e) {
107          throw new RuntimeException(e);
108        }
109      }
110        
111      @Override
112      public Class<T> getInterface() {
113        return xface;
114      }
115    
116      /**
117       * Lazily initialize the RPC proxy object.
118       */
119      @Override
120      public synchronized ProxyInfo<T> getProxy() {
121        AddressRpcProxyPair<T> current = proxies.get(currentProxyIndex);
122        if (current.namenode == null) {
123          try {
124            current.namenode = NameNodeProxies.createNonHAProxy(conf,
125                current.address, xface, ugi, false, fallbackToSimpleAuth).getProxy();
126          } catch (IOException e) {
127            LOG.error("Failed to create RPC proxy to NameNode", e);
128            throw new RuntimeException(e);
129          }
130        }
131        return new ProxyInfo<T>(current.namenode, current.address.toString());
132      }
133    
134      @Override
135      public synchronized void performFailover(T currentProxy) {
136        currentProxyIndex = (currentProxyIndex + 1) % proxies.size();
137      }
138    
139      /**
140       * A little pair object to store the address and connected RPC proxy object to
141       * an NN. Note that {@link AddressRpcProxyPair#namenode} may be null.
142       */
143      private static class AddressRpcProxyPair<T> {
144        public final InetSocketAddress address;
145        public T namenode;
146        
147        public AddressRpcProxyPair(InetSocketAddress address) {
148          this.address = address;
149        }
150      }
151    
152      /**
153       * Close all the proxy objects which have been opened over the lifetime of
154       * this proxy provider.
155       */
156      @Override
157      public synchronized void close() throws IOException {
158        for (AddressRpcProxyPair<T> proxy : proxies) {
159          if (proxy.namenode != null) {
160            if (proxy.namenode instanceof Closeable) {
161              ((Closeable)proxy.namenode).close();
162            } else {
163              RPC.stopProxy(proxy.namenode);
164            }
165          }
166        }
167      }
168    
169      /**
170       * Logical URI is required for this failover proxy provider.
171       */
172      @Override
173      public boolean useLogicalURI() {
174        return true;
175      }
176    }