You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kyuubi.apache.org by ch...@apache.org on 2021/08/06 17:54:03 UTC
[incubator-kyuubi] branch master updated: [KYUUBI #897] Support
stop Spark engine through web ui
This is an automated email from the ASF dual-hosted git repository.
chengpan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-kyuubi.git
The following commit(s) were added to refs/heads/master by this push:
new 065c98a [KYUUBI #897] Support stop Spark engine through web ui
065c98a is described below
commit 065c98a259f12ebccdf88900ef51329fe13acc22
Author: ulysses-you <ul...@gmail.com>
AuthorDate: Sat Aug 7 01:53:49 2021 +0800
[KYUUBI #897] Support stop Spark engine through web ui
<!--
Thanks for sending a pull request!
Here are some tips for you:
1. If this is your first time, please read our contributor guidelines: https://kyuubi.readthedocs.io/en/latest/community/contributions.html
2. If the PR is related to an issue in https://github.com/apache/incubator-kyuubi/issues, add '[KYUUBI #XXXX]' in your PR title, e.g., '[KYUUBI #XXXX] Your PR title ...'.
3. If the PR is unfinished, add '[WIP]' in your PR title, e.g., '[WIP][KYUUBI #XXXX] Your PR title ...'.
-->
### _Why are the changes needed?_
<!--
Please clarify why the changes are needed. For instance,
1. If you add a feature, you can talk about the use case of it.
2. If you fix a bug, you can clarify why it is a bug.
-->
Closes https://github.com/apache/incubator-kyuubi/issues/897
### _How was this patch tested?_
The following two images show the stop feature is enabled:
![image](https://user-images.githubusercontent.com/12025282/128304854-20f3619a-6cae-4975-837b-34858ba4ef37.png)
![image](https://user-images.githubusercontent.com/12025282/128304875-987fec0d-5242-4d09-acf5-c6af8aad0068.png)
The following image show the stop feature is disabled:
![image](https://user-images.githubusercontent.com/12025282/128304893-0547fb54-da58-4691-a0a6-38feabc3930d.png)
Closes #898 from ulysses-you/kyuubi-897.
Closes #897
61ad25c1 [ulysses-you] address comment
b63997ec [ulysses-you] maven
dbb36014 [ulysses-you] maven
3c91307c [ulysses-you] simplify
6e9e5bca [ulysses-you] pom
4e143657 [ulysses-you] config
e579e227 [ulysses-you] Support stop Spark engine through web ui
Authored-by: ulysses-you <ul...@gmail.com>
Signed-off-by: Cheng Pan <ch...@apache.org>
---
docs/deployment/settings.md | 1 +
.../org/apache/spark/kyuubi/ui/EnginePage.scala | 25 ++++++++++++--
.../org/apache/spark/kyuubi/ui/EngineTab.scala | 39 ++++++++++++++++++++--
.../org/apache/kyuubi/config/KyuubiConf.scala | 7 ++++
4 files changed, 68 insertions(+), 4 deletions(-)
diff --git a/docs/deployment/settings.md b/docs/deployment/settings.md
index 605418e..61c7f5d 100644
--- a/docs/deployment/settings.md
+++ b/docs/deployment/settings.md
@@ -153,6 +153,7 @@ kyuubi\.engine\.session<br>\.initialize\.sql|<div style='width: 65pt;word-wrap:
kyuubi\.engine\.share<br>\.level|<div style='width: 65pt;word-wrap: break-word;white-space: normal'>USER</div>|<div style='width: 170pt;word-wrap: break-word;white-space: normal'>Engines will be shared in different levels, available configs are: <ul> <li>CONNECTION: engine will not be shared but only used by the current client connection</li> <li>USER: engine will be shared by all sessions created by a unique username, see also kyuubi.engine.share.level.sub.domain</li> <li>SERVER: the Ap [...]
kyuubi\.engine\.share<br>\.level\.sub\.domain|<div style='width: 65pt;word-wrap: break-word;white-space: normal'><undefined></div>|<div style='width: 170pt;word-wrap: break-word;white-space: normal'>Allow end-users to create a sub-domain for the share level of an engine. A sub-domain is a case-insensitive string values in `^[a-zA-Z_]{1,10}$` form. For example, for `USER` share level, an end-user can share a certain engine within a sub-domain, not for all of its clients. End-users a [...]
kyuubi\.engine\.single<br>\.spark\.session|<div style='width: 65pt;word-wrap: break-word;white-space: normal'>false</div>|<div style='width: 170pt;word-wrap: break-word;white-space: normal'>When set to true, this engine is running in a single session mode. All the JDBC/ODBC connections share the temporary views, function registries, SQL configuration and the current database.</div>|<div style='width: 30pt'>boolean</div>|<div style='width: 20pt'>1.3.0</div>
+kyuubi\.engine\.ui\.stop<br>\.enabled|<div style='width: 65pt;word-wrap: break-word;white-space: normal'>true</div>|<div style='width: 170pt;word-wrap: break-word;white-space: normal'>When true, allows Kyuubi engine to be killed from the Spark Web UI.</div>|<div style='width: 30pt'>boolean</div>|<div style='width: 20pt'>1.3.0</div>
### Frontend
diff --git a/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/spark/kyuubi/ui/EnginePage.scala b/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/spark/kyuubi/ui/EnginePage.scala
index 1443d5e..f835ef7 100644
--- a/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/spark/kyuubi/ui/EnginePage.scala
+++ b/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/spark/kyuubi/ui/EnginePage.scala
@@ -27,8 +27,11 @@ import org.apache.spark.ui.UIUtils.formatDurationVerbose
case class EnginePage(parent: EngineTab) extends WebUIPage("") {
override def render(request: HttpServletRequest): Seq[Node] = {
- val content = generateBasicStats() ++
- <br/> ++
+ val content =
+ generateBasicStats() ++
+ <br/> ++
+ stop(request) ++
+ <br/> ++
<h4>
{parent.engine.backendService.sessionManager.getOpenSessionCount} session(s) are online,
running {parent.engine.backendService.sessionManager.operationManager.getOperationCount}
@@ -62,4 +65,22 @@ case class EnginePage(parent: EngineTab) extends WebUIPage("") {
</li>
</ul>
}
+
+ private def stop(request: HttpServletRequest): Seq[Node] = {
+ val basePath = UIUtils.prependBaseUri(request, parent.basePath)
+ if (parent.killEnabled) {
+ val confirm =
+ s"if (window.confirm('Are you sure you want to kill kyuubi engine ?')) " +
+ "{ this.parentNode.submit(); return true; } else { return false; }"
+ val stopLinkUri = s"$basePath/kyuubi/stop"
+ <ul class ="list-unstyled">
+ <li>
+ <strong>Stop kyuubi engine: </strong>
+ <a href={stopLinkUri} onclick={confirm} class="stop-link">(kill)</a>
+ </li>
+ </ul>
+ } else {
+ Seq.empty
+ }
+ }
}
diff --git a/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/spark/kyuubi/ui/EngineTab.scala b/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/spark/kyuubi/ui/EngineTab.scala
index a9d8de9..a183584 100644
--- a/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/spark/kyuubi/ui/EngineTab.scala
+++ b/externals/kyuubi-spark-sql-engine/src/main/scala/org/apache/spark/kyuubi/ui/EngineTab.scala
@@ -17,22 +17,57 @@
package org.apache.spark.kyuubi.ui
+import javax.servlet.http.HttpServletRequest
+
import org.apache.spark.ui.SparkUITab
+import scala.util.control.NonFatal
-import org.apache.kyuubi.Utils
+import org.apache.kyuubi.{Logging, Utils}
+import org.apache.kyuubi.config.KyuubiConf
import org.apache.kyuubi.engine.spark.SparkSQLEngine
+import org.apache.kyuubi.service.ServiceState
/**
* Note that [[SparkUITab]] is private for Spark
*/
case class EngineTab(engine: SparkSQLEngine)
- extends SparkUITab(engine.spark.sparkContext.ui.orNull, "kyuubi") {
+ extends SparkUITab(engine.spark.sparkContext.ui.orNull, "kyuubi") with Logging {
override val name: String = "Kyuubi Query Engine"
+ val killEnabled = engine.getConf.get(KyuubiConf.ENGINE_UI_STOP_ENABLED)
engine.spark.sparkContext.ui.foreach { ui =>
this.attachPage(EnginePage(this))
ui.attachTab(this)
Utils.addShutdownHook(() => ui.detachTab(this))
}
+
+ engine.spark.sparkContext.ui.foreach { ui =>
+ try {
+ // Spark shade the jetty package so here we use reflect
+ Class.forName("org.apache.spark.ui.SparkUI")
+ .getMethod("attachHandler",
+ classOf[org.sparkproject.jetty.servlet.ServletContextHandler])
+ .invoke(ui,
+ Class.forName("org.apache.spark.ui.JettyUtils")
+ .getMethod("createRedirectHandler",
+ classOf[String],
+ classOf[String],
+ classOf[(HttpServletRequest) => Unit],
+ classOf[String],
+ classOf[scala.collection.immutable.Set[String]])
+ .invoke(null, "/kyuubi/stop", "/kyuubi", handleKillRequest _, "", Set("GET", "POST"))
+ )
+ } catch {
+ case NonFatal(e) =>
+ warn("Failed to attach handler using SparkUI, please check the Spark version. " +
+ s"So the config '${KyuubiConf.ENGINE_UI_STOP_ENABLED.key}' does not work.", e)
+ }
+ }
+
+ def handleKillRequest(request: HttpServletRequest): Unit = {
+ if (killEnabled && engine != null && engine.getServiceState != ServiceState.STOPPED) {
+ engine.stop()
+ }
+ }
}
diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala
index 95f6afc..aa11fcf 100644
--- a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala
+++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala
@@ -702,4 +702,11 @@ object KyuubiConf {
.checkValue(_.toSet.subsetOf(Set("SPARK", "JSON", "JDBC", "CUSTOM")),
"Unsupported event loggers")
.createWithDefault(Nil)
+
+ val ENGINE_UI_STOP_ENABLED: ConfigEntry[Boolean] =
+ buildConf("engine.ui.stop.enabled")
+ .doc("When true, allows Kyuubi engine to be killed from the Spark Web UI.")
+ .version("1.3.0")
+ .booleanConf
+ .createWithDefault(true)
}