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