| @@ -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> | |||