@@ -5,6 +5,7 @@ import com.ruoyi.common.core.domain.AjaxResult; | |||||
import com.ruoyi.file.model.FileModel; | import com.ruoyi.file.model.FileModel; | ||||
import com.ruoyi.file.model.FileSystemFilter; | import com.ruoyi.file.model.FileSystemFilter; | ||||
import com.ruoyi.file.object.ProjectState; | import com.ruoyi.file.object.ProjectState; | ||||
import com.ruoyi.file.object.RuntimeState; | |||||
import com.ruoyi.file.object.StateMachine; | import com.ruoyi.file.object.StateMachine; | ||||
import com.ruoyi.file.service.FileSystemService; | import com.ruoyi.file.service.FileSystemService; | ||||
import com.ruoyi.file.service.StateService; | import com.ruoyi.file.service.StateService; | ||||
@@ -94,4 +95,15 @@ public class StateController extends BaseController | |||||
return AjaxResult.success(); | 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() | public void GC() | ||||
{ | { | ||||
System.gc(); | |||||
//System.gc(); | |||||
gcCount++; | gcCount++; | ||||
lastGCTime = new Date(); | 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.StringUtils; | ||||
import com.ruoyi.common.utils.spring.SpringUtils; | import com.ruoyi.common.utils.spring.SpringUtils; | ||||
import com.ruoyi.file.object.ProjectState; | import com.ruoyi.file.object.ProjectState; | ||||
import com.ruoyi.file.object.RuntimeState; | |||||
import com.ruoyi.file.object.StateMachine; | import com.ruoyi.file.object.StateMachine; | ||||
import com.ruoyi.file.utils.ProjectUtils; | |||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import org.springframework.stereotype.Service; | import org.springframework.stereotype.Service; | ||||
import javax.annotation.PostConstruct; | import javax.annotation.PostConstruct; | ||||
import javax.annotation.PreDestroy; | import javax.annotation.PreDestroy; | ||||
import java.util.Date; | |||||
import java.util.List; | import java.util.List; | ||||
import java.util.Map; | import java.util.Map; | ||||
@@ -32,9 +31,17 @@ public class StateService | |||||
return StateMachine.INSTANCE.GetStates(); | return StateMachine.INSTANCE.GetStates(); | ||||
} | } | ||||
public RuntimeState runtimeState() | |||||
{ | |||||
return RuntimeState.capture(); | |||||
} | |||||
public long gc() | public long gc() | ||||
{ | { | ||||
StateMachine.INSTANCE.GC(); | StateMachine.INSTANCE.GC(); | ||||
dump(); | |||||
System.gc(); | |||||
init(); | |||||
return StateMachine.INSTANCE.gcCount; | return StateMachine.INSTANCE.gcCount; | ||||
} | } | ||||
@@ -34,3 +34,10 @@ export function dump() { | |||||
method: 'post', | 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> |