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.protocol.NamenodeProtocols;
038    import org.apache.hadoop.io.retry.FailoverProxyProvider;
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> implements
050        FailoverProxyProvider<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      @SuppressWarnings("unchecked")
120      @Override
121      public synchronized T getProxy() {
122        AddressRpcProxyPair current = proxies.get(currentProxyIndex);
123        if (current.namenode == null) {
124          try {
125            current.namenode = NameNodeProxies.createNonHAProxy(conf,
126                current.address, xface, ugi, false).getProxy();
127          } catch (IOException e) {
128            LOG.error("Failed to create RPC proxy to NameNode", e);
129            throw new RuntimeException(e);
130          }
131        }
132        return (T)current.namenode;
133      }
134    
135      @Override
136      public synchronized void performFailover(T currentProxy) {
137        currentProxyIndex = (currentProxyIndex + 1) % proxies.size();
138      }
139    
140      /**
141       * A little pair object to store the address and connected RPC proxy object to
142       * an NN. Note that {@link AddressRpcProxyPair#namenode} may be null.
143       */
144      private static class AddressRpcProxyPair<T> {
145        public InetSocketAddress address;
146        public T namenode;
147        
148        public AddressRpcProxyPair(InetSocketAddress address) {
149          this.address = address;
150        }
151      }
152    
153      /**
154       * Close all the proxy objects which have been opened over the lifetime of
155       * this proxy provider.
156       */
157      @Override
158      public synchronized void close() throws IOException {
159        for (AddressRpcProxyPair<T> proxy : proxies) {
160          if (proxy.namenode != null) {
161            if (proxy.namenode instanceof Closeable) {
162              ((Closeable)proxy.namenode).close();
163            } else {
164              RPC.stopProxy(proxy.namenode);
165            }
166          }
167        }
168      }
169    }