Hippo4j 动态线程池监控优化改造
一、本次优化点
- 「线程池监控」查询范围由原先的 30 分钟改为 60 分钟且可配置化;
- 「线程池监控」增加时间选择器,可选范围 10 天;
- 「线程池监控」数据库数据持久化 10 天且可配置;
二、优化步骤
前端部分:增加时间选择器组件、设置默认时间、中英文转译、传递至后端
-
修改页面路径:/hippo4j-ui/src/views/hippo4j/monitor/index.vue,在 class 为「
filter-container
」的大块后面接着写1 <div> 2 <el-date-picker v-model="listQuery.entTime" type="datetime" placeholder="请选择截止时间" :picker-options="pickerOptions"> 3 </el-date-picker> 4 </div>
-
选择地址及端口的选择器代码修改
1 <el-select v-model="listQuery.identify" :placeholder="$t('threadPoolMonitor.ipPortRequired')" style="width: 220px" 2 filterable class="filter-item" @change="identifyChange"> 3 <el-option v-for="item in identifyOptions" :key="item.key" :label="item.display_name" :value="item.key" /> 4 </el-select>
-
「data」块更改
1 data() { 2 return { 3 pickerOptions: { 4 disabledDate(time) { 5 let curDateMillisecond = (new Date()).getTime(); 6 let tenDaysMillisecond = 10 * 24 * 3600 * 1000; 7 let tenDaysTime = curDateMillisecond - tenDaysMillisecond; 8 return time.getTime() > Date.now() || time.getTime() < tenDaysTime;; 9 } 10 }, 11 listQuery: { 12 endTime: null 13 }, 14 endTime: null 15 }; 16 }
-
「methods」方法更改
1 identifyChange(identify) { 2 if (identify) { 3 this.endTime = new Date(); 4 } 5 }, 6 fetchData() { 7 if (!this.listQuery.tenantId) { 8 this.$message.warning(this.$t('message.emptyWarning', { name: this.$t('tenantManage.tenant') })); 9 return; 10 } 11 if (!this.listQuery.itemId) { 12 this.$message.warning(this.$t('message.emptyWarning', { name: this.$t('projectManage.item') })); 13 return; 14 } 15 if (!this.listQuery.tpId) { 16 this.$message.warning(this.$t('message.emptyWarning', { name: this.$t('threadPool.threadPool') })); 17 return; 18 } 19 if (!this.listQuery.identify) { 20 this.$message.warning(this.$t('message.emptyWarning', { name: this.$t('threadPoolMonitor.ipPort') })); 21 return; 22 } 23 if (this.endTime) { 24 let time = this.endTime; 25 time.setHours(time.getHours() + 8); 26 this.listQuery.endTime = time.toISOString(); 27 time.setHours(time.getHours() - 8); 28 } 29 this.listQuery.instanceId = this.listQuery.identify; 30 threadPoolApi.info(this.listQuery).then((res) => { 31 this.temp = res; 32 }); 33 34 // monitorApi.lastTaskCountFun(this.listQuery).then((res) => { 35 // this.rejectCount = res.rejectCount; 36 // this.lastTaskCount = res.completedTaskCount; 37 // }); 38 39 this.initChart(); 40 }, 41 refreshData() { 42 this.listQuery.tenantId = null; 43 this.listQuery.itemId = null; 44 this.listQuery.tpId = null; 45 this.listQuery.identify = null; 46 this.itemOptions = []; 47 this.threadPoolOptions = []; 48 this.identifyOptions = []; 49 this.listQuery.endTime = null; 50 this.endTime = null; 51 }
-
中英文转译,js 路径 /hippo4j-ui/src/locale/lang/zh.js、/hippo4j-ui/src/locale/lang/en.js,分别在 js 页面下找到如下代码块,增加
chooseDeadline
以及对应翻译1 //线程池监控 2 threadPoolMonitor: { 3 ipPort: 'IP : Port', 4 ipPortRequired: 'IP : Port(必填)', 5 noResultsYet: '暂无结果', 6 chooseDeadline: '请选择截止时间', 7 }, 8 9 //线程池监控 10 threadPoolMonitor: { 11 ipPort: 'IP : Port', 12 ipPortRequired: 'IP : Port(Required)', 13 noResultsYet: 'No results yet', 14 chooseDeadline: 'Choose deadline', 15 },
-
编译前端项目,执行「package.json」中 scritps 的第二个命令
vue-cli-service build
,会生成或重新替换 dist 目录下的文件,将该目录下的所有文件替换 /hippo4j//hippo4j-server/hippo4j-console/src/main/resources/static 该目录下的静态文件,前端即完成;也可单独将前端代码拉出来部署,后端不依赖静态文件,需要改动前端访问后端接口的 ip+port,在 vue.config.js 中 查找http://127.0.0.1:6691/hippo4j/v1/cs
后替换即可;
后端部分:接口动态查询,数据持久化配置、查询范围配置
-
我这边是将原本的内置配置文件,改为了读取 nacos 中的配置文件,有个一层注册中心的概念,大家可参考下;
-
在 hippo4j-config 的 pom.xml 中引入如下依赖
1 <dependency> 2 <groupId>org.springframework.cloud</groupId> 3 <artifactId>spring-cloud-starter-bootstrap</artifactId> 4 <version>3.0.2</version> 5 </dependency>
-
修改
ServerBootstrapProperties
类1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package cn.hippo4j.config.config; 19 20import lombok.Getter; 21import lombok.Setter; 22import lombok.extern.slf4j.Slf4j; 23import org.springframework.boot.context.properties.ConfigurationProperties; 24import org.springframework.cloud.context.config.annotation.RefreshScope; 25import org.springframework.context.annotation.Configuration; 26 27/** 28 * Server bootstrap properties. 29 */ 30@Slf4j 31@Getter 32@Setter 33@RefreshScope 34@Configuration 35@ConfigurationProperties(prefix = ServerBootstrapProperties.PREFIX) 36public class ServerBootstrapProperties { 37 38 public final static String PREFIX = "hippo4j.core"; 39 40 /** 41 * Whether to start the background task of cleaning up thread pool history data. 42 */ 43 private Boolean cleanHistoryDataEnable = Boolean.TRUE; 44 45 /** 46 * Regularly clean up the historical running data of thread pool. unit: minute. 47 */ 48 private Integer cleanHistoryDataPeriod = 30; 49 50 /** 51 * query up the historical running data of thread pool. unit: minute. 52 */ 53 private Integer queryHistoryDataPeriod = 60; 54 55 /** 56 * Netty server port. 57 */ 58 private String nettyServerPort = "8899"; 59} 60
-
修改
MonitorQueryReqDTO
类1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package cn.hippo4j.config.model.biz.monitor; 19 20import lombok.Data; 21 22import java.time.LocalDateTime; 23 24/** 25 * Monitor query req DTO. 26 */ 27@Data 28public class MonitorQueryReqDTO { 29 30 /** 31 * Tenant id 32 */ 33 private String tenantId; 34 35 /** 36 * Item id 37 */ 38 private String itemId; 39 40 /** 41 * Thread-pool id 42 */ 43 private String tpId; 44 45 /** 46 * Instance id 47 */ 48 private String instanceId; 49 50 /** 51 * end time 52 * 截止时间 53 */ 54 private LocalDateTime endTime; 55} 56
-
修改
HisRunDataServiceImpl
类下的query
、queryInfoThreadPoolMonitor
、queryThreadPoolLastTaskCount
方法1 @Override 2 public List<MonitorRespDTO> query(MonitorQueryReqDTO reqDTO) { 3 LocalDateTime currentDate = LocalDateTime.now(); 4 LocalDateTime dateTime = currentDate.plusMinutes(-properties.getQueryHistoryDataPeriod()); 5 long startTime = DateUtil.getTime(dateTime); 6 List<HisRunDataInfo> hisRunDataInfos = this.lambdaQuery() 7 .eq(HisRunDataInfo::getTenantId, reqDTO.getTenantId()) 8 .eq(HisRunDataInfo::getItemId, reqDTO.getItemId()) 9 .eq(HisRunDataInfo::getTpId, reqDTO.getTpId()) 10 .eq(HisRunDataInfo::getInstanceId, reqDTO.getInstanceId()) 11 .between(HisRunDataInfo::getTimestamp, startTime, DateUtil.getTime(currentDate)) 12 .orderByAsc(HisRunDataInfo::getTimestamp) 13 .list(); 14 return BeanUtil.convert(hisRunDataInfos, MonitorRespDTO.class); 15 } 16 17 @Override 18 public MonitorActiveRespDTO queryInfoThreadPoolMonitor(MonitorQueryReqDTO reqDTO) { 19 LocalDateTime currentDate; 20 if (Objects.isNull(reqDTO.getEndTime())) { 21 currentDate = LocalDateTime.now(); 22 } else { 23 currentDate = reqDTO.getEndTime(); 24 } 25 LocalDateTime dateTime = currentDate.plusMinutes(-properties.getQueryHistoryDataPeriod()); 26 long startTime = DateUtil.getTime(dateTime); 27 List<HisRunDataInfo> hisRunDataInfos = this.lambdaQuery() 28 .eq(HisRunDataInfo::getTenantId, reqDTO.getTenantId()) 29 .eq(HisRunDataInfo::getItemId, reqDTO.getItemId()) 30 .eq(HisRunDataInfo::getTpId, reqDTO.getTpId()) 31 .eq(HisRunDataInfo::getInstanceId, reqDTO.getInstanceId()) 32 .between(HisRunDataInfo::getTimestamp, startTime, DateUtil.getTime(currentDate)) 33 .orderByAsc(HisRunDataInfo::getTimestamp) 34 .list(); 35 List<String> times = new ArrayList<>(); 36 List<Long> poolSizeList = new ArrayList<>(); 37 List<Long> activeSizeList = new ArrayList<>(); 38 List<Long> queueCapacityList = new ArrayList<>(); 39 List<Long> queueSizeList = new ArrayList<>(); 40 List<Long> completedTaskCountList = new ArrayList<>(); 41 List<Long> rejectCountList = new ArrayList<>(); 42 List<Long> queueRemainingCapacityList = new ArrayList<>(); 43 List<Long> currentLoadList = new ArrayList<>(); 44 long countTemp = 0L; 45 AtomicBoolean firstFlag = new AtomicBoolean(Boolean.TRUE); 46 for (HisRunDataInfo each : hisRunDataInfos) { 47 String time = DateUtil.format(new Date(each.getTimestamp()), NORM_TIME_PATTERN); 48 times.add(time); 49 poolSizeList.add(each.getPoolSize()); 50 activeSizeList.add(each.getActiveSize()); 51 queueSizeList.add(each.getQueueSize()); 52 rejectCountList.add(each.getRejectCount()); 53 queueRemainingCapacityList.add(each.getQueueRemainingCapacity()); 54 currentLoadList.add(each.getCurrentLoad()); 55 queueCapacityList.add(each.getQueueCapacity()); 56 if (firstFlag.get()) { 57 completedTaskCountList.add(0L); 58 firstFlag.set(Boolean.FALSE); 59 countTemp = each.getCompletedTaskCount(); 60 continue; 61 } 62 long completedTaskCount = each.getCompletedTaskCount(); 63 long countTask = completedTaskCount - countTemp; 64 completedTaskCountList.add(countTask); 65 countTemp = each.getCompletedTaskCount(); 66 } 67 return new MonitorActiveRespDTO(times, poolSizeList, activeSizeList, queueSizeList, completedTaskCountList, rejectCountList, queueRemainingCapacityList, currentLoadList, queueCapacityList); 68 } 69 70 @Override 71 public MonitorRespDTO queryThreadPoolLastTaskCount(MonitorQueryReqDTO reqDTO) { 72 LocalDateTime currentDate = LocalDateTime.now(); 73 LocalDateTime dateTime = currentDate.plusMinutes(-properties.getQueryHistoryDataPeriod()); 74 long startTime = DateUtil.getTime(dateTime); 75 HisRunDataInfo hisRunDataInfo = this.lambdaQuery() 76 .eq(HisRunDataInfo::getTenantId, reqDTO.getTenantId()) 77 .eq(HisRunDataInfo::getItemId, reqDTO.getItemId()) 78 .eq(HisRunDataInfo::getTpId, reqDTO.getTpId()) 79 .eq(HisRunDataInfo::getInstanceId, reqDTO.getInstanceId()) 80 .orderByDesc(HisRunDataInfo::getTimestamp) 81 .between(HisRunDataInfo::getTimestamp, startTime, DateUtil.getTime(currentDate)) 82 .last("LIMIT 1") 83 .one(); 84 return BeanUtil.convert(hisRunDataInfo, MonitorRespDTO.class); 85 }
-
在 nacos 中可以自定义查询时间以及数据持久化时间配置,由于加了
@RefreshScope
注解,修改后会立即生效1# 是否开启线程池历史数据清洗 false 不开启 2hippo4j: 3 core: 4 clean-history-data-enable: true 5 clean-history-data-period: 14400 6 query-history-data-period: 60
三、注意事项
- 客户端在每次启动时,都会根据一些信息动态的生成实例标识,这也是查询线程池监控数据的查询参数,这就导致了,本次启动后会查询不到之前的线程池监控数据;