Skip to content

Commit f65e8fe

Browse files
author
Dmitriy Fingerman
committed
HIVE-28775: HiveServer2: enabling HA Leader endpoint on a different port than WebUI
1 parent 38dc6b6 commit f65e8fe

File tree

10 files changed

+379
-149
lines changed

10 files changed

+379
-149
lines changed

common/src/java/org/apache/hadoop/hive/conf/HiveConf.java

+2
Original file line numberDiff line numberDiff line change
@@ -3979,6 +3979,8 @@ public static enum ConfVars {
39793979
HIVE_SERVER2_WEBUI_BIND_HOST("hive.server2.webui.host", "0.0.0.0", "The host address the HiveServer2 WebUI will listen on"),
39803980
HIVE_SERVER2_WEBUI_PORT("hive.server2.webui.port", 10002, "The port the HiveServer2 WebUI will listen on. This can be"
39813981
+ "set to 0 or a negative integer to disable the web UI"),
3982+
HIVE_SERVER2_HEALTH_HA_PORT("hive.server2.health.ha.port", 11002, "The port the HiveServer2 health-ha web app will listen on. This can be"
3983+
+ "set to 0 or a negative integer to disable HS2 health-ha checking"),
39823984
HIVE_SERVER2_WEBUI_MAX_THREADS("hive.server2.webui.max.threads", 50, "The max HiveServer2 WebUI threads"),
39833985
HIVE_SERVER2_WEBUI_USE_SSL("hive.server2.webui.use.ssl", false,
39843986
"Set this to true for using SSL encryption for HiveServer2 WebUI."),

common/src/java/org/apache/hive/http/HttpServer.java

+122-61
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,16 @@
8282
import org.eclipse.jetty.security.ConstraintMapping;
8383
import org.eclipse.jetty.security.ConstraintSecurityHandler;
8484
import org.eclipse.jetty.security.LoginService;
85-
import org.eclipse.jetty.server.Connector;
8685
import org.eclipse.jetty.server.Handler;
8786
import org.eclipse.jetty.server.HttpConfiguration;
8887
import org.eclipse.jetty.server.HttpConnectionFactory;
8988
import org.eclipse.jetty.server.LowResourceMonitor;
89+
import org.eclipse.jetty.server.Request;
9090
import org.eclipse.jetty.server.Server;
9191
import org.eclipse.jetty.server.handler.ContextHandler.Context;
9292
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
9393
import org.eclipse.jetty.server.ServerConnector;
94+
import org.eclipse.jetty.server.handler.HandlerCollection;
9495
import org.eclipse.jetty.servlet.DefaultServlet;
9596
import org.eclipse.jetty.servlet.FilterHolder;
9697
import org.eclipse.jetty.servlet.FilterMapping;
@@ -138,6 +139,8 @@ public class HttpServer {
138139
private String appDir;
139140
private WebAppContext webAppContext;
140141
private Server webServer;
142+
private QueuedThreadPool threadPool;
143+
private PortHandlerWrapper portHandlerWrapper;
141144

142145
/**
143146
* Create a status server on the given port.
@@ -179,6 +182,7 @@ public static class Builder {
179182
new LinkedList<Pair<String, Class<? extends HttpServlet>>>();
180183
private boolean disableDirListing = false;
181184
private final Map<String, Pair<String, Filter>> globalFilters = new LinkedHashMap<>();
185+
private String contextPath;
182186

183187
public Builder(String name) {
184188
Preconditions.checkArgument(name != null && !name.isEmpty(), "Name must be specified");
@@ -189,6 +193,10 @@ public HttpServer build() throws IOException {
189193
return new HttpServer(this);
190194
}
191195

196+
public void setContextPath(String contextPath) {
197+
this.contextPath = contextPath;
198+
}
199+
192200
public Builder setConf(HiveConf origConf) {
193201
this.conf = new HiveConf(origConf);
194202
origConf.stripHiddenConfigurations(conf);
@@ -489,13 +497,13 @@ static boolean userHasAdministratorAccess(ServletContext servletContext,
489497
/**
490498
* Create the web context for the application of specified name
491499
*/
492-
WebAppContext createWebAppContext(Builder b) {
500+
WebAppContext createWebAppContext(Builder b) throws FileNotFoundException {
493501
WebAppContext ctx = new WebAppContext();
494502
setContextAttributes(ctx.getServletContext(), b.contextAttrs);
495503
ctx.getServletContext().getSessionCookieConfig().setHttpOnly(true);
496504
ctx.setDisplayName(b.name);
497-
ctx.setContextPath("/");
498-
ctx.setWar(appDir + "/" + b.name);
505+
ctx.setContextPath(b.contextPath == null ? "/" : b.contextPath);
506+
ctx.setWar(getWebAppsPath(b.name) + "/" + b.name);
499507
return ctx;
500508
}
501509

@@ -519,8 +527,9 @@ void setupSpnegoFilter(Builder b, ServletContextHandler ctx) throws IOException
519527
/**
520528
* Setup cross-origin requests (CORS) filter.
521529
* @param b - builder
530+
* @param webAppContext - webAppContext
522531
*/
523-
private void setupCORSFilter(Builder b) {
532+
private void setupCORSFilter(Builder b, WebAppContext webAppContext) {
524533
FilterHolder holder = new FilterHolder();
525534
holder.setClassName(CrossOriginFilter.class.getName());
526535
Map<String, String> params = new HashMap<>();
@@ -533,10 +542,62 @@ private void setupCORSFilter(Builder b) {
533542
handler.addFilterWithMapping(holder, "/*", FilterMapping.ALL);
534543
}
535544

545+
public void addWebApp(Builder b) throws IOException {
546+
WebAppContext webAppContext = createWebAppContext(b);
547+
ContextHandlerCollection portHandler = new ContextHandlerCollection();
548+
ServerConnector connector = addWebApp(b, webAppContext, portHandler);
549+
portHandlerWrapper.addHandler(connector, portHandler);
550+
}
551+
552+
private ServerConnector addWebApp(Builder b, WebAppContext webAppContext, ContextHandlerCollection portHandler)
553+
throws IOException {
554+
555+
if (b.useSPNEGO) {
556+
// Secure the web server with kerberos
557+
setupSpnegoFilter(b, webAppContext);
558+
}
559+
560+
if (b.enableCORS) {
561+
setupCORSFilter(b, webAppContext);
562+
}
563+
564+
Map<String, String> xFrameParams = setHeaders();
565+
if (b.xFrameEnabled) {
566+
setupXframeFilter(b, xFrameParams, webAppContext);
567+
}
568+
569+
if (b.disableDirListing) {
570+
disableDirectoryListingOnServlet(webAppContext);
571+
}
572+
573+
ServerConnector connector = createChannelConnector(threadPool.getQueueSize(), b);
574+
webServer.addConnector(connector);
575+
576+
RewriteHandler rwHandler = new RewriteHandler();
577+
rwHandler.setRewriteRequestURI(true);
578+
rwHandler.setRewritePathInfo(false);
579+
580+
RewriteRegexRule rootRule = new RewriteRegexRule();
581+
rootRule.setRegex("^/$");
582+
rootRule.setReplacement(b.contextRootRewriteTarget);
583+
rootRule.setTerminating(true);
584+
585+
rwHandler.addRule(rootRule);
586+
rwHandler.setHandler(webAppContext);
587+
588+
portHandler.addHandler(rwHandler);
589+
590+
for (Pair<String, Class<? extends HttpServlet>> p : b.servlets) {
591+
addServlet(p.getFirst(), "/" + p.getFirst(), p.getSecond(), webAppContext);
592+
}
593+
594+
return connector;
595+
}
596+
536597
/**
537598
* Create a channel connector for "http/https" requests
538599
*/
539-
Connector createChannelConnector(int queueSize, Builder b) {
600+
ServerConnector createChannelConnector(int queueSize, Builder b) {
540601
ServerConnector connector;
541602

542603
final HttpConfiguration conf = new HttpConfiguration();
@@ -604,7 +665,7 @@ void setContextAttributes(Context ctx, Map<String, Object> contextAttrs) {
604665

605666
private void createWebServer(final Builder b) throws IOException {
606667
// Create the thread pool for the web server to handle HTTP requests
607-
QueuedThreadPool threadPool = new QueuedThreadPool();
668+
threadPool = new QueuedThreadPool();
608669
if (b.maxThreads > 0) {
609670
threadPool.setMaxThreads(b.maxThreads);
610671
}
@@ -615,59 +676,29 @@ private void createWebServer(final Builder b) throws IOException {
615676
this.appDir = getWebAppsPath(b.name);
616677
this.webAppContext = createWebAppContext(b);
617678

618-
if (b.useSPNEGO) {
619-
// Secure the web server with kerberos
620-
setupSpnegoFilter(b, webAppContext);
621-
}
622-
623-
if (b.enableCORS) {
624-
setupCORSFilter(b);
625-
}
626-
627-
Map<String, String> xFrameParams = setHeaders();
628-
if (b.xFrameEnabled) {
629-
setupXframeFilter(b,xFrameParams);
630-
}
631-
632-
if (b.disableDirListing) {
633-
disableDirectoryListingOnServlet(webAppContext);
634-
}
635-
636-
initializeWebServer(b, threadPool.getMaxThreads());
679+
initializeWebServer(b);
637680
}
638681

639-
private void initializeWebServer(final Builder b, int queueSize) throws IOException {
682+
private void initializeWebServer(final Builder b) throws IOException {
640683
// Set handling for low resource conditions.
641684
final LowResourceMonitor low = new LowResourceMonitor(webServer);
642685
low.setLowResourcesIdleTimeout(10000);
643686
webServer.addBean(low);
644687

645-
Connector connector = createChannelConnector(queueSize, b);
646-
webServer.addConnector(connector);
647-
648-
RewriteHandler rwHandler = new RewriteHandler();
649-
rwHandler.setRewriteRequestURI(true);
650-
rwHandler.setRewritePathInfo(false);
651-
652-
RewriteRegexRule rootRule = new RewriteRegexRule();
653-
rootRule.setRegex("^/$");
654-
rootRule.setReplacement(b.contextRootRewriteTarget);
655-
rootRule.setTerminating(true);
656-
657-
rwHandler.addRule(rootRule);
658-
rwHandler.setHandler(webAppContext);
659-
660-
// Configure web application contexts for the web server
661-
ContextHandlerCollection contexts = new ContextHandlerCollection();
662-
contexts.addHandler(rwHandler);
663-
webServer.setHandler(contexts);
664-
688+
// Configure the global context handler collection for the web server
689+
portHandlerWrapper = new PortHandlerWrapper();
690+
webServer.setHandler(portHandlerWrapper);
691+
692+
ContextHandlerCollection portHandler = new ContextHandlerCollection();
693+
portHandler.addHandler(webAppContext);
694+
ServerConnector connector = addWebApp(b, webAppContext, portHandler);
695+
696+
portHandlerWrapper.addHandler(connector, portHandler);
665697

666698
if (b.usePAM) {
667-
setupPam(b, contexts);
699+
setupPam(b, portHandlerWrapper);
668700
}
669701

670-
671702
addServlet("jmx", "/jmx", JMXJsonServlet.class);
672703
addServlet("conf", "/conf", ConfServlet.class);
673704
addServlet("stacks", "/stacks", StackServlet.class);
@@ -679,8 +710,7 @@ private void initializeWebServer(final Builder b, int queueSize) throws IOExcept
679710
if (Files.notExists(tmpDir)) {
680711
Files.createDirectories(tmpDir);
681712
}
682-
ServletContextHandler genCtx =
683-
new ServletContextHandler(contexts, "/prof-output");
713+
ServletContextHandler genCtx = new ServletContextHandler(portHandler, "/prof-output");
684714
setContextAttributes(genCtx.getServletContext(), b.contextAttrs);
685715
genCtx.addServlet(ProfileOutputServlet.class, "/*");
686716
genCtx.setResourceBase(tmpDir.toAbsolutePath().toString());
@@ -689,23 +719,17 @@ private void initializeWebServer(final Builder b, int queueSize) throws IOExcept
689719
LOG.info("ASYNC_PROFILER_HOME env or -Dasync.profiler.home not specified. Disabling /prof endpoint..");
690720
}
691721

692-
for (Pair<String, Class<? extends HttpServlet>> p : b.servlets) {
693-
addServlet(p.getFirst(), "/" + p.getFirst(), p.getSecond());
694-
}
695-
696722
b.globalFilters.forEach((k, v) -> addFilter(k, v.getFirst(), v.getSecond(), webAppContext.getServletHandler()));
697723

698-
ServletContextHandler staticCtx =
699-
new ServletContextHandler(contexts, "/static");
724+
ServletContextHandler staticCtx = new ServletContextHandler(portHandler, "/static");
700725
staticCtx.setResourceBase(appDir + "/static");
701726
staticCtx.addServlet(DefaultServlet.class, "/*");
702727
staticCtx.setDisplayName("static");
703728
disableDirectoryListingOnServlet(staticCtx);
704729

705730
String logDir = getLogDir(b.conf);
706731
if (logDir != null) {
707-
ServletContextHandler logCtx =
708-
new ServletContextHandler(contexts, "/logs");
732+
ServletContextHandler logCtx = new ServletContextHandler(portHandler, "/logs");
709733
setContextAttributes(logCtx.getServletContext(), b.contextAttrs);
710734
if(b.useSPNEGO) {
711735
setupSpnegoFilter(b,logCtx);
@@ -716,7 +740,7 @@ private void initializeWebServer(final Builder b, int queueSize) throws IOExcept
716740
}
717741

718742
// Define the global filers for each servlet context except the staticCtx(css style).
719-
Optional<Handler[]> handlers = Optional.ofNullable(contexts.getHandlers());
743+
Optional<Handler[]> handlers = Optional.ofNullable(portHandler.getHandlers());
720744
handlers.ifPresent(hs -> Arrays.stream(hs)
721745
.filter(h -> h instanceof ServletContextHandler && !"static".equals(((ServletContextHandler) h).getDisplayName()))
722746
.forEach(h -> b.globalFilters.forEach((k, v) ->
@@ -748,7 +772,7 @@ private Map<String, String> getDefaultHeaders() {
748772
return headers;
749773
}
750774

751-
private void setupXframeFilter(Builder b, Map<String, String> params) {
775+
private void setupXframeFilter(Builder b, Map<String, String> params, WebAppContext webAppContext) {
752776
FilterHolder holder = new FilterHolder();
753777
holder.setClassName(QuotingInputFilter.class.getName());
754778
holder.setInitParameters(params);
@@ -811,6 +835,15 @@ public void addServlet(String name, String pathSpec,
811835
webAppContext.addServlet(holder, pathSpec);
812836
}
813837

838+
private void addServlet(String name, String pathSpec, Class<? extends HttpServlet> clazz,
839+
WebAppContext webAppContext) {
840+
ServletHolder holder = new ServletHolder(clazz);
841+
if (name != null) {
842+
holder.setName(name);
843+
}
844+
webAppContext.addServlet(holder, pathSpec);
845+
}
846+
814847
public void addServlet(String name, String pathSpec, ServletHolder holder) {
815848
if (name != null) {
816849
holder.setName(name);
@@ -1026,4 +1059,32 @@ private void initHttpHeaderMap() {
10261059
}
10271060
}
10281061

1062+
static class PortHandlerWrapper extends ContextHandlerCollection {
1063+
1064+
private final Map<ServerConnector, HandlerCollection> connectorToHandlerMap = new HashMap<>();
1065+
1066+
public PortHandlerWrapper() {
1067+
}
1068+
1069+
public void addHandler(ServerConnector connector, HandlerCollection handler) {
1070+
connectorToHandlerMap.put(connector, handler);
1071+
addHandler(handler);
1072+
}
1073+
1074+
@Override
1075+
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
1076+
// Determine the connector (port) the request came through
1077+
int port = request.getServerPort();
1078+
1079+
// Find the handler for the corresponding port
1080+
Handler handler = connectorToHandlerMap.entrySet().stream()
1081+
.filter(entry -> entry.getKey().getPort() == port)
1082+
.map(Map.Entry::getValue)
1083+
.findFirst()
1084+
.orElseThrow(() -> new IllegalStateException("No handler found for port " + port));
1085+
1086+
// Delegate the request to the handler
1087+
handler.handle(target, baseRequest, request, response);
1088+
}
1089+
}
10291090
}

data/conf/hive-site.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@
358358

359359
<property>
360360
<name>hive.server2.webui.max.threads</name>
361-
<value>4</value>
361+
<value>6</value>
362362
</property>
363363

364364
<property>

data/conf/mr/hive-site.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@
354354

355355
<property>
356356
<name>hive.server2.webui.max.threads</name>
357-
<value>4</value>
357+
<value>6</value>
358358
</property>
359359

360360
<property>

0 commit comments

Comments
 (0)