HBASE-25252 Move HMaster inner classes out (#2628)

Signed-off-by: Viraj Jasani <vjasani@apache.org>
Signed-off-by: Wellington Chevreuil <wchevreuil@apache.org>
Signed-off-by: Guanghao Zhang <zghao@apache.org>
This commit is contained in:
Duo Zhang 2020-11-07 20:05:04 +08:00 committed by GitHub
parent 23e656712b
commit 671129df56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 174 additions and 112 deletions

View File

@ -50,10 +50,7 @@ import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.hadoop.conf.Configuration;
@ -95,7 +92,6 @@ import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
import org.apache.hadoop.hbase.exceptions.MasterStoppedException;
import org.apache.hadoop.hbase.executor.ExecutorType;
import org.apache.hadoop.hbase.favored.FavoredNodesManager;
import org.apache.hadoop.hbase.http.InfoServer;
import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils;
import org.apache.hadoop.hbase.ipc.RpcServer;
import org.apache.hadoop.hbase.ipc.ServerNotRunningYetException;
@ -238,76 +234,23 @@ import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.GetRegionIn
import org.apache.hadoop.hbase.shaded.protobuf.generated.SnapshotProtos.SnapshotDescription;
/**
* HMaster is the "master server" for HBase. An HBase cluster has one active
* master. If many masters are started, all compete. Whichever wins goes on to
* run the cluster. All others park themselves in their constructor until
* master or cluster shutdown or until the active master loses its lease in
* zookeeper. Thereafter, all running master jostle to take over master role.
*
* <p>The Master can be asked shutdown the cluster. See {@link #shutdown()}. In
* this case it will tell all regionservers to go down and then wait on them
* all reporting in that they are down. This master will then shut itself down.
*
* <p>You can also shutdown just this master. Call {@link #stopMaster()}.
*
* HMaster is the "master server" for HBase. An HBase cluster has one active master. If many masters
* are started, all compete. Whichever wins goes on to run the cluster. All others park themselves
* in their constructor until master or cluster shutdown or until the active master loses its lease
* in zookeeper. Thereafter, all running master jostle to take over master role.
* <p/>
* The Master can be asked shutdown the cluster. See {@link #shutdown()}. In this case it will tell
* all regionservers to go down and then wait on them all reporting in that they are down. This
* master will then shut itself down.
* <p/>
* You can also shutdown just this master. Call {@link #stopMaster()}.
* @see org.apache.zookeeper.Watcher
*/
@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS)
@SuppressWarnings("deprecation")
public class HMaster extends HRegionServer implements MasterServices {
private static Logger LOG = LoggerFactory.getLogger(HMaster.class);
/**
* Protection against zombie master. Started once Master accepts active responsibility and
* starts taking over responsibilities. Allows a finite time window before giving up ownership.
*/
private static class InitializationMonitor extends Thread {
/** The amount of time in milliseconds to sleep before checking initialization status. */
public static final String TIMEOUT_KEY = "hbase.master.initializationmonitor.timeout";
public static final long TIMEOUT_DEFAULT = TimeUnit.MILLISECONDS.convert(15, TimeUnit.MINUTES);
/**
* When timeout expired and initialization has not complete, call {@link System#exit(int)} when
* true, do nothing otherwise.
*/
public static final String HALT_KEY = "hbase.master.initializationmonitor.haltontimeout";
public static final boolean HALT_DEFAULT = false;
private final HMaster master;
private final long timeout;
private final boolean haltOnTimeout;
/** Creates a Thread that monitors the {@link #isInitialized()} state. */
InitializationMonitor(HMaster master) {
super("MasterInitializationMonitor");
this.master = master;
this.timeout = master.getConfiguration().getLong(TIMEOUT_KEY, TIMEOUT_DEFAULT);
this.haltOnTimeout = master.getConfiguration().getBoolean(HALT_KEY, HALT_DEFAULT);
this.setDaemon(true);
}
@Override
public void run() {
try {
while (!master.isStopped() && master.isActiveMaster()) {
Thread.sleep(timeout);
if (master.isInitialized()) {
LOG.debug("Initialization completed within allotted tolerance. Monitor exiting.");
} else {
LOG.error("Master failed to complete initialization after " + timeout + "ms. Please"
+ " consider submitting a bug report including a thread dump of this process.");
if (haltOnTimeout) {
LOG.error("Zombie Master exiting. Thread dump to stdout");
Threads.printThreadInfo(System.out, "Zombie HMaster");
System.exit(-1);
}
}
}
} catch (InterruptedException ie) {
LOG.trace("InitMonitor thread interrupted. Existing.");
}
}
}
private static final Logger LOG = LoggerFactory.getLogger(HMaster.class);
// MASTER is name of the webapp and the attribute name used stuffing this
//instance into web context.
@ -464,48 +407,6 @@ public class HMaster extends HRegionServer implements MasterServices {
// Cached clusterId on stand by masters to serve clusterID requests from clients.
private final CachedClusterId cachedClusterId;
public static class RedirectServlet extends HttpServlet {
private static final long serialVersionUID = 2894774810058302473L;
private final int regionServerInfoPort;
private final String regionServerHostname;
/**
* @param infoServer that we're trying to send all requests to
* @param hostname may be null. if given, will be used for redirects instead of host from client.
*/
public RedirectServlet(InfoServer infoServer, String hostname) {
regionServerInfoPort = infoServer.getPort();
regionServerHostname = hostname;
}
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String redirectHost = regionServerHostname;
if(redirectHost == null) {
redirectHost = request.getServerName();
if(!Addressing.isLocalAddress(InetAddress.getByName(redirectHost))) {
LOG.warn("Couldn't resolve '" + redirectHost + "' as an address local to this node and '" +
MASTER_HOSTNAME_KEY + "' is not set; client will get an HTTP 400 response. If " +
"your HBase deployment relies on client accessible names that the region server process " +
"can't resolve locally, then you should set the previously mentioned configuration variable " +
"to an appropriate hostname.");
// no sending client provided input back to the client, so the goal host is just in the logs.
response.sendError(400, "Request was to a host that I can't resolve for any of the network interfaces on " +
"this node. If this is due to an intermediary such as an HTTP load balancer or other proxy, your HBase " +
"administrator can set '" + MASTER_HOSTNAME_KEY + "' to point to the correct hostname.");
return;
}
}
// TODO this scheme should come from looking at the scheme registered in the infoserver's http server for the
// host and port we're using, but it's buried way too deep to do that ATM.
String redirectUrl = request.getScheme() + "://"
+ redirectHost + ":" + regionServerInfoPort
+ request.getRequestURI();
response.sendRedirect(redirectUrl);
}
}
/**
* Initializes the HMaster. The steps are as follows:
* <p>
@ -678,7 +579,7 @@ public class HMaster extends HRegionServer implements MasterServices {
final String redirectHostname =
StringUtils.isBlank(useThisHostnameInstead) ? null : useThisHostnameInstead;
final RedirectServlet redirect = new RedirectServlet(infoServer, redirectHostname);
final MasterRedirectServlet redirect = new MasterRedirectServlet(infoServer, redirectHostname);
final WebAppContext context = new WebAppContext(null, "/", null, null, null, null, WebAppContext.NO_SESSIONS);
context.addServlet(new ServletHolder(redirect), "/*");
context.setServer(masterJettyServer);
@ -998,7 +899,7 @@ public class HMaster extends HRegionServer implements MasterServices {
this.activeMaster = true;
// Start the Zombie master detector after setting master as active, see HBASE-21535
Thread zombieDetector = new Thread(new InitializationMonitor(this),
Thread zombieDetector = new Thread(new MasterInitializationMonitor(this),
"ActiveMasterInitializationMonitor-" + System.currentTimeMillis());
zombieDetector.setDaemon(true);
zombieDetector.start();

View File

@ -0,0 +1,80 @@
/*
* 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.master;
import java.util.concurrent.TimeUnit;
import org.apache.hadoop.hbase.util.Threads;
import org.apache.yetus.audience.InterfaceAudience;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Protection against zombie master. Started once Master accepts active responsibility and starts
* taking over responsibilities. Allows a finite time window before giving up ownership.
*/
@InterfaceAudience.Private
class MasterInitializationMonitor extends Thread {
private static final Logger LOG = LoggerFactory.getLogger(MasterInitializationMonitor.class);
/** The amount of time in milliseconds to sleep before checking initialization status. */
public static final String TIMEOUT_KEY = "hbase.master.initializationmonitor.timeout";
public static final long TIMEOUT_DEFAULT = TimeUnit.MILLISECONDS.convert(15, TimeUnit.MINUTES);
/**
* When timeout expired and initialization has not complete, call {@link System#exit(int)} when
* true, do nothing otherwise.
*/
public static final String HALT_KEY = "hbase.master.initializationmonitor.haltontimeout";
public static final boolean HALT_DEFAULT = false;
private final HMaster master;
private final long timeout;
private final boolean haltOnTimeout;
/** Creates a Thread that monitors the {@link #isInitialized()} state. */
MasterInitializationMonitor(HMaster master) {
super("MasterInitializationMonitor");
this.master = master;
this.timeout = master.getConfiguration().getLong(TIMEOUT_KEY, TIMEOUT_DEFAULT);
this.haltOnTimeout = master.getConfiguration().getBoolean(HALT_KEY, HALT_DEFAULT);
this.setDaemon(true);
}
@Override
public void run() {
try {
while (!master.isStopped() && master.isActiveMaster()) {
Thread.sleep(timeout);
if (master.isInitialized()) {
LOG.debug("Initialization completed within allotted tolerance. Monitor exiting.");
} else {
LOG.error("Master failed to complete initialization after " + timeout + "ms. Please" +
" consider submitting a bug report including a thread dump of this process.");
if (haltOnTimeout) {
LOG.error("Zombie Master exiting. Thread dump to stdout");
Threads.printThreadInfo(System.out, "Zombie HMaster");
System.exit(-1);
}
}
}
} catch (InterruptedException ie) {
LOG.trace("InitMonitor thread interrupted. Existing.");
}
}
}

View File

@ -0,0 +1,81 @@
/*
* 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.master;
import static org.apache.hadoop.hbase.util.DNS.MASTER_HOSTNAME_KEY;
import java.io.IOException;
import java.net.InetAddress;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.hadoop.hbase.http.InfoServer;
import org.apache.hadoop.hbase.util.Addressing;
import org.apache.yetus.audience.InterfaceAudience;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@InterfaceAudience.Private
class MasterRedirectServlet extends HttpServlet {
private static final long serialVersionUID = 2894774810058302473L;
private static final Logger LOG = LoggerFactory.getLogger(MasterRedirectServlet.class);
private final int regionServerInfoPort;
private final String regionServerHostname;
/**
* @param infoServer that we're trying to send all requests to
* @param hostname may be null. if given, will be used for redirects instead of host from client.
*/
public MasterRedirectServlet(InfoServer infoServer, String hostname) {
regionServerInfoPort = infoServer.getPort();
regionServerHostname = hostname;
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String redirectHost = regionServerHostname;
if (redirectHost == null) {
redirectHost = request.getServerName();
if (!Addressing.isLocalAddress(InetAddress.getByName(redirectHost))) {
LOG.warn("Couldn't resolve '" + redirectHost + "' as an address local to this node and '" +
MASTER_HOSTNAME_KEY + "' is not set; client will get an HTTP 400 response. If " +
"your HBase deployment relies on client accessible names that the region server " +
"process can't resolve locally, then you should set the previously mentioned " +
"configuration variable to an appropriate hostname.");
// no sending client provided input back to the client, so the goal host is just in the
// logs.
response.sendError(400,
"Request was to a host that I can't resolve for any of the network interfaces on " +
"this node. If this is due to an intermediary such as an HTTP load balancer or " +
"other proxy, your HBase administrator can set '" + MASTER_HOSTNAME_KEY +
"' to point to the correct hostname.");
return;
}
}
// TODO: this scheme should come from looking at the scheme registered in the infoserver's http
// server for the host and port we're using, but it's buried way too deep to do that ATM.
String redirectUrl = request.getScheme() + "://" + redirectHost + ":" + regionServerInfoPort +
request.getRequestURI();
response.sendRedirect(redirectUrl);
}
}