Skip to content

Commit 8746ec3

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

File tree

10 files changed

+380
-149
lines changed

10 files changed

+380
-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

+123-61
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,17 @@
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;
91+
import org.eclipse.jetty.server.handler.AbstractHandler;
9192
import org.eclipse.jetty.server.handler.ContextHandler.Context;
9293
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
9394
import org.eclipse.jetty.server.ServerConnector;
95+
import org.eclipse.jetty.server.handler.HandlerCollection;
9496
import org.eclipse.jetty.servlet.DefaultServlet;
9597
import org.eclipse.jetty.servlet.FilterHolder;
9698
import org.eclipse.jetty.servlet.FilterMapping;
@@ -138,6 +140,8 @@ public class HttpServer {
138140
private String appDir;
139141
private WebAppContext webAppContext;
140142
private Server webServer;
143+
private QueuedThreadPool threadPool;
144+
private PortHandlerWrapper portHandlerWrapper;
141145

142146
/**
143147
* Create a status server on the given port.
@@ -179,6 +183,7 @@ public static class Builder {
179183
new LinkedList<Pair<String, Class<? extends HttpServlet>>>();
180184
private boolean disableDirListing = false;
181185
private final Map<String, Pair<String, Filter>> globalFilters = new LinkedHashMap<>();
186+
private String contextPath;
182187

183188
public Builder(String name) {
184189
Preconditions.checkArgument(name != null && !name.isEmpty(), "Name must be specified");
@@ -189,6 +194,10 @@ public HttpServer build() throws IOException {
189194
return new HttpServer(this);
190195
}
191196

197+
public void setContextPath(String contextPath) {
198+
this.contextPath = contextPath;
199+
}
200+
192201
public Builder setConf(HiveConf origConf) {
193202
this.conf = new HiveConf(origConf);
194203
origConf.stripHiddenConfigurations(conf);
@@ -489,13 +498,13 @@ static boolean userHasAdministratorAccess(ServletContext servletContext,
489498
/**
490499
* Create the web context for the application of specified name
491500
*/
492-
WebAppContext createWebAppContext(Builder b) {
501+
WebAppContext createWebAppContext(Builder b) throws FileNotFoundException {
493502
WebAppContext ctx = new WebAppContext();
494503
setContextAttributes(ctx.getServletContext(), b.contextAttrs);
495504
ctx.getServletContext().getSessionCookieConfig().setHttpOnly(true);
496505
ctx.setDisplayName(b.name);
497-
ctx.setContextPath("/");
498-
ctx.setWar(appDir + "/" + b.name);
506+
ctx.setContextPath(b.contextPath == null ? "/" : b.contextPath);
507+
ctx.setWar(getWebAppsPath(b.name) + "/" + b.name);
499508
return ctx;
500509
}
501510

@@ -519,8 +528,9 @@ void setupSpnegoFilter(Builder b, ServletContextHandler ctx) throws IOException
519528
/**
520529
* Setup cross-origin requests (CORS) filter.
521530
* @param b - builder
531+
* @param webAppContext - webAppContext
522532
*/
523-
private void setupCORSFilter(Builder b) {
533+
private void setupCORSFilter(Builder b, WebAppContext webAppContext) {
524534
FilterHolder holder = new FilterHolder();
525535
holder.setClassName(CrossOriginFilter.class.getName());
526536
Map<String, String> params = new HashMap<>();
@@ -533,10 +543,62 @@ private void setupCORSFilter(Builder b) {
533543
handler.addFilterWithMapping(holder, "/*", FilterMapping.ALL);
534544
}
535545

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

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

605667
private void createWebServer(final Builder b) throws IOException {
606668
// Create the thread pool for the web server to handle HTTP requests
607-
QueuedThreadPool threadPool = new QueuedThreadPool();
669+
threadPool = new QueuedThreadPool();
608670
if (b.maxThreads > 0) {
609671
threadPool.setMaxThreads(b.maxThreads);
610672
}
@@ -615,59 +677,29 @@ private void createWebServer(final Builder b) throws IOException {
615677
this.appDir = getWebAppsPath(b.name);
616678
this.webAppContext = createWebAppContext(b);
617679

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());
680+
initializeWebServer(b);
637681
}
638682

639-
private void initializeWebServer(final Builder b, int queueSize) throws IOException {
683+
private void initializeWebServer(final Builder b) throws IOException {
640684
// Set handling for low resource conditions.
641685
final LowResourceMonitor low = new LowResourceMonitor(webServer);
642686
low.setLowResourcesIdleTimeout(10000);
643687
webServer.addBean(low);
644688

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-
689+
// Configure the global context handler collection for the web server
690+
portHandlerWrapper = new PortHandlerWrapper();
691+
webServer.setHandler(portHandlerWrapper);
692+
693+
ContextHandlerCollection portHandler = new ContextHandlerCollection();
694+
portHandler.addHandler(webAppContext);
695+
ServerConnector connector = addWebApp(b, webAppContext, portHandler);
696+
697+
portHandlerWrapper.addHandler(connector, portHandler);
665698

666699
if (b.usePAM) {
667-
setupPam(b, contexts);
700+
setupPam(b, portHandlerWrapper);
668701
}
669702

670-
671703
addServlet("jmx", "/jmx", JMXJsonServlet.class);
672704
addServlet("conf", "/conf", ConfServlet.class);
673705
addServlet("stacks", "/stacks", StackServlet.class);
@@ -679,8 +711,7 @@ private void initializeWebServer(final Builder b, int queueSize) throws IOExcept
679711
if (Files.notExists(tmpDir)) {
680712
Files.createDirectories(tmpDir);
681713
}
682-
ServletContextHandler genCtx =
683-
new ServletContextHandler(contexts, "/prof-output");
714+
ServletContextHandler genCtx = new ServletContextHandler(portHandler, "/prof-output");
684715
setContextAttributes(genCtx.getServletContext(), b.contextAttrs);
685716
genCtx.addServlet(ProfileOutputServlet.class, "/*");
686717
genCtx.setResourceBase(tmpDir.toAbsolutePath().toString());
@@ -689,23 +720,17 @@ private void initializeWebServer(final Builder b, int queueSize) throws IOExcept
689720
LOG.info("ASYNC_PROFILER_HOME env or -Dasync.profiler.home not specified. Disabling /prof endpoint..");
690721
}
691722

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

698-
ServletContextHandler staticCtx =
699-
new ServletContextHandler(contexts, "/static");
725+
ServletContextHandler staticCtx = new ServletContextHandler(portHandler, "/static");
700726
staticCtx.setResourceBase(appDir + "/static");
701727
staticCtx.addServlet(DefaultServlet.class, "/*");
702728
staticCtx.setDisplayName("static");
703729
disableDirectoryListingOnServlet(staticCtx);
704730

705731
String logDir = getLogDir(b.conf);
706732
if (logDir != null) {
707-
ServletContextHandler logCtx =
708-
new ServletContextHandler(contexts, "/logs");
733+
ServletContextHandler logCtx = new ServletContextHandler(portHandler, "/logs");
709734
setContextAttributes(logCtx.getServletContext(), b.contextAttrs);
710735
if(b.useSPNEGO) {
711736
setupSpnegoFilter(b,logCtx);
@@ -716,7 +741,7 @@ private void initializeWebServer(final Builder b, int queueSize) throws IOExcept
716741
}
717742

718743
// Define the global filers for each servlet context except the staticCtx(css style).
719-
Optional<Handler[]> handlers = Optional.ofNullable(contexts.getHandlers());
744+
Optional<Handler[]> handlers = Optional.ofNullable(portHandler.getHandlers());
720745
handlers.ifPresent(hs -> Arrays.stream(hs)
721746
.filter(h -> h instanceof ServletContextHandler && !"static".equals(((ServletContextHandler) h).getDisplayName()))
722747
.forEach(h -> b.globalFilters.forEach((k, v) ->
@@ -748,7 +773,7 @@ private Map<String, String> getDefaultHeaders() {
748773
return headers;
749774
}
750775

751-
private void setupXframeFilter(Builder b, Map<String, String> params) {
776+
private void setupXframeFilter(Builder b, Map<String, String> params, WebAppContext webAppContext) {
752777
FilterHolder holder = new FilterHolder();
753778
holder.setClassName(QuotingInputFilter.class.getName());
754779
holder.setInitParameters(params);
@@ -811,6 +836,15 @@ public void addServlet(String name, String pathSpec,
811836
webAppContext.addServlet(holder, pathSpec);
812837
}
813838

839+
private void addServlet(String name, String pathSpec, Class<? extends HttpServlet> clazz,
840+
WebAppContext webAppContext) {
841+
ServletHolder holder = new ServletHolder(clazz);
842+
if (name != null) {
843+
holder.setName(name);
844+
}
845+
webAppContext.addServlet(holder, pathSpec);
846+
}
847+
814848
public void addServlet(String name, String pathSpec, ServletHolder holder) {
815849
if (name != null) {
816850
holder.setName(name);
@@ -1026,4 +1060,32 @@ private void initHttpHeaderMap() {
10261060
}
10271061
}
10281062

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

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)