View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.client;
20  
21  import static org.apache.hadoop.hbase.util.DNS.getMasterHostname;
22  import com.google.common.base.Preconditions;
23  import com.google.common.base.Strings;
24  import com.google.common.collect.ImmutableMap;
25  import com.google.common.collect.ImmutableSet;
26  import com.google.common.net.HostAndPort;
27  import com.google.protobuf.Message;
28  import com.google.protobuf.RpcController;
29  import java.io.IOException;
30  import java.net.UnknownHostException;
31  import java.util.ArrayList;
32  import java.util.Collections;
33  import java.util.HashSet;
34  import java.util.List;
35  import java.util.Set;
36  import java.util.concurrent.ThreadLocalRandom;
37  import org.apache.hadoop.conf.Configuration;
38  import org.apache.hadoop.hbase.HBaseIOException;
39  import org.apache.hadoop.hbase.HConstants;
40  import org.apache.hadoop.hbase.HRegionLocation;
41  import org.apache.hadoop.hbase.RegionLocations;
42  import org.apache.hadoop.hbase.ServerName;
43  import org.apache.hadoop.hbase.TableName;
44  import org.apache.hadoop.hbase.classification.InterfaceAudience;
45  import org.apache.hadoop.hbase.exceptions.ClientExceptionsUtil;
46  import org.apache.hadoop.hbase.exceptions.MasterRegistryFetchException;
47  import org.apache.hadoop.hbase.ipc.BlockingRpcCallback;
48  import org.apache.hadoop.hbase.ipc.HBaseRpcController;
49  import org.apache.hadoop.hbase.ipc.RpcClient;
50  import org.apache.hadoop.hbase.ipc.RpcClientFactory;
51  import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
52  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
53  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
54  import org.apache.hadoop.hbase.security.User;
55  
56  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ClientMetaService;
57  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetClusterIdRequest;
58  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetClusterIdResponse;
59  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetMastersRequest;
60  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetMastersResponse;
61  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetMastersResponseEntry;
62  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetMetaRegionLocationsRequest;
63  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetMetaRegionLocationsResponse;
64  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetNumLiveRSRequest;
65  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetNumLiveRSResponse;
66  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableStateRequest;
67  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableStateResponse;
68  
69  /**
70   * Master based registry implementation. Makes RPCs to the configured master addresses from config
71   * {@value org.apache.hadoop.hbase.HConstants#MASTER_ADDRS_KEY}. All the registry methods are
72   * blocking unlike implementations in other branches.
73   */
74  @InterfaceAudience.Private
75  public class MasterRegistry implements ConnectionRegistry {
76    private static final String MASTER_ADDRS_CONF_SEPARATOR = ",";
77  
78    private volatile ImmutableMap<String, ClientMetaService.Interface> masterAddr2Stub;
79  
80    // RPC client used to talk to the masters.
81    private RpcClient rpcClient;
82    private RpcControllerFactory rpcControllerFactory;
83    private int rpcTimeoutMs;
84  
85    protected MasterAddressRefresher masterAddressRefresher;
86  
87    @Override
88    public void init(Connection connection) throws IOException {
89      Configuration conf = connection.getConfiguration();
90      rpcTimeoutMs = (int) Math.min(Integer.MAX_VALUE,
91          conf.getLong(HConstants.HBASE_RPC_TIMEOUT_KEY, HConstants.DEFAULT_HBASE_RPC_TIMEOUT));
92      // HBASE-25051: we pass cluster id as null here since we do not have a cluster id yet, we have
93      // to fetch this through the master registry...
94      // This is a problem as we will use the cluster id to determine the authentication method
95      rpcClient = RpcClientFactory.createClient(conf, null);
96      rpcControllerFactory = RpcControllerFactory.instantiate(conf);
97      populateMasterStubs(parseMasterAddrs(conf));
98      masterAddressRefresher = new MasterAddressRefresher(conf, this);
99    }
100 
101   protected interface Callable <T extends Message> {
102     T call(ClientMetaService.Interface stub, RpcController controller) throws IOException;
103   }
104 
105    protected <T extends Message> T doCall(Callable<T> callable)
106        throws MasterRegistryFetchException {
107     Exception lastException = null;
108     Set<String> masters = masterAddr2Stub.keySet();
109     List<ClientMetaService.Interface> stubs = new ArrayList<>(masterAddr2Stub.values());
110     Collections.shuffle(stubs, ThreadLocalRandom.current());
111     for (ClientMetaService.Interface stub: stubs) {
112       HBaseRpcController controller = rpcControllerFactory.newController();
113       try {
114         T resp = callable.call(stub, controller);
115         if (!controller.failed()) {
116           return resp;
117         }
118         lastException = controller.getFailed();
119       } catch (Exception e) {
120         lastException = e;
121       }
122       if (ClientExceptionsUtil.isConnectionException(lastException)) {
123         masterAddressRefresher.refreshNow();
124       }
125     }
126     // rpcs to all masters failed.
127     throw new MasterRegistryFetchException(masters, lastException);
128   }
129 
130   @Override
131   public ServerName getActiveMaster() throws IOException {
132     GetMastersResponseEntry activeMaster = null;
133     for (GetMastersResponseEntry entry: getMastersInternal().getMasterServersList()) {
134       if (entry.getIsActive()) {
135         activeMaster = entry;
136         break;
137       }
138     }
139     if (activeMaster == null) {
140       throw new HBaseIOException("No active master found");
141     }
142     return ProtobufUtil.toServerName(activeMaster.getServerName());
143   }
144 
145   List<ServerName> getMasters() throws IOException {
146     List<ServerName> result = new ArrayList<>();
147     for (GetMastersResponseEntry entry: getMastersInternal().getMasterServersList()) {
148       result.add(ProtobufUtil.toServerName(entry.getServerName()));
149     }
150     return result;
151   }
152 
153   private GetMastersResponse getMastersInternal() throws IOException {
154     return doCall(new Callable<GetMastersResponse>() {
155       @Override
156       public GetMastersResponse call(
157           ClientMetaService.Interface stub, RpcController controller) throws IOException {
158         BlockingRpcCallback<GetMastersResponse> cb = new BlockingRpcCallback<>();
159         stub.getMasters(controller, GetMastersRequest.getDefaultInstance(), cb);
160         return cb.get();
161       }
162     });
163   }
164 
165   @Override
166   public RegionLocations getMetaRegionLocations() throws IOException {
167     GetMetaRegionLocationsResponse resp = doCall(new Callable<GetMetaRegionLocationsResponse>() {
168       @Override
169       public GetMetaRegionLocationsResponse call(
170           ClientMetaService.Interface stub, RpcController controller) throws IOException {
171         BlockingRpcCallback<GetMetaRegionLocationsResponse> cb = new BlockingRpcCallback<>();
172         stub.getMetaRegionLocations(controller, GetMetaRegionLocationsRequest.getDefaultInstance(),
173             cb);
174         return cb.get();
175       }
176     });
177     List<HRegionLocation> result = new ArrayList<>();
178     for (HBaseProtos.RegionLocation loc: resp.getMetaLocationsList()) {
179       result.add(ProtobufUtil.toRegionLocation(loc));
180     }
181     return new RegionLocations(result);
182   }
183 
184   @Override
185   public String getClusterId() throws IOException {
186     GetClusterIdResponse resp = doCall(new Callable<GetClusterIdResponse>() {
187       @Override
188       public GetClusterIdResponse call(ClientMetaService.Interface stub, RpcController controller)
189           throws IOException {
190         BlockingRpcCallback<GetClusterIdResponse> cb = new BlockingRpcCallback<>();
191         stub.getClusterId(controller, GetClusterIdRequest.getDefaultInstance(), cb);
192         return cb.get();
193       }
194     });
195     return resp.getClusterId();
196   }
197 
198   @Override
199   public int getCurrentNrHRS() throws IOException {
200     GetNumLiveRSResponse resp = doCall(new Callable<GetNumLiveRSResponse>() {
201       @Override
202       public GetNumLiveRSResponse call(ClientMetaService.Interface stub, RpcController controller)
203           throws IOException {
204         BlockingRpcCallback<GetNumLiveRSResponse> cb = new BlockingRpcCallback<>();
205         stub.getNumLiveRS(controller, GetNumLiveRSRequest.getDefaultInstance(), cb);
206         return cb.get();
207       }
208     });
209     return resp.getNumRegionServers();
210   }
211 
212   @Override
213   public boolean isTableOnlineState(TableName tableName, boolean enabled) throws IOException {
214     final GetTableStateRequest request = GetTableStateRequest.newBuilder().setTableName(
215       tableName.getNameAsString()).setIsEnabled(enabled).build();
216     GetTableStateResponse resp = doCall(new Callable<GetTableStateResponse>() {
217       @Override
218       public GetTableStateResponse call(ClientMetaService.Interface stub, RpcController controller)
219           throws IOException {
220         BlockingRpcCallback<GetTableStateResponse> cb = new BlockingRpcCallback<>();
221         stub.getTableState(controller, request, cb);
222         return cb.get();
223       }
224     });
225     return resp.getEnabledOrDisabled();
226   }
227 
228   @Override
229   public void close() {
230     if (rpcClient != null) {
231       rpcClient.close();
232     }
233   }
234 
235   /**
236    * Parses the list of master addresses from the provided configuration. Supported format is comma
237    * separated host[:port] values. If no port number if specified, default master port is assumed.
238    * @param conf Configuration to parse from.
239    */
240   @InterfaceAudience.Private
241    public static Set<ServerName> parseMasterAddrs(Configuration conf) throws UnknownHostException {
242     Set<ServerName> masterAddrs = new HashSet<>();
243     String configuredMasters = getMasterAddr(conf);
244     for (String masterAddr : configuredMasters.split(MASTER_ADDRS_CONF_SEPARATOR)) {
245       HostAndPort masterHostPort =
246           HostAndPort.fromString(masterAddr.trim()).withDefaultPort(HConstants.DEFAULT_MASTER_PORT);
247       masterAddrs.add(ServerName.valueOf(masterHostPort.toString(), ServerName.NON_STARTCODE));
248     }
249     Preconditions.checkArgument(!masterAddrs.isEmpty(), "At least one master address is needed");
250     return masterAddrs;
251   }
252 
253   /**
254    * Builds the default master address end point if it is not specified in the configuration.
255    * <p/>
256    * Will be called in {@code HBaseTestingUtility}.
257    */
258   @InterfaceAudience.Private
259   public static String getMasterAddr(Configuration conf) throws UnknownHostException {
260     String masterAddrFromConf = conf.get(HConstants.MASTER_ADDRS_KEY);
261     if (!Strings.isNullOrEmpty(masterAddrFromConf)) {
262       return masterAddrFromConf;
263     }
264     String hostname = getMasterHostname(conf);
265     int port = conf.getInt(HConstants.MASTER_PORT, HConstants.DEFAULT_MASTER_PORT);
266     return String.format("%s:%d", hostname, port);
267   }
268 
269   void populateMasterStubs(Set<ServerName> masters) throws IOException {
270     Preconditions.checkNotNull(masters);
271     ImmutableMap.Builder<String, ClientMetaService.Interface> builder = ImmutableMap.builder();
272     User user = User.getCurrent();
273     for (ServerName masterAddr : masters) {
274       builder.put(masterAddr.toString(), ClientMetaService.newStub(
275           rpcClient.createRpcChannel(masterAddr, user, rpcTimeoutMs)));
276     }
277     masterAddr2Stub = builder.build();
278   }
279 
280   @InterfaceAudience.Private
281   ImmutableSet<String> getParsedMasterServers() {
282     return masterAddr2Stub.keySet();
283   }
284 }