@@ -5,6 +5,7 @@ import com.ruoyi.common.core.domain.AjaxResult; | |||
import com.ruoyi.file.model.FileModel; | |||
import com.ruoyi.file.model.FileSystemFilter; | |||
import com.ruoyi.file.object.ProjectState; | |||
import com.ruoyi.file.object.RuntimeState; | |||
import com.ruoyi.file.object.StateMachine; | |||
import com.ruoyi.file.service.FileSystemService; | |||
import com.ruoyi.file.service.StateService; | |||
@@ -94,4 +95,15 @@ public class StateController extends BaseController | |||
return AjaxResult.success(); | |||
} | |||
/** | |||
* 查询jvm信息 | |||
*/ | |||
@ApiOperation("查询jvm信息") | |||
@PreAuthorize("@ss.hasPermi('admin:state:runtime')") | |||
@GetMapping("/runtime") | |||
public AjaxResult runtime() | |||
{ | |||
RuntimeState state = stateService.runtimeState(); | |||
return AjaxResult.success(state); | |||
} | |||
} |
@@ -0,0 +1,126 @@ | |||
package com.ruoyi.file.object; | |||
import com.fasterxml.jackson.annotation.JsonFormat; | |||
import lombok.Data; | |||
import java.lang.management.ManagementFactory; | |||
import java.lang.management.MemoryMXBean; | |||
import java.lang.management.MemoryPoolMXBean; | |||
import java.lang.management.MemoryUsage; | |||
import java.lang.management.RuntimeMXBean; | |||
import java.lang.management.ThreadInfo; | |||
import java.lang.management.ThreadMXBean; | |||
import java.util.ArrayList; | |||
import java.util.Date; | |||
import java.util.List; | |||
@Data | |||
public final class RuntimeState | |||
{ | |||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS") | |||
private Date startTime; | |||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS") | |||
private Date uptime; | |||
private long runDuration; | |||
private String arguments; | |||
private long heapUsed; | |||
private long heapInit; | |||
private long heapMax; | |||
private long heapCommitted; | |||
private long nonHeapUsed; | |||
private long nonHeapInit; | |||
private long nonHeapMax; | |||
private long nonHeapCommitted; | |||
private long threadCount; | |||
private long daemonThreadCount; | |||
private long peakThreadCount; | |||
private long startedThreadCount; | |||
private List<ThreadState> threads; | |||
private List<MemoryPoolState> memoryPools; | |||
@Data | |||
public static class ThreadState | |||
{ | |||
private long threadId; | |||
private String threadName; | |||
private String threadState; | |||
private Boolean isCurrent; | |||
} | |||
@Data | |||
public static class MemoryPoolState | |||
{ | |||
private String name; | |||
private String type; | |||
private long memoryUsed = -1; | |||
private long memoryInit = -1; | |||
private long memoryMax = -1; | |||
private long memoryCommitted = -1; | |||
} | |||
public static RuntimeState capture() | |||
{ | |||
RuntimeState state = new RuntimeState(); | |||
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); | |||
long startTime = runtimeMXBean.getStartTime(); | |||
long uptime = runtimeMXBean.getUptime(); | |||
state.startTime = new Date(startTime); | |||
state.uptime = new Date(startTime + uptime); | |||
state.runDuration = uptime; | |||
state.arguments = String.join(" ", runtimeMXBean.getInputArguments()); | |||
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); | |||
MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); | |||
state.heapUsed = heapMemoryUsage.getUsed(); | |||
state.heapMax = heapMemoryUsage.getMax(); | |||
state.heapInit = heapMemoryUsage.getInit(); | |||
state.heapCommitted = heapMemoryUsage.getCommitted(); | |||
MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage(); | |||
state.nonHeapUsed = nonHeapMemoryUsage.getUsed(); | |||
state.nonHeapMax = nonHeapMemoryUsage.getMax(); | |||
state.nonHeapInit = nonHeapMemoryUsage.getInit(); | |||
state.nonHeapCommitted = nonHeapMemoryUsage.getCommitted(); | |||
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); | |||
state.threadCount = threadMXBean.getThreadCount(); | |||
state.daemonThreadCount = threadMXBean.getDaemonThreadCount(); | |||
state.peakThreadCount = threadMXBean.getPeakThreadCount(); | |||
state.startedThreadCount = threadMXBean.getTotalStartedThreadCount(); | |||
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(true, true); | |||
state.threads = new ArrayList<>(); | |||
for(ThreadInfo threadInfo : threadInfos) | |||
{ | |||
ThreadState t = new ThreadState(); | |||
t.threadId = threadInfo.getThreadId(); | |||
t.threadName = threadInfo.getThreadName(); | |||
t.threadState = threadInfo.getThreadState().name(); | |||
t.isCurrent = threadInfo.getThreadId() == Thread.currentThread().getId(); | |||
state.threads.add(t); | |||
} | |||
state.memoryPools = new ArrayList<>(); | |||
List<MemoryPoolMXBean> memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans(); | |||
for(MemoryPoolMXBean memoryPoolMXBean : memoryPoolMXBeans) | |||
{ | |||
MemoryPoolState m = new MemoryPoolState(); | |||
m.name = memoryPoolMXBean.getName(); | |||
m.type = memoryPoolMXBean.getType().name(); | |||
MemoryUsage usage = memoryPoolMXBean.getUsage(); | |||
if(null != usage) | |||
{ | |||
m.memoryUsed = usage.getUsed(); | |||
m.memoryMax = usage.getMax(); | |||
m.memoryInit = usage.getInit(); | |||
m.memoryCommitted = usage.getCommitted(); | |||
} | |||
state.memoryPools.add(m); | |||
} | |||
return state; | |||
} | |||
} |
@@ -178,7 +178,7 @@ public final class StateMachine | |||
public void GC() | |||
{ | |||
System.gc(); | |||
//System.gc(); | |||
gcCount++; | |||
lastGCTime = new Date(); | |||
} | |||
@@ -5,14 +5,13 @@ import com.ruoyi.common.core.redis.RedisCache; | |||
import com.ruoyi.common.utils.StringUtils; | |||
import com.ruoyi.common.utils.spring.SpringUtils; | |||
import com.ruoyi.file.object.ProjectState; | |||
import com.ruoyi.file.object.RuntimeState; | |||
import com.ruoyi.file.object.StateMachine; | |||
import com.ruoyi.file.utils.ProjectUtils; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.springframework.stereotype.Service; | |||
import javax.annotation.PostConstruct; | |||
import javax.annotation.PreDestroy; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.Map; | |||
@@ -32,9 +31,17 @@ public class StateService | |||
return StateMachine.INSTANCE.GetStates(); | |||
} | |||
public RuntimeState runtimeState() | |||
{ | |||
return RuntimeState.capture(); | |||
} | |||
public long gc() | |||
{ | |||
StateMachine.INSTANCE.GC(); | |||
dump(); | |||
System.gc(); | |||
init(); | |||
return StateMachine.INSTANCE.gcCount; | |||
} | |||
@@ -34,3 +34,10 @@ export function dump() { | |||
method: 'post', | |||
}) | |||
} | |||
export function runtimeState() { | |||
return request({ | |||
url: '/state/runtime', | |||
method: 'get', | |||
}) | |||
} |
@@ -0,0 +1,170 @@ | |||
<template> | |||
<div class="app-container"> | |||
<el-row :gutter="10" class="mb8"> | |||
<el-col :span="1.5"> | |||
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="gc" v-hasPermi="['admin:state:gc']">GC</el-button> | |||
</el-col> | |||
<right-toolbar :showSearch.sync="showSearch" @queryTable="refresh"></right-toolbar> | |||
</el-row> | |||
<el-divider content-position="left">JVM</el-divider> | |||
<Descriptions> | |||
<el-descriptions-item label="启动时间">{{state.startTime}}</el-descriptions-item> | |||
<el-descriptions-item label="当前时间">{{state.uptime}}</el-descriptions-item> | |||
<el-descriptions-item label="运行时长(s)">{{state.runDuration / 1000}}</el-descriptions-item> | |||
<el-descriptions-item label="启动参数">{{state.arguments}}</el-descriptions-item> | |||
</Descriptions> | |||
<el-divider content-position="left">堆内存</el-divider> | |||
<Descriptions> | |||
<el-descriptions-item label="初始分配">{{state.heapInit | formatFileSize}}</el-descriptions-item> | |||
<el-descriptions-item label="最大">{{state.heapMax | formatFileSize}}</el-descriptions-item> | |||
<el-descriptions-item label="已使用">{{state.heapUsed | formatFileSize}}</el-descriptions-item> | |||
<el-descriptions-item label="已提交">{{state.heapCommitted | formatFileSize}}</el-descriptions-item> | |||
</Descriptions> | |||
<el-divider content-position="left">非堆内存</el-divider> | |||
<Descriptions> | |||
<el-descriptions-item label="初始分配">{{state.nonHeapInit | formatFileSize}}</el-descriptions-item> | |||
<el-descriptions-item label="最大">{{state.nonHeapMax | formatFileSize}}</el-descriptions-item> | |||
<el-descriptions-item label="已使用">{{state.nonHeapUsed | formatFileSize}}</el-descriptions-item> | |||
<el-descriptions-item label="已提交">{{state.nonHeapCommitted | formatFileSize}}</el-descriptions-item> | |||
</Descriptions> | |||
<el-divider content-position="left">内存池</el-divider> | |||
<el-table :data="state.memoryPools || []"> | |||
<el-table-column type="index" label="序号" width="50" align="center" /> | |||
<el-table-column label="名称" align="left" header-align="center" prop="name" /> | |||
<el-table-column label="类型" align="center" prop="type"/> | |||
<el-table-column label="初始分配" align="center" prop="memoryInit" :formatter="fileSizeFormatter"/> | |||
<el-table-column label="最大" align="center" prop="memoryMax" :formatter="fileSizeFormatter"/> | |||
<el-table-column label="已使用" align="center" prop="memoryUsed" :formatter="fileSizeFormatter"/> | |||
<el-table-column label="已提交" align="center" prop="memoryCommitted" :formatter="fileSizeFormatter"/> | |||
</el-table> | |||
<el-divider content-position="left">线程</el-divider> | |||
<Descriptions> | |||
<el-descriptions-item label="前后台活动线程数">{{state.threadCount}}</el-descriptions-item> | |||
<el-descriptions-item label="后台活动线程数">{{state.daemonThreadCount}}</el-descriptions-item> | |||
<el-descriptions-item label="实时线程数">{{state.peakThreadCount}}</el-descriptions-item> | |||
<el-descriptions-item label="最多线程数">{{state.startedThreadCount}}</el-descriptions-item> | |||
</Descriptions> | |||
<el-table :data="state.threads || []" :row-style="threadRowStyle"> | |||
<el-table-column type="index" label="序号" width="50" align="center" /> | |||
<el-table-column label="线程ID" align="center" prop="threadId" sortable/> | |||
<el-table-column label="线程名" align="left" header-align="center" prop="threadName" /> | |||
<el-table-column label="线程状态" align="center" prop="threadState" sortable/> | |||
</el-table> | |||
</div> | |||
</template> | |||
<script> | |||
import DeptSelect from "@/components/common/DeptSelect.vue"; | |||
import ServerFileChooser from "@/components/ServerFileChooser.vue"; | |||
import FullscreenDialog from "@/components/FullscreenDialog.vue"; | |||
import ProjectState from "@/views/file/state/ProjectState.vue"; | |||
import {stateList, gc, stateMachine, dump, runtimeState} from "@/api/file/state"; | |||
import Descriptions from "@/components/common/Descriptions.vue"; | |||
import {formatFileSize} from "@/utils/file"; | |||
export default { | |||
name: "State", | |||
components: { | |||
Descriptions, | |||
ProjectState, | |||
FullscreenDialog, | |||
ServerFileChooser, | |||
DeptSelect | |||
}, | |||
dicts: ['sys_bool'], | |||
data() { | |||
return { | |||
// 遮罩层 | |||
loading: false, | |||
// 遮罩按钮新增点击状态 | |||
diglogStatus: true, | |||
// 导出遮罩层 | |||
exportLoading: false, | |||
// 选中数组 | |||
ids: [], | |||
// 非单个禁用 | |||
single: true, | |||
// 非多个禁用 | |||
multiple: true, | |||
// 默认隐藏搜索条件 | |||
showSearch: false, | |||
// 总条数 | |||
total: 0, | |||
// 项目表格数据 | |||
stateList: [], | |||
// 弹出层标题 | |||
title: "", | |||
// 是否禁用 字典 sys_bool字典 | |||
disabledOptions: [], | |||
// 查询参数 | |||
queryParams: { | |||
// 分页 | |||
pageNum: 1, | |||
pageSize: 10, | |||
// 查询排序 | |||
//orderByColumn: "id", | |||
//isAsc: "desc", | |||
// 翻译字典 | |||
//translate_dict: "1", | |||
}, | |||
// 表单参数 | |||
form: {}, | |||
// 详情组件列数 | |||
descColumn: 2, | |||
// 详情组件Label所占百分比, 最大={100 / descColumn}. 内容所占百分比={100 / descColumn - descLabelWidth} | |||
descLabelWidth: 15, | |||
// 对话框显示只读的详情 | |||
projectToken: null, | |||
state: {}, | |||
}; | |||
}, | |||
created() { | |||
this.getRuntimeState(); | |||
}, | |||
methods: { | |||
// 取消按钮 | |||
cancel() { | |||
this.reset(); | |||
}, | |||
// 表单重置 | |||
reset() { | |||
this.form = { | |||
}; | |||
}, | |||
refresh() { | |||
this.getRuntimeState(); | |||
}, | |||
/** 重置按钮操作 */ | |||
resetQuery() { | |||
this.resetForm("queryForm"); | |||
this.handleQuery(); | |||
}, | |||
gc() { | |||
gc().then((resp) => { | |||
this.$message.success('成功'); | |||
this.getRuntimeState(); | |||
}); | |||
}, | |||
getRuntimeState() { | |||
this.state = {}; | |||
runtimeState().then((resp) => { | |||
this.state = resp.data; | |||
}); | |||
}, | |||
fileSizeFormatter(row, col, val) { | |||
return formatFileSize(val); | |||
}, | |||
threadRowStyle({row}) { | |||
let style = {}; | |||
if(row.isCurrent) | |||
style['background-color'] = '#67C23A'; | |||
return style; | |||
}, | |||
}, | |||
filters: { | |||
formatFileSize, | |||
}, | |||
}; | |||
</script> |