All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.hadoop.hbase.client.AbstractRpcBasedConnectionRegistry Maven / Gradle / Ivy

There is a newer version: 3.0.0-beta-1
Show newest version
/*
 * 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.hbase.client;

import static org.apache.hadoop.hbase.trace.TraceUtil.trace;
import static org.apache.hadoop.hbase.trace.TraceUtil.tracedFuture;
import static org.apache.hadoop.hbase.util.FutureUtils.addListener;

import com.google.errorprone.annotations.RestrictedApi;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HRegionLocation;
import org.apache.hadoop.hbase.RegionLocations;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.exceptions.ClientExceptionsUtil;
import org.apache.hadoop.hbase.exceptions.MasterRegistryFetchException;
import org.apache.hadoop.hbase.ipc.HBaseRpcController;
import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
import org.apache.hadoop.hbase.security.User;
import org.apache.hadoop.hbase.util.FutureUtils;
import org.apache.yetus.audience.InterfaceAudience;

import org.apache.hbase.thirdparty.com.google.protobuf.Message;
import org.apache.hbase.thirdparty.com.google.protobuf.RpcCallback;

import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
import org.apache.hadoop.hbase.shaded.protobuf.generated.RegistryProtos.ClientMetaService;
import org.apache.hadoop.hbase.shaded.protobuf.generated.RegistryProtos.GetActiveMasterRequest;
import org.apache.hadoop.hbase.shaded.protobuf.generated.RegistryProtos.GetActiveMasterResponse;
import org.apache.hadoop.hbase.shaded.protobuf.generated.RegistryProtos.GetClusterIdRequest;
import org.apache.hadoop.hbase.shaded.protobuf.generated.RegistryProtos.GetClusterIdResponse;
import org.apache.hadoop.hbase.shaded.protobuf.generated.RegistryProtos.GetMetaRegionLocationsRequest;
import org.apache.hadoop.hbase.shaded.protobuf.generated.RegistryProtos.GetMetaRegionLocationsResponse;

/**
 * Base class for rpc based connection registry implementation.
 * 

* The implementation needs a bootstrap node list in configuration, and then it will use the methods * in {@link ClientMetaService} to refresh the connection registry end points. *

* It also supports hedged reads, the default fan out value is 2. *

* For the actual configuration names, see javadoc of sub classes. */ @InterfaceAudience.Private abstract class AbstractRpcBasedConnectionRegistry implements ConnectionRegistry { /** Default value for the fan out of hedged requests. **/ public static final int HEDGED_REQS_FANOUT_DEFAULT = 2; private final int hedgedReadFanOut; // RPC client used to talk to the masters. private final ConnectionRegistryRpcStubHolder rpcStubHolder; private final RpcControllerFactory rpcControllerFactory; private final RegistryEndpointsRefresher registryEndpointRefresher; protected AbstractRpcBasedConnectionRegistry(Configuration conf, User user, String hedgedReqsFanoutConfigName, String initialRefreshDelaySecsConfigName, String refreshIntervalSecsConfigName, String minRefreshIntervalSecsConfigName) throws IOException { this.hedgedReadFanOut = Math.max(1, conf.getInt(hedgedReqsFanoutConfigName, HEDGED_REQS_FANOUT_DEFAULT)); rpcControllerFactory = RpcControllerFactory.instantiate(conf); rpcStubHolder = new ConnectionRegistryRpcStubHolder(conf, user, rpcControllerFactory, getBootstrapNodes(conf)); // could return null here is refresh interval is less than zero registryEndpointRefresher = RegistryEndpointsRefresher.create(conf, initialRefreshDelaySecsConfigName, refreshIntervalSecsConfigName, minRefreshIntervalSecsConfigName, this::refreshStubs); } protected abstract Set getBootstrapNodes(Configuration conf) throws IOException; protected abstract CompletableFuture> fetchEndpoints(); private void refreshStubs() throws IOException { rpcStubHolder.refreshStubs(() -> FutureUtils.get(fetchEndpoints())); } /** * For describing the actual asynchronous rpc call. *

* Typically, you can use lambda expression to implement this interface as * *

   * (c, s, d) -> s.xxx(c, your request here, d)
   * 
*/ @FunctionalInterface protected interface Callable { void call(HBaseRpcController controller, ClientMetaService.Interface stub, RpcCallback done); } private CompletableFuture call(ClientMetaService.Interface stub, Callable callable) { HBaseRpcController controller = rpcControllerFactory.newController(); CompletableFuture future = new CompletableFuture<>(); callable.call(controller, stub, resp -> { if (controller.failed()) { IOException failureReason = controller.getFailed(); future.completeExceptionally(failureReason); if (ClientExceptionsUtil.isConnectionException(failureReason)) { // RPC has failed, trigger a refresh of end points. We can have some spurious // refreshes, but that is okay since the RPC is not expensive and not in a hot path. registryEndpointRefresher.refreshNow(); } } else { future.complete(resp); } }); return future; } private IOException badResponse(String debug) { return new IOException(String.format("Invalid result for request %s. Will be retried", debug)); } /** * send requests concurrently to hedgedReadsFanout end points. If any of the request is succeeded, * we will complete the future and quit. If all the requests in one round are failed, we will * start another round to send requests concurrently tohedgedReadsFanout end points. If all end * points have been tried and all of them are failed, we will fail the future. */ private void groupCall(CompletableFuture future, Set servers, List stubs, int startIndexInclusive, Callable callable, Predicate isValidResp, String debug, ConcurrentLinkedQueue errors) { int endIndexExclusive = Math.min(startIndexInclusive + hedgedReadFanOut, stubs.size()); AtomicInteger remaining = new AtomicInteger(endIndexExclusive - startIndexInclusive); for (int i = startIndexInclusive; i < endIndexExclusive; i++) { addListener(call(stubs.get(i), callable), (r, e) -> { // a simple check to skip all the later operations earlier if (future.isDone()) { return; } if (e == null && !isValidResp.test(r)) { e = badResponse(debug); } if (e != null) { // make sure when remaining reaches 0 we have all exceptions in the errors queue errors.add(e); if (remaining.decrementAndGet() == 0) { if (endIndexExclusive == stubs.size()) { // we are done, complete the future with exception RetriesExhaustedException ex = new RetriesExhaustedException("masters", stubs.size(), new ArrayList<>(errors)); future.completeExceptionally(new MasterRegistryFetchException(servers, ex)); } else { groupCall(future, servers, stubs, endIndexExclusive, callable, isValidResp, debug, errors); } } } else { // do not need to decrement the counter any more as we have already finished the future. future.complete(r); } }); } } protected final CompletableFuture call(Callable callable, Predicate isValidResp, String debug) { CompletableFuture future = new CompletableFuture<>(); FutureUtils.addListener(rpcStubHolder.getStubs(), (addr2Stub, error) -> { if (error != null) { future.completeExceptionally(error); return; } Set servers = addr2Stub.keySet(); List stubs = new ArrayList<>(addr2Stub.values()); Collections.shuffle(stubs, ThreadLocalRandom.current()); groupCall(future, servers, stubs, 0, callable, isValidResp, debug, new ConcurrentLinkedQueue<>()); }); return future; } @RestrictedApi(explanation = "Should only be called in tests", link = "", allowedOnPath = ".*/src/test/.*") Set getParsedServers() throws IOException { return FutureUtils.get(rpcStubHolder.getStubs()).keySet(); } /** * Simple helper to transform the result of getMetaRegionLocations() rpc. */ private static RegionLocations transformMetaRegionLocations(GetMetaRegionLocationsResponse resp) { List regionLocations = new ArrayList<>(); resp.getMetaLocationsList() .forEach(location -> regionLocations.add(ProtobufUtil.toRegionLocation(location))); return new RegionLocations(regionLocations); } @Override public CompletableFuture getMetaRegionLocations() { return tracedFuture( () -> this . call( (c, s, d) -> s.getMetaRegionLocations(c, GetMetaRegionLocationsRequest.getDefaultInstance(), d), r -> r.getMetaLocationsCount() != 0, "getMetaLocationsCount") .thenApply(AbstractRpcBasedConnectionRegistry::transformMetaRegionLocations), getClass().getSimpleName() + ".getMetaRegionLocations"); } @Override public CompletableFuture getClusterId() { return tracedFuture( () -> this . call( (c, s, d) -> s.getClusterId(c, GetClusterIdRequest.getDefaultInstance(), d), GetClusterIdResponse::hasClusterId, "getClusterId()") .thenApply(GetClusterIdResponse::getClusterId), getClass().getSimpleName() + ".getClusterId"); } @Override public CompletableFuture getActiveMaster() { return tracedFuture( () -> this . call( (c, s, d) -> s.getActiveMaster(c, GetActiveMasterRequest.getDefaultInstance(), d), GetActiveMasterResponse::hasServerName, "getActiveMaster()") .thenApply(resp -> ProtobufUtil.toServerName(resp.getServerName())), getClass().getSimpleName() + ".getActiveMaster"); } @Override public void close() { trace(() -> { if (registryEndpointRefresher != null) { registryEndpointRefresher.stop(); } if (rpcStubHolder != null) { rpcStubHolder.close(); } }, getClass().getSimpleName() + ".close"); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy