From 51e02d0539786b3888832c16a4248bc2ded44513 Mon Sep 17 00:00:00 2001
From: yl09099 <33595968+yl09099@users.noreply.github.com>
Date: Thu, 23 Nov 2023 09:51:27 +0800
Subject: [PATCH] [#960][part-3] feat(dashboard): Provides a start-stop script
for the dashboard. (#1056)
### What changes were proposed in this pull request?
Provides a start-stop script for the dashboard.
### Why are the changes needed?
Fix: https://github.com/apache/incubator-uniffle/issues/960
### Does this PR introduce any user-facing change?
Start and close the dashboard module using shell scripts.
### How was this patch tested?
UT.
---
bin/start-dashboard.sh | 86 +++++++++++++++++++
bin/stop-dashboard.sh | 28 ++++++
build_distribution.sh | 7 ++
common/pom.xml | 4 +
.../dashboard/web/JettyServerFront.java | 28 +++++-
.../dashboard/web/config/DashboardConf.java | 6 ++
.../dashboard/web/proxy/WebProxyServlet.java | 79 +++++++++++++++++
dashboard/src/main/webapp/src/api/api.js | 4 +-
.../webapp/src/components/ApplicationPage.vue | 1 -
.../src/components/CoordinatorServerPage.vue | 6 +-
dashboard/src/main/webapp/vue.config.js | 8 --
docs/dashboard_guide.md | 42 +++++++++
pom.xml | 5 ++
13 files changed, 286 insertions(+), 18 deletions(-)
create mode 100644 bin/start-dashboard.sh
create mode 100644 bin/stop-dashboard.sh
create mode 100644 dashboard/src/main/java/org/apache/uniffle/dashboard/web/proxy/WebProxyServlet.java
create mode 100644 docs/dashboard_guide.md
diff --git a/bin/start-dashboard.sh b/bin/start-dashboard.sh
new file mode 100644
index 0000000000..9a52515003
--- /dev/null
+++ b/bin/start-dashboard.sh
@@ -0,0 +1,86 @@
+#!/usr/bin/env bash
+
+#
+# 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.
+#
+
+set -o pipefail
+set -o nounset # exit the script if you try to use an uninitialised variable
+set -o errexit # exit the script if any statement returns a non-true return value
+
+source "$(dirname "$0")/utils.sh"
+load_rss_env
+
+cd "$RSS_HOME"
+
+COORDINATOR_CONF_FILE="${RSS_CONF_DIR}/coordinator.conf"
+JAR_DIR="${RSS_HOME}/jars"
+LOG_CONF_FILE="${RSS_CONF_DIR}/log4j.properties"
+LOG_PATH="${RSS_LOG_DIR}/dashboard.log"
+OUT_PATH="${RSS_LOG_DIR}/dashboard.out"
+
+MAIN_CLASS="org.apache.uniffle.dashboard.web.JettyServerFront"
+
+HADOOP_DEPENDENCY="$("$HADOOP_HOME/bin/hadoop" classpath --glob)"
+
+echo "Check process existence"
+is_jvm_process_running "$JPS" $MAIN_CLASS
+
+CLASSPATH=""
+
+for file in $(ls ${JAR_DIR}/dashboard/*.jar 2>/dev/null); do
+ CLASSPATH=$CLASSPATH:$file
+done
+
+mkdir -p "${RSS_LOG_DIR}"
+mkdir -p "${RSS_PID_DIR}"
+
+CLASSPATH=$CLASSPATH:$HADOOP_CONF_DIR:$HADOOP_DEPENDENCY
+JAVA_LIB_PATH="-Djava.library.path=$HADOOP_HOME/lib/native"
+
+echo "class path is $CLASSPATH"
+
+JVM_ARGS=" -server \
+ -Xmx${XMX_SIZE:-8g} \
+ -Xms${XMX_SIZE:-8g} \
+ -XX:+UseG1GC \
+ -XX:MaxGCPauseMillis=200 \
+ -XX:ParallelGCThreads=20 \
+ -XX:ConcGCThreads=5 \
+ -XX:InitiatingHeapOccupancyPercent=45 \
+ -XX:+PrintGC \
+ -XX:+PrintAdaptiveSizePolicy \
+ -XX:+PrintGCDateStamps \
+ -XX:+PrintGCTimeStamps \
+ -XX:+PrintGCDetails \
+ -Xloggc:${RSS_LOG_DIR}/gc-%t.log"
+
+JAVA11_EXTRA_ARGS=" -XX:+IgnoreUnrecognizedVMOptions \
+ -Xlog:gc:tags,time,uptime,level"
+
+ARGS=""
+
+if [ -f ${LOG_CONF_FILE} ]; then
+ ARGS="$ARGS -Dlog4j.configuration=file:${LOG_CONF_FILE} -Dlog.path=${LOG_PATH}"
+else
+ echo "Exit with error: ${LOG_CONF_FILE} file doesn't exist."
+ exit 1
+fi
+
+$RUNNER $ARGS $JVM_ARGS $JAVA11_EXTRA_ARGS -cp $CLASSPATH $MAIN_CLASS --conf "$COORDINATOR_CONF_FILE" $@ &> $OUT_PATH &
+
+get_pid_file_name uniffle-dashboard
+echo $! >${RSS_PID_DIR}/${pid_file}
diff --git a/bin/stop-dashboard.sh b/bin/stop-dashboard.sh
new file mode 100644
index 0000000000..289aebae21
--- /dev/null
+++ b/bin/stop-dashboard.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+#
+# 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.
+#
+#
+
+set -o pipefail
+set -o nounset # exit the script if you try to use an uninitialised variable
+set -o errexit # exit the script if any statement returns a non-true return value
+
+source "$(dirname "$0")/utils.sh"
+load_rss_env
+
+common_shutdown "uniffle-dashboard" "${RSS_PID_DIR}"
diff --git a/build_distribution.sh b/build_distribution.sh
index f3c413e785..62ecc83aa1 100755
--- a/build_distribution.sh
+++ b/build_distribution.sh
@@ -169,6 +169,13 @@ echo "copy $COORDINATOR_JAR to ${COORDINATOR_JAR_DIR}"
cp $COORDINATOR_JAR ${COORDINATOR_JAR_DIR}
cp "${RSS_HOME}"/coordinator/target/jars/* ${COORDINATOR_JAR_DIR}
+DASHBOARD_JAR_DIR="${DISTDIR}/jars/dashboard"
+mkdir -p $DASHBOARD_JAR_DIR
+DASHBOARD_JAR="${RSS_HOME}/dashboard/target/dashboard-${VERSION}.jar"
+echo "copy $DASHBOARD_JAR to ${DASHBOARD_JAR_DIR}"
+cp $DASHBOARD_JAR $DASHBOARD_JAR_DIR
+cp "${RSS_HOME}"/dashboard/target/jars/* ${DASHBOARD_JAR_DIR}
+
CLI_JAR_DIR="${DISTDIR}/jars/cli"
mkdir -p $CLI_JAR_DIR
CLI_JAR="${RSS_HOME}/cli/target/cli-${VERSION}.jar"
diff --git a/common/pom.xml b/common/pom.xml
index 2ad3bfc4d4..9f9d11d125 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -145,6 +145,10 @@
org.eclipse.jetty
jetty-util
+
+ org.eclipse.jetty
+ jetty-proxy
+
org.apache.hbase.thirdparty
hbase-shaded-jersey
diff --git a/dashboard/src/main/java/org/apache/uniffle/dashboard/web/JettyServerFront.java b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/JettyServerFront.java
index 7518f95913..65e1fd46dd 100644
--- a/dashboard/src/main/java/org/apache/uniffle/dashboard/web/JettyServerFront.java
+++ b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/JettyServerFront.java
@@ -22,12 +22,16 @@
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import org.eclipse.jetty.proxy.ProxyServlet;
+import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.handler.ContextHandler;
+import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.ResourceHandler;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.thread.ExecutorThreadPool;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
@@ -40,6 +44,7 @@
import org.apache.uniffle.common.util.ExitUtils;
import org.apache.uniffle.common.util.ThreadUtils;
import org.apache.uniffle.dashboard.web.config.DashboardConf;
+import org.apache.uniffle.dashboard.web.proxy.WebProxyServlet;
public class JettyServerFront {
@@ -83,14 +88,29 @@ private void initialization() {
}
private void setRootServletHandler() {
- final ContextHandler contextHandler = new ContextHandler("/");
+ HandlerList handlers = new HandlerList();
+ ResourceHandler resourceHandler = addResourceHandler();
+ String coordinatorWebAddress = conf.getString(DashboardConf.COORDINATOR_WEB_ADDRESS);
+ ServletContextHandler servletContextHandler = addProxyHandler(coordinatorWebAddress);
+ handlers.setHandlers(new Handler[] {resourceHandler, servletContextHandler});
+ server.setHandler(handlers);
+ }
+
+ private static ResourceHandler addResourceHandler() {
ResourceHandler resourceHandler = new ResourceHandler();
resourceHandler.setDirectoriesListed(true);
resourceHandler.setBaseResource(
Resource.newResource(JettyServerFront.class.getClassLoader().getResource("static")));
resourceHandler.setWelcomeFiles(new String[] {"index.html"});
- contextHandler.setHandler(resourceHandler);
- server.setHandler(contextHandler);
+ return resourceHandler;
+ }
+
+ private static ServletContextHandler addProxyHandler(String coordinatorWebAddress) {
+ ProxyServlet proxyServlet = new WebProxyServlet(coordinatorWebAddress);
+ ServletHolder holder = new ServletHolder(proxyServlet);
+ ServletContextHandler contextHandler = new ServletContextHandler();
+ contextHandler.addServlet(holder, "/api/*");
+ return contextHandler;
}
private void addHttpConnector(int port, HttpConfiguration httpConfig, long idleTimeout) {
diff --git a/dashboard/src/main/java/org/apache/uniffle/dashboard/web/config/DashboardConf.java b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/config/DashboardConf.java
index f2a6cccf01..8d1fbbb329 100644
--- a/dashboard/src/main/java/org/apache/uniffle/dashboard/web/config/DashboardConf.java
+++ b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/config/DashboardConf.java
@@ -30,6 +30,12 @@ public class DashboardConf extends RssBaseConf {
.defaultValue(19988)
.withDescription("http server http port");
+ public static final ConfigOption COORDINATOR_WEB_ADDRESS =
+ ConfigOptions.key("coordinator.web.address")
+ .stringType()
+ .noDefaultValue()
+ .withDescription("Coordinator jetty port request address");
+
public static final ConfigOption DASHBOARD_STOP_TIMEOUT =
ConfigOptions.key("rss.dashboard.stop.timeout")
.longType()
diff --git a/dashboard/src/main/java/org/apache/uniffle/dashboard/web/proxy/WebProxyServlet.java b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/proxy/WebProxyServlet.java
new file mode 100644
index 0000000000..2877cc6efb
--- /dev/null
+++ b/dashboard/src/main/java/org/apache/uniffle/dashboard/web/proxy/WebProxyServlet.java
@@ -0,0 +1,79 @@
+/*
+ * 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.uniffle.dashboard.web.proxy;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.proxy.ProxyServlet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class WebProxyServlet extends ProxyServlet {
+
+ private String targetAddress;
+
+ private static final Logger LOG = LoggerFactory.getLogger(WebProxyServlet.class);
+
+ public WebProxyServlet(String targetAddress) {
+ this.targetAddress = targetAddress;
+ }
+
+ @Override
+ protected String rewriteTarget(HttpServletRequest clientRequest) {
+ if (!validateDestination(clientRequest.getServerName(), clientRequest.getServerPort())) {
+ return null;
+ }
+ StringBuilder target = new StringBuilder();
+
+ if (targetAddress.endsWith("/")) {
+ targetAddress = targetAddress.substring(0, targetAddress.length() - 1);
+ }
+ target.append(targetAddress).append("/api").append(clientRequest.getPathInfo());
+ String query = clientRequest.getQueryString();
+ if (query != null) {
+ target.append("?").append(query);
+ }
+ return target.toString();
+ }
+
+ @Override
+ protected void onProxyRewriteFailed(
+ HttpServletRequest clientRequest, HttpServletResponse clientResponse) {}
+
+ @Override
+ protected void onProxyResponseFailure(
+ HttpServletRequest clientRequest,
+ HttpServletResponse proxyResponse,
+ Response serverResponse,
+ Throwable failure) {}
+
+ @Override
+ protected String filterServerResponseHeader(
+ HttpServletRequest clientRequest,
+ Response serverResponse,
+ String headerName,
+ String headerValue) {
+ return null;
+ }
+
+ @Override
+ protected void addXForwardedHeaders(HttpServletRequest clientRequest, Request proxyRequest) {}
+}
diff --git a/dashboard/src/main/webapp/src/api/api.js b/dashboard/src/main/webapp/src/api/api.js
index c346d1d2e1..d70e24b2b4 100644
--- a/dashboard/src/main/webapp/src/api/api.js
+++ b/dashboard/src/main/webapp/src/api/api.js
@@ -69,10 +69,10 @@ export function getAppTotal(params){
// Create an interface for the app basic information list
export function getApplicationInfoList(params){
- return http.get('/app/appinfos', params,{})
+ return http.get('/app/appInfos', params,{})
}
// Create an interface for the number of apps for a user
export function getTotalForUser(params){
- return http.get('/app/usertotal', params,{})
+ return http.get('/app/userTotal', params,{})
}
diff --git a/dashboard/src/main/webapp/src/components/ApplicationPage.vue b/dashboard/src/main/webapp/src/components/ApplicationPage.vue
index 90f1762189..8d96f62549 100644
--- a/dashboard/src/main/webapp/src/components/ApplicationPage.vue
+++ b/dashboard/src/main/webapp/src/components/ApplicationPage.vue
@@ -73,7 +73,6 @@ export default {
async function getTotalForUserPage() {
const res = await getTotalForUser();
- console.log(res)
pageData.userAppCount = res.data.data
}
diff --git a/dashboard/src/main/webapp/src/components/CoordinatorServerPage.vue b/dashboard/src/main/webapp/src/components/CoordinatorServerPage.vue
index 76d9e657a8..ca30d47bb5 100644
--- a/dashboard/src/main/webapp/src/components/CoordinatorServerPage.vue
+++ b/dashboard/src/main/webapp/src/components/CoordinatorServerPage.vue
@@ -73,7 +73,7 @@
-
+
@@ -96,11 +96,11 @@ export default {
);
async function getCoordinatorServerConfPage() {
const res = await getCoordinatorConf();
- pageData.serverInfo = res.data.data
+ pageData.tableData = res.data.data
}
async function getCoorServerInfo() {
const res = await getCoordinatorServerInfo();
- pageData.tableData = res.data.data
+ pageData.serverInfo = res.data.data
}
onMounted(() => {
getCoordinatorServerConfPage();
diff --git a/dashboard/src/main/webapp/vue.config.js b/dashboard/src/main/webapp/vue.config.js
index 64ab67d573..a007fc8bad 100644
--- a/dashboard/src/main/webapp/vue.config.js
+++ b/dashboard/src/main/webapp/vue.config.js
@@ -16,12 +16,4 @@
*/
module.exports ={
- devServer:{
- proxy:{
- '/api':{
- target:'http://localhost:9528',
- changeOrigin:true
- }
- }
- }
}
diff --git a/docs/dashboard_guide.md b/docs/dashboard_guide.md
new file mode 100644
index 0000000000..34d48e7df7
--- /dev/null
+++ b/docs/dashboard_guide.md
@@ -0,0 +1,42 @@
+---
+layout: page
+displayTitle: Dashboard Guide
+title: Dashboard Guide
+description: Dashboard Guide
+license: |
+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.
+---
+# Dashboard Guide
+
+## Summary
+This document explains how to install and start Uniffle's dashboard.
+
+### Configure related parameters
+In $RSS_HOME/conf directory, configure the dashboard data request port and front-end access port in the coordinator
+``` shell
+## Front-end access port
+rss.dashboard.http.port 19997
+## The dashboard request data port, which is the Coordinator's HTTP port
+## coordinator.hostname is the hostname or IP address of a Coordinator
+coordinator.web.address http://coordinator.hostname:19998/
+```
+
+### Start the dashboard process
+In the $RSS_HOME/bin directory, start with a script.
+``` shell
+## Start dashboard
+sh start-dashboard.sh
+
+## Close dashboard
+sh stop-dashboard.sh
diff --git a/pom.xml b/pom.xml
index 944fc575e3..87806e5657 100644
--- a/pom.xml
+++ b/pom.xml
@@ -711,6 +711,11 @@
jetty-servlet
${jetty.version}
+
+ org.eclipse.jetty
+ jetty-proxy
+ ${jetty.version}
+
org.eclipse.jetty
jetty-server