This is an automated email from the ASF dual-hosted git repository.
roryqi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-uniffle.git
The following commit(s) were added to refs/heads/master by this push:
new 2c670d5ae [#1924] feat(dashboard): Show Thread Dump, Conf and Metrics
in DashBoard (#1927)
2c670d5ae is described below
commit 2c670d5ae615648d5dc7539cf27c00461f6b78dc
Author: kqhzz <[email protected]>
AuthorDate: Mon Jul 22 10:08:10 2024 +0800
[#1924] feat(dashboard): Show Thread Dump, Conf and Metrics in DashBoard
(#1927)
### What changes were proposed in this pull request?
Add jetty_port in message ShuffleServerId.
Modify dashboard, add some link in dashboard
<img width="1086" alt="企业微信截图_ede78628-56ac-4b7e-86ab-84b224da7ce4"
src="https://github.com/user-attachments/assets/a263871f-0a9a-4b1d-9ba6-0341708e0a2d">
<img width="1775" alt="企业微信截图_88b2c855-68e4-4054-94a3-d730f074a4b9"
src="https://github.com/user-attachments/assets/480352d6-bf29-48bc-af73-a41fa1fd8248">
### Why are the changes needed?
Enhance dashboard capabilities
Fix: #1924
### Does this PR introduce _any_ user-facing change?
No.
---
.../apache/uniffle/common/util/ThreadUtils.java | 18 +++++
.../org/apache/uniffle/common/web/JettyServer.java | 4 ++
.../coordinator/CoordinatorGrpcService.java | 3 +-
.../org/apache/uniffle/coordinator/ServerNode.java | 40 +++++++++++
.../web/resource/CoordinatorServerResource.java | 21 ++++++
.../dashboard/web/proxy/WebProxyServlet.java | 14 ++--
dashboard/src/main/webapp/src/api/api.js | 78 ++++++++++++++++++++
.../webapp/src/pages/CoordinatorServerPage.vue | 35 ++++++++-
.../webapp/src/pages/serverstatus/NodeListPage.vue | 54 +++++++++++++-
dashboard/src/main/webapp/src/utils/http.js | 7 +-
.../client/impl/grpc/CoordinatorGrpcClient.java | 7 +-
.../client/request/RssSendHeartBeatRequest.java | 9 ++-
proto/src/main/proto/Rss.proto | 1 +
.../apache/uniffle/server/RegisterHeartBeat.java | 9 ++-
.../org/apache/uniffle/server/ShuffleServer.java | 11 ++-
.../server/web/resource/ServerResource.java | 82 ++++++++++++++++++++++
.../apache/uniffle/server/web/vo/ServerConfVO.java | 44 ++++++++++++
17 files changed, 415 insertions(+), 22 deletions(-)
diff --git
a/common/src/main/java/org/apache/uniffle/common/util/ThreadUtils.java
b/common/src/main/java/org/apache/uniffle/common/util/ThreadUtils.java
index eb2003995..d84e6dc23 100644
--- a/common/src/main/java/org/apache/uniffle/common/util/ThreadUtils.java
+++ b/common/src/main/java/org/apache/uniffle/common/util/ThreadUtils.java
@@ -17,6 +17,9 @@
package org.apache.uniffle.common.util;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -39,6 +42,7 @@ import org.slf4j.LoggerFactory;
public class ThreadUtils {
private static final Logger LOGGER =
LoggerFactory.getLogger(ThreadUtils.class);
+ private static final ThreadMXBean THREAD_BEAN =
ManagementFactory.getThreadMXBean();
/** Provide a general method to create a thread factory to make the code
more standardized */
public static ThreadFactory getThreadFactory(String factoryName) {
@@ -183,4 +187,18 @@ public class ThreadUtils {
String taskMsg) {
return executeTasks(executorService, items, task, timeoutMs, taskMsg,
future -> null);
}
+
+ public static synchronized void printThreadInfo(StringBuilder builder,
String title) {
+ builder.append("Process Thread Dump: " + title + "\n");
+ builder.append(THREAD_BEAN.getThreadCount() + " active threads\n");
+ long[] threadIds = THREAD_BEAN.getAllThreadIds();
+ for (long id : threadIds) {
+ ThreadInfo info = THREAD_BEAN.getThreadInfo(id, Integer.MAX_VALUE);
+ if (info == null) {
+ // The thread is no longer active, ignore
+ continue;
+ }
+ builder.append(info + "\n");
+ }
+ }
}
diff --git
a/common/src/main/java/org/apache/uniffle/common/web/JettyServer.java
b/common/src/main/java/org/apache/uniffle/common/web/JettyServer.java
index 87e52cbc6..0ace09414 100644
--- a/common/src/main/java/org/apache/uniffle/common/web/JettyServer.java
+++ b/common/src/main/java/org/apache/uniffle/common/web/JettyServer.java
@@ -175,4 +175,8 @@ public class JettyServer {
public void stop() throws Exception {
server.stop();
}
+
+ public int getHttpPort() {
+ return httpPort;
+ }
}
diff --git
a/coordinator/src/main/java/org/apache/uniffle/coordinator/CoordinatorGrpcService.java
b/coordinator/src/main/java/org/apache/uniffle/coordinator/CoordinatorGrpcService.java
index ddca8a1e3..b3df63db5 100644
---
a/coordinator/src/main/java/org/apache/uniffle/coordinator/CoordinatorGrpcService.java
+++
b/coordinator/src/main/java/org/apache/uniffle/coordinator/CoordinatorGrpcService.java
@@ -433,6 +433,7 @@ public class CoordinatorGrpcService extends
CoordinatorServerGrpc.CoordinatorSer
Sets.newHashSet(request.getTagsList()),
serverStatus,
StorageInfoUtils.fromProto(request.getStorageInfoMap()),
- request.getServerId().getNettyPort());
+ request.getServerId().getNettyPort(),
+ request.getServerId().getJettyPort());
}
}
diff --git
a/coordinator/src/main/java/org/apache/uniffle/coordinator/ServerNode.java
b/coordinator/src/main/java/org/apache/uniffle/coordinator/ServerNode.java
index ec33d262a..a9723e00b 100644
--- a/coordinator/src/main/java/org/apache/uniffle/coordinator/ServerNode.java
+++ b/coordinator/src/main/java/org/apache/uniffle/coordinator/ServerNode.java
@@ -42,6 +42,7 @@ public class ServerNode implements Comparable<ServerNode> {
private ServerStatus status;
private Map<String, StorageInfo> storageInfo;
private int nettyPort = -1;
+ private int jettyPort = -1;
public ServerNode(String id) {
this(id, "", 0, 0, 0, 0, 0, Sets.newHashSet(), ServerStatus.EXCLUDED);
@@ -115,6 +116,7 @@ public class ServerNode implements Comparable<ServerNode> {
tags,
status,
storageInfoMap,
+ -1,
-1);
}
@@ -130,6 +132,34 @@ public class ServerNode implements Comparable<ServerNode> {
ServerStatus status,
Map<String, StorageInfo> storageInfoMap,
int nettyPort) {
+ this(
+ id,
+ ip,
+ grpcPort,
+ usedMemory,
+ preAllocatedMemory,
+ availableMemory,
+ eventNumInFlush,
+ tags,
+ status,
+ storageInfoMap,
+ nettyPort,
+ -1);
+ }
+
+ public ServerNode(
+ String id,
+ String ip,
+ int grpcPort,
+ long usedMemory,
+ long preAllocatedMemory,
+ long availableMemory,
+ int eventNumInFlush,
+ Set<String> tags,
+ ServerStatus status,
+ Map<String, StorageInfo> storageInfoMap,
+ int nettyPort,
+ int jettyPort) {
this.id = id;
this.ip = ip;
this.grpcPort = grpcPort;
@@ -145,6 +175,9 @@ public class ServerNode implements Comparable<ServerNode> {
if (nettyPort > 0) {
this.nettyPort = nettyPort;
}
+ if (jettyPort > 0) {
+ this.jettyPort = jettyPort;
+ }
}
public ShuffleServerId convertToGrpcProto() {
@@ -153,6 +186,7 @@ public class ServerNode implements Comparable<ServerNode> {
.setIp(ip)
.setPort(grpcPort)
.setNettyPort(nettyPort)
+ .setJettyPort(jettyPort)
.build();
}
@@ -214,6 +248,8 @@ public class ServerNode implements Comparable<ServerNode> {
+ grpcPort
+ "], netty port["
+ nettyPort
+ + "], jettyPort["
+ + jettyPort
+ "], usedMemory["
+ usedMemory
+ "], preAllocatedMemory["
@@ -277,4 +313,8 @@ public class ServerNode implements Comparable<ServerNode> {
public int getNettyPort() {
return nettyPort;
}
+
+ public int getJettyPort() {
+ return jettyPort;
+ }
}
diff --git
a/coordinator/src/main/java/org/apache/uniffle/coordinator/web/resource/CoordinatorServerResource.java
b/coordinator/src/main/java/org/apache/uniffle/coordinator/web/resource/CoordinatorServerResource.java
index e09d3dbdc..788c22461 100644
---
a/coordinator/src/main/java/org/apache/uniffle/coordinator/web/resource/CoordinatorServerResource.java
+++
b/coordinator/src/main/java/org/apache/uniffle/coordinator/web/resource/CoordinatorServerResource.java
@@ -31,7 +31,10 @@ import org.apache.hbase.thirdparty.javax.ws.rs.core.Context;
import org.apache.hbase.thirdparty.javax.ws.rs.core.MediaType;
import org.apache.uniffle.common.util.RssUtils;
+import org.apache.uniffle.common.util.ThreadUtils;
import org.apache.uniffle.common.web.resource.BaseResource;
+import org.apache.uniffle.common.web.resource.MetricResource;
+import org.apache.uniffle.common.web.resource.PrometheusMetricResource;
import org.apache.uniffle.common.web.resource.Response;
import org.apache.uniffle.coordinator.CoordinatorConf;
import org.apache.uniffle.coordinator.CoordinatorServer;
@@ -85,4 +88,22 @@ public class CoordinatorServerResource extends BaseResource {
return (CoordinatorServer)
servletContext.getAttribute(CoordinatorServer.class.getCanonicalName());
}
+
+ @Path("/metrics")
+ public Class<MetricResource> getMetricResource() {
+ return MetricResource.class;
+ }
+
+ @Path("/prometheus/metrics")
+ public Class<PrometheusMetricResource> getPrometheusMetricResource() {
+ return PrometheusMetricResource.class;
+ }
+
+ @GET
+ @Path("/stacks")
+ public String getCoordinatorStacks() {
+ StringBuilder builder = new StringBuilder();
+ ThreadUtils.printThreadInfo(builder, "");
+ return builder.toString();
+ }
}
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
index 64fab2eaf..084478030 100644
---
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
@@ -45,11 +45,15 @@ public class WebProxyServlet extends ProxyServlet {
if (!validateDestination(clientRequest.getServerName(),
clientRequest.getServerPort())) {
return null;
}
- String targetAddress =
-
coordinatorServerAddressesMap.get(clientRequest.getHeader("targetAddress"));
- if (targetAddress == null) {
- // Get random one from coordinatorServerAddressesMap
- targetAddress = coordinatorServerAddressesMap.values().iterator().next();
+ String targetAddress;
+ if (clientRequest.getHeader("serverType").equals("coordinator")) {
+ targetAddress =
coordinatorServerAddressesMap.get(clientRequest.getHeader("targetAddress"));
+ if (targetAddress == null) {
+ // Get random one from coordinatorServerAddressesMap
+ targetAddress =
coordinatorServerAddressesMap.values().iterator().next();
+ }
+ } else {
+ targetAddress = clientRequest.getHeader("targetAddress");
}
StringBuilder target = new StringBuilder();
if (targetAddress.endsWith("/")) {
diff --git a/dashboard/src/main/webapp/src/api/api.js
b/dashboard/src/main/webapp/src/api/api.js
index 08277f0c9..b837658ce 100644
--- a/dashboard/src/main/webapp/src/api/api.js
+++ b/dashboard/src/main/webapp/src/api/api.js
@@ -26,6 +26,84 @@ export function getCoordinatorConf(params, headers) {
return http.get('/coordinator/conf', params, headers, 0)
}
+export async function getShuffleServerConf(address, params, headers) {
+ if (typeof headers === 'undefined') {
+ headers = {};
+ }
+ headers.targetAddress = address;
+ const response = await http.get('/shuffleServer/conf', params, headers, 0);
+ const newWindow = window.open('', '_blank');
+ let tableHTML = `
+ <style>
+ table {
+ width: 100%;
+ }
+ th, td {
+ padding: 0 20px;
+ text-align: left;
+ }
+ </style>
+ <table>
+ <tr>
+ <th>Key</th>
+ <th>Value</th>
+ </tr>
+ `;
+ for (const item of response.data.data) {
+ tableHTML +=
`<tr><td>${item.argumentKey}</td><td>${item.argumentValue}</td></tr>`;
+ }
+ tableHTML += '</table>';
+ newWindow.document.write(tableHTML);
+}
+
+export async function getCoordinatorMetrics(params, headers) {
+ const response = await http.get('/coordinator/metrics', params, headers, 0)
+ const newWindow = window.open('', '_blank');
+ newWindow.document.write('<pre>' + JSON.stringify(response.data, null, 2) +
'</pre>');
+}
+
+export async function getShuffleServerMetrics(address, params, headers) {
+ if (typeof headers === 'undefined') {
+ headers = {}
+ }
+ headers.targetAddress = address
+ const response = await http.get('/shuffleServer/metrics', params, headers, 0)
+ const newWindow = window.open('', '_blank');
+ newWindow.document.write('<pre>' + JSON.stringify(response.data, null, 2) +
'</pre>');
+}
+
+export async function getCoordinatorPrometheusMetrics(params, headers) {
+ const response = await http.get('/coordinator/prometheus/metrics/all',
params, headers, 0)
+ const newWindow = window.open('', '_blank');
+ newWindow.document.write('<pre>' + response.data + '</pre>');
+}
+
+export async function getShuffleServerPrometheusMetrics(address, params,
headers) {
+ if (typeof headers === 'undefined') {
+ headers = {}
+ }
+ headers.targetAddress = address
+ const response = await http.get('/shuffleServer/prometheus/metrics/all',
params, headers, 0)
+ const newWindow = window.open('', '_blank');
+ newWindow.document.write('<pre>' + response.data + '</pre>');
+}
+
+export async function getCoordinatorStacks(params, headers) {
+ const response = await http.get('/coordinator/stacks', params, headers, 0)
+ const newWindow = window.open('', '_blank');
+ newWindow.document.write('<pre>' + response.data + '</pre>');
+}
+
+export async function getShuffleServerStacks(address, params, headers) {
+ if (typeof headers === 'undefined') {
+ headers = {}
+ }
+ headers.targetAddress = address
+ const response = await http.get('/shuffleServer/stacks', params, headers, 0)
+ const newWindow = window.open('', '_blank');
+ newWindow.document.write('<pre>' + response.data + '</pre>');
+}
+
// Create an interface for the total number of nodes
export function getShufflegetStatusTotal(params, headers) {
return http.get('/server/nodes/summary', params, headers, 0)
diff --git a/dashboard/src/main/webapp/src/pages/CoordinatorServerPage.vue
b/dashboard/src/main/webapp/src/pages/CoordinatorServerPage.vue
index a13be3bde..aac7cf317 100644
--- a/dashboard/src/main/webapp/src/pages/CoordinatorServerPage.vue
+++ b/dashboard/src/main/webapp/src/pages/CoordinatorServerPage.vue
@@ -74,19 +74,50 @@
<el-table-column prop="argumentValue" label="Value" min-width="380"
/>
</el-table>
</el-collapse-item>
+ <el-collapse-item title="Coordinator Metrics" name="3">
+ <el-link @click="getCoordinatorMetrics" target="_blank">
+ <el-icon :style="iconStyle">
+ <Link />
+ </el-icon>
+ metrics
+ </el-link>
+ </el-collapse-item>
+ <el-collapse-item title="Coordinator Prometheus Metrics" name="4">
+ <el-link @click="getCoordinatorPrometheusMetrics" target="_blank">
+ <el-icon :style="iconStyle">
+ <Link />
+ </el-icon>
+ prometheus metrics
+ </el-link>
+ </el-collapse-item>
+ <el-collapse-item title="Coordinator Stacks" name="5">
+ <el-link @click="getCoordinatorStacks" target="_blank">
+ <el-icon :style="iconStyle">
+ <Link />
+ </el-icon>
+ stacks
+ </el-link>
+ </el-collapse-item>
</el-collapse>
</div>
</template>
<script>
import { ref, reactive, computed, onMounted } from 'vue'
-import { getCoordinatorConf, getCoordinatorServerInfo } from '@/api/api'
+import {
+ getCoordinatorConf,
+ getCoordinatorMetrics,
+ getCoordinatorPrometheusMetrics,
+ getCoordinatorServerInfo,
+ getCoordinatorStacks
+} from '@/api/api'
import { useCurrentServerStore } from '@/store/useCurrentServerStore'
export default {
+ methods: {getCoordinatorMetrics, getCoordinatorPrometheusMetrics,
getCoordinatorStacks},
setup() {
const pageData = reactive({
- activeNames: ['1', '2'],
+ activeNames: ['1', '2', '3', '4', '5'],
tableData: [],
serverInfo: {}
})
diff --git a/dashboard/src/main/webapp/src/pages/serverstatus/NodeListPage.vue
b/dashboard/src/main/webapp/src/pages/serverstatus/NodeListPage.vue
index 8c455f747..41c7a19c4 100644
--- a/dashboard/src/main/webapp/src/pages/serverstatus/NodeListPage.vue
+++ b/dashboard/src/main/webapp/src/pages/serverstatus/NodeListPage.vue
@@ -28,6 +28,7 @@
<el-table-column prop="ip" label="IP" min-width="80" sortable />
<el-table-column prop="grpcPort" label="Port" min-width="80" />
<el-table-column prop="nettyPort" label="NettyPort" min-width="80" />
+ <el-table-column prop="jettyPort" label="JettyPort" min-width="80" />
<el-table-column
prop="usedMemory"
label="UsedMem"
@@ -65,6 +66,46 @@
:formatter="dateFormatter"
sortable
/>
+ <el-table-column label="Conf">
+ <template v-slot="{ row }">
+ <el-link @click="getShuffleServerConf('http://' + row.ip + ':' +
row.jettyPort)" target="_blank">
+ <el-icon :style="iconStyle">
+ <Link />
+ </el-icon>
+ conf
+ </el-link>
+ </template>
+ </el-table-column>
+ <el-table-column label="Metrics">
+ <template v-slot="{ row }">
+ <el-link @click="getShuffleServerMetrics('http://' + row.ip + ':' +
row.jettyPort)" target="_blank">
+ <el-icon :style="iconStyle">
+ <Link />
+ </el-icon>
+ metrics
+ </el-link>
+ </template>
+ </el-table-column>
+ <el-table-column label="PrometheusMetrics" min-width="150">
+ <template v-slot="{ row }">
+ <el-link @click="getShuffleServerPrometheusMetrics('http://' +
row.ip + ':' + row.jettyPort)" target="_blank">
+ <el-icon :style="iconStyle">
+ <Link />
+ </el-icon>
+ prometheus metrics
+ </el-link>
+ </template>
+ </el-table-column>
+ <el-table-column label="Stacks">
+ <template v-slot="{ row }">
+ <el-link @click="getShuffleServerStacks('http://' + row.ip + ':' +
row.jettyPort)" target="_blank">
+ <el-icon :style="iconStyle">
+ <Link />
+ </el-icon>
+ stacks
+ </el-link>
+ </template>
+ </el-table-column>
<el-table-column prop="tags" label="Tags" min-width="140" />
<el-table-column v-if="isShowRemove" label="Operations">
<template v-slot:default="scope">
@@ -88,10 +129,15 @@ import {
getShuffleDecommissioningList,
getShuffleLostList,
getShuffleUnhealthyList,
- deleteConfirmedLostServer
+ deleteConfirmedLostServer,
+ getShuffleServerConf,
+ getShuffleServerMetrics,
+ getShuffleServerPrometheusMetrics,
+ getShuffleServerStacks
} from '@/api/api'
export default {
+ methods: {getShuffleServerConf, getShuffleServerMetrics,
getShuffleServerPrometheusMetrics, getShuffleServerStacks},
setup() {
const router = useRouter()
const currentServerStore = useCurrentServerStore()
@@ -110,7 +156,8 @@ export default {
tags: '',
status: '',
registrationTime: '',
- timestamp: ''
+ timestamp: '',
+ jettyPort: 0
}
]
})
@@ -170,7 +217,8 @@ export default {
tags: '',
status: '',
registrationTime: '',
- timestamp: ''
+ timestamp: '',
+ jettyPort: 0
}
]
if (router.currentRoute.value.name === 'activeNodeList') {
diff --git a/dashboard/src/main/webapp/src/utils/http.js
b/dashboard/src/main/webapp/src/utils/http.js
index d6afb4ed1..40efd1ed8 100644
--- a/dashboard/src/main/webapp/src/utils/http.js
+++ b/dashboard/src/main/webapp/src/utils/http.js
@@ -21,13 +21,14 @@ import { useCurrentServerStore } from
'@/store/useCurrentServerStore'
const http = {
get(url, params, headers, fontBackFlag) {
if (fontBackFlag === 0) {
- // The system obtains the address of the Coordinator to be accessed from
global variables.
- const currentServerStore = useCurrentServerStore()
if (headers) {
- headers.targetAddress = currentServerStore.currentServer
+ headers.serverType = 'server'
} else {
+ // The system obtains the address of the Coordinator to be accessed
from global variables.
+ const currentServerStore = useCurrentServerStore()
headers = {}
headers.targetAddress = currentServerStore.currentServer
+ headers.serverType = 'coordinator'
}
return request.getBackEndAxiosInstance().get(url, { params, headers })
} else {
diff --git
a/internal-client/src/main/java/org/apache/uniffle/client/impl/grpc/CoordinatorGrpcClient.java
b/internal-client/src/main/java/org/apache/uniffle/client/impl/grpc/CoordinatorGrpcClient.java
index 484bb43d7..5900de6eb 100644
---
a/internal-client/src/main/java/org/apache/uniffle/client/impl/grpc/CoordinatorGrpcClient.java
+++
b/internal-client/src/main/java/org/apache/uniffle/client/impl/grpc/CoordinatorGrpcClient.java
@@ -124,13 +124,15 @@ public class CoordinatorGrpcClient extends GrpcClient
implements CoordinatorClie
Set<String> tags,
ServerStatus serverStatus,
Map<String, StorageInfo> storageInfo,
- int nettyPort) {
+ int nettyPort,
+ int jettyPort) {
ShuffleServerId serverId =
ShuffleServerId.newBuilder()
.setId(id)
.setIp(ip)
.setPort(port)
.setNettyPort(nettyPort)
+ .setJettyPort(jettyPort)
.build();
ShuffleServerHeartBeatRequest request =
ShuffleServerHeartBeatRequest.newBuilder()
@@ -216,7 +218,8 @@ public class CoordinatorGrpcClient extends GrpcClient
implements CoordinatorClie
request.getTags(),
request.getServerStatus(),
request.getStorageInfo(),
- request.getNettyPort());
+ request.getNettyPort(),
+ request.getJettyPort());
RssSendHeartBeatResponse response;
RssProtos.StatusCode statusCode = rpcResponse.getStatus();
diff --git
a/internal-client/src/main/java/org/apache/uniffle/client/request/RssSendHeartBeatRequest.java
b/internal-client/src/main/java/org/apache/uniffle/client/request/RssSendHeartBeatRequest.java
index 72d12642b..34d29d750 100644
---
a/internal-client/src/main/java/org/apache/uniffle/client/request/RssSendHeartBeatRequest.java
+++
b/internal-client/src/main/java/org/apache/uniffle/client/request/RssSendHeartBeatRequest.java
@@ -37,6 +37,7 @@ public class RssSendHeartBeatRequest {
private final ServerStatus serverStatus;
private final Map<String, StorageInfo> storageInfo;
private final int nettyPort;
+ private final int jettyPort;
public RssSendHeartBeatRequest(
String shuffleServerId,
@@ -50,7 +51,8 @@ public class RssSendHeartBeatRequest {
Set<String> tags,
ServerStatus serverStatus,
Map<String, StorageInfo> storageInfo,
- int nettyPort) {
+ int nettyPort,
+ int jettyPort) {
this.shuffleServerId = shuffleServerId;
this.shuffleServerIp = shuffleServerIp;
this.shuffleServerPort = shuffleServerPort;
@@ -63,6 +65,7 @@ public class RssSendHeartBeatRequest {
this.serverStatus = serverStatus;
this.storageInfo = storageInfo;
this.nettyPort = nettyPort;
+ this.jettyPort = jettyPort;
}
public String getShuffleServerId() {
@@ -112,4 +115,8 @@ public class RssSendHeartBeatRequest {
public int getNettyPort() {
return nettyPort;
}
+
+ public int getJettyPort() {
+ return jettyPort;
+ }
}
diff --git a/proto/src/main/proto/Rss.proto b/proto/src/main/proto/Rss.proto
index 5ce36df7e..861d73a3f 100644
--- a/proto/src/main/proto/Rss.proto
+++ b/proto/src/main/proto/Rss.proto
@@ -287,6 +287,7 @@ message ShuffleServerId {
string ip = 2;
int32 port = 3;
int32 netty_port = 4;
+ int32 jetty_port = 5;
}
message ShuffleServerResult {
diff --git
a/server/src/main/java/org/apache/uniffle/server/RegisterHeartBeat.java
b/server/src/main/java/org/apache/uniffle/server/RegisterHeartBeat.java
index 8181ddc74..a8c8b5d76 100644
--- a/server/src/main/java/org/apache/uniffle/server/RegisterHeartBeat.java
+++ b/server/src/main/java/org/apache/uniffle/server/RegisterHeartBeat.java
@@ -85,7 +85,8 @@ public class RegisterHeartBeat {
shuffleServer.getTags(),
shuffleServer.getServerStatus(),
shuffleServer.getStorageManager().getStorageInfo(),
- shuffleServer.getNettyPort());
+ shuffleServer.getNettyPort(),
+ shuffleServer.getJettyPort());
} catch (Exception e) {
LOG.warn("Error happened when send heart beat to coordinator");
}
@@ -106,7 +107,8 @@ public class RegisterHeartBeat {
Set<String> tags,
ServerStatus serverStatus,
Map<String, StorageInfo> localStorageInfo,
- int nettyPort) {
+ int nettyPort,
+ int jettyPort) {
AtomicBoolean sendSuccessfully = new AtomicBoolean(false);
// use `rss.server.heartbeat.interval` as the timeout option
RssSendHeartBeatRequest request =
@@ -122,7 +124,8 @@ public class RegisterHeartBeat {
tags,
serverStatus,
localStorageInfo,
- nettyPort);
+ nettyPort,
+ jettyPort);
ThreadUtils.executeTasks(
heartBeatExecutorService,
diff --git a/server/src/main/java/org/apache/uniffle/server/ShuffleServer.java
b/server/src/main/java/org/apache/uniffle/server/ShuffleServer.java
index 7340fe3b3..461fe2aab 100644
--- a/server/src/main/java/org/apache/uniffle/server/ShuffleServer.java
+++ b/server/src/main/java/org/apache/uniffle/server/ShuffleServer.java
@@ -237,7 +237,9 @@ public class ShuffleServer {
jettyServer = new JettyServer(shuffleServerConf);
registerMetrics();
// register packages and instances for jersey
- jettyServer.addResourcePackages("org.apache.uniffle.common.web.resource");
+ jettyServer.addResourcePackages(
+ "org.apache.uniffle.server.web.resource",
"org.apache.uniffle.common.web.resource");
+ jettyServer.registerInstance(ShuffleServer.class, this);
jettyServer.registerInstance(
CollectorRegistry.class.getCanonicalName() + "#server",
ShuffleServerMetrics.getCollectorRegistry());
@@ -532,6 +534,10 @@ public class ShuffleServer {
return nettyPort;
}
+ public int getJettyPort() {
+ return jettyServer.getHttpPort();
+ }
+
public String getEncodedTags() {
return StringUtils.join(tags, ",");
}
@@ -550,6 +556,7 @@ public class ShuffleServer {
shuffleServer.getTags(),
shuffleServer.getServerStatus(),
shuffleServer.getStorageManager().getStorageInfo(),
- shuffleServer.getNettyPort());
+ shuffleServer.getNettyPort(),
+ shuffleServer.getJettyPort());
}
}
diff --git
a/server/src/main/java/org/apache/uniffle/server/web/resource/ServerResource.java
b/server/src/main/java/org/apache/uniffle/server/web/resource/ServerResource.java
new file mode 100644
index 000000000..c84895c91
--- /dev/null
+++
b/server/src/main/java/org/apache/uniffle/server/web/resource/ServerResource.java
@@ -0,0 +1,82 @@
+/*
+ * 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.server.web.resource;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.servlet.ServletContext;
+
+import org.apache.hbase.thirdparty.javax.ws.rs.GET;
+import org.apache.hbase.thirdparty.javax.ws.rs.Path;
+import org.apache.hbase.thirdparty.javax.ws.rs.core.Context;
+
+import org.apache.uniffle.common.util.ThreadUtils;
+import org.apache.uniffle.common.web.resource.BaseResource;
+import org.apache.uniffle.common.web.resource.MetricResource;
+import org.apache.uniffle.common.web.resource.PrometheusMetricResource;
+import org.apache.uniffle.common.web.resource.Response;
+import org.apache.uniffle.server.ShuffleServer;
+import org.apache.uniffle.server.ShuffleServerConf;
+import org.apache.uniffle.server.web.vo.ServerConfVO;
+
+@Path("/api/shuffleServer")
+public class ServerResource extends BaseResource {
+ @Context protected ServletContext servletContext;
+
+ @GET
+ @Path("/conf")
+ public Response<List<ServerConfVO>> getShuffleServerConf() {
+ return execute(
+ () -> {
+ ShuffleServerConf serverConf =
getShuffleServer().getShuffleServerConf();
+ Set<Map.Entry<String, Object>> allEntry = serverConf.getAll();
+ List<ServerConfVO> serverConfVOs = new ArrayList<>();
+ for (Map.Entry<String, Object> stringObjectEntry : allEntry) {
+ ServerConfVO result =
+ new ServerConfVO(
+ stringObjectEntry.getKey(),
String.valueOf(stringObjectEntry.getValue()));
+ serverConfVOs.add(result);
+ }
+ return serverConfVOs;
+ });
+ }
+
+ @Path("/metrics")
+ public Class<MetricResource> getMetricResource() {
+ return MetricResource.class;
+ }
+
+ @Path("/prometheus/metrics")
+ public Class<PrometheusMetricResource> getPrometheusMetricResource() {
+ return PrometheusMetricResource.class;
+ }
+
+ @GET
+ @Path("/stacks")
+ public String getShuffleServerStacks() {
+ StringBuilder builder = new StringBuilder();
+ ThreadUtils.printThreadInfo(builder, "");
+ return builder.toString();
+ }
+
+ private ShuffleServer getShuffleServer() {
+ return (ShuffleServer)
servletContext.getAttribute(ShuffleServer.class.getCanonicalName());
+ }
+}
diff --git
a/server/src/main/java/org/apache/uniffle/server/web/vo/ServerConfVO.java
b/server/src/main/java/org/apache/uniffle/server/web/vo/ServerConfVO.java
new file mode 100644
index 000000000..0cf3ffe3f
--- /dev/null
+++ b/server/src/main/java/org/apache/uniffle/server/web/vo/ServerConfVO.java
@@ -0,0 +1,44 @@
+/*
+ * 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.server.web.vo;
+
+public class ServerConfVO {
+ private String argumentKey;
+ private String argumentValue;
+
+ public ServerConfVO(String argumentKey, String argumentValue) {
+ this.argumentKey = argumentKey;
+ this.argumentValue = argumentValue;
+ }
+
+ public String getArgumentKey() {
+ return argumentKey;
+ }
+
+ public void setArgumentKey(String argumentKey) {
+ this.argumentKey = argumentKey;
+ }
+
+ public String getArgumentValue() {
+ return argumentValue;
+ }
+
+ public void setArgumentValue(String argumentValue) {
+ this.argumentValue = argumentValue;
+ }
+}