目录

大橙子

VX:ZzzChChen
Phone:13403656751
Email:zxydczzs@gmail.com

X

Hippo4j 动态线程池监控优化改造

一、本次优化点

  1. 「线程池监控」查询范围由原先的 30 分钟改为 60分钟且可配置化;
  2. 「线程池监控」增加时间选择器,可选范围 10 天;
  3. 「线程池监控」数据库数据持久化 10 天且可配置;

二、优化步骤

前端部分:增加时间选择器组件、设置默认时间、中英文转译、传递至后端

  1. 修改页面路径:/hippo4j-ui/src/views/hippo4j/monitor/index.vue,在 class 为「filter-container」的大块后面接着写

        <div>
          <el-date-picker v-model="listQuery.entTime" type="datetime" placeholder="请选择截止时间" :picker-options="pickerOptions">
          </el-date-picker>
        </div>
    
  2. 选择地址及端口的选择器代码修改

          <el-select v-model="listQuery.identify" :placeholder="$t('threadPoolMonitor.ipPortRequired')" style="width: 220px"
            filterable class="filter-item" @change="identifyChange">
            <el-option v-for="item in identifyOptions" :key="item.key" :label="item.display_name" :value="item.key" />
          </el-select>
    
  3. 「data」块更改

      data() {
        return {
          pickerOptions: {
            disabledDate(time) {
              let curDateMillisecond = (new Date()).getTime();
              let tenDaysMillisecond = 10 * 24 * 3600 * 1000;
              let tenDaysTime = curDateMillisecond - tenDaysMillisecond;
              return time.getTime() > Date.now() || time.getTime() < tenDaysTime;;
            }
          },
          listQuery: {
            endTime: null
          },
          endTime: null
        };
      }
    
  4. 「methods」方法更改

        identifyChange(identify) {
          if (identify) {
            this.endTime = new Date();
          }
        },
        fetchData() {
          if (!this.listQuery.tenantId) {
            this.$message.warning(this.$t('message.emptyWarning', { name: this.$t('tenantManage.tenant') }));
            return;
          }
          if (!this.listQuery.itemId) {
            this.$message.warning(this.$t('message.emptyWarning', { name: this.$t('projectManage.item') }));
            return;
          }
          if (!this.listQuery.tpId) {
            this.$message.warning(this.$t('message.emptyWarning', { name: this.$t('threadPool.threadPool') }));
            return;
          }
          if (!this.listQuery.identify) {
            this.$message.warning(this.$t('message.emptyWarning', { name: this.$t('threadPoolMonitor.ipPort') }));
            return;
          }
          if (this.endTime) {
            let time = this.endTime;
            time.setHours(time.getHours() + 8);
            this.listQuery.endTime = time.toISOString();
            time.setHours(time.getHours() - 8);
          }
          this.listQuery.instanceId = this.listQuery.identify;
          threadPoolApi.info(this.listQuery).then((res) => {
            this.temp = res;
          });
    
          // monitorApi.lastTaskCountFun(this.listQuery).then((res) => {
          //   this.rejectCount = res.rejectCount;
          //   this.lastTaskCount = res.completedTaskCount;
          // });
    
          this.initChart();
        },
        refreshData() {
          this.listQuery.tenantId = null;
          this.listQuery.itemId = null;
          this.listQuery.tpId = null;
          this.listQuery.identify = null;
          this.itemOptions = [];
          this.threadPoolOptions = [];
          this.identifyOptions = [];
          this.listQuery.endTime = null;
          this.endTime = null;
        }
    
  5. 中英文转译,js 路径 /hippo4j-ui/src/locale/lang/zh.js、/hippo4j-ui/src/locale/lang/en.js,分别在 js 页面下找到如下代码块,增加 chooseDeadline 以及对应翻译

      //线程池监控
      threadPoolMonitor: {
        ipPort: 'IP : Port',
        ipPortRequired: 'IP : Port(必填)',
        noResultsYet: '暂无结果',
        chooseDeadline: '请选择截止时间',
      },
    
      //线程池监控
      threadPoolMonitor: {
        ipPort: 'IP : Port',
        ipPortRequired: 'IP : Port(Required)',
        noResultsYet: 'No results yet',
        chooseDeadline: 'Choose deadline',
      },
    
  6. 编译前端项目,执行「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 后替换即可;

后端部分:接口动态查询,数据持久化配置、查询范围配置

  1. 我这边是将原本的内置配置文件,改为了读取 nacos 中的配置文件,有个一层注册中心的概念,大家可参考下;

  2. 在 hippo4j-config 的 pom.xml 中引入如下依赖

            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-bootstrap</artifactId>
                <version>3.0.2</version>
            </dependency>
    
  3. 修改 ServerBootstrapProperties

    /*
     * 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 cn.hippo4j.config.config;
    
    import lombok.Getter;
    import lombok.Setter;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.cloud.context.config.annotation.RefreshScope;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * Server bootstrap properties.
     */
    @Slf4j
    @Getter
    @Setter
    @RefreshScope
    @Configuration
    @ConfigurationProperties(prefix = ServerBootstrapProperties.PREFIX)
    public class ServerBootstrapProperties {
    
        public final static String PREFIX = "hippo4j.core";
    
        /**
         * Whether to start the background task of cleaning up thread pool history data.
         */
        private Boolean cleanHistoryDataEnable = Boolean.TRUE;
    
        /**
         * Regularly clean up the historical running data of thread pool. unit: minute.
         */
        private Integer cleanHistoryDataPeriod = 30;
    
        /**
         * query up the historical running data of thread pool. unit: minute.
         */
        private Integer queryHistoryDataPeriod = 60;
    
        /**
         * Netty server port.
         */
        private String nettyServerPort = "8899";
    }
    
    
  4. 修改 MonitorQueryReqDTO

    /*
     * 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 cn.hippo4j.config.model.biz.monitor;
    
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    /**
     * Monitor query req DTO.
     */
    @Data
    public class MonitorQueryReqDTO {
    
        /**
         * Tenant id
         */
        private String tenantId;
    
        /**
         * Item id
         */
        private String itemId;
    
        /**
         * Thread-pool id
         */
        private String tpId;
    
        /**
         * Instance id
         */
        private String instanceId;
    
        /**
         * end time
         * 截止时间
         */
        private LocalDateTime endTime;
    }
    
    
  5. 修改 HisRunDataServiceImpl 类下的 queryqueryInfoThreadPoolMonitorqueryThreadPoolLastTaskCount 方法

        @Override
        public List<MonitorRespDTO> query(MonitorQueryReqDTO reqDTO) {
            LocalDateTime currentDate = LocalDateTime.now();
            LocalDateTime dateTime = currentDate.plusMinutes(-properties.getQueryHistoryDataPeriod());
            long startTime = DateUtil.getTime(dateTime);
            List<HisRunDataInfo> hisRunDataInfos = this.lambdaQuery()
                    .eq(HisRunDataInfo::getTenantId, reqDTO.getTenantId())
                    .eq(HisRunDataInfo::getItemId, reqDTO.getItemId())
                    .eq(HisRunDataInfo::getTpId, reqDTO.getTpId())
                    .eq(HisRunDataInfo::getInstanceId, reqDTO.getInstanceId())
                    .between(HisRunDataInfo::getTimestamp, startTime, DateUtil.getTime(currentDate))
                    .orderByAsc(HisRunDataInfo::getTimestamp)
                    .list();
            return BeanUtil.convert(hisRunDataInfos, MonitorRespDTO.class);
        }
    
        @Override
        public MonitorActiveRespDTO queryInfoThreadPoolMonitor(MonitorQueryReqDTO reqDTO) {
            LocalDateTime currentDate;
            if (Objects.isNull(reqDTO.getEndTime())) {
                currentDate = LocalDateTime.now();
            } else {
                currentDate = reqDTO.getEndTime();
            }
            LocalDateTime dateTime = currentDate.plusMinutes(-properties.getQueryHistoryDataPeriod());
            long startTime = DateUtil.getTime(dateTime);
            List<HisRunDataInfo> hisRunDataInfos = this.lambdaQuery()
                    .eq(HisRunDataInfo::getTenantId, reqDTO.getTenantId())
                    .eq(HisRunDataInfo::getItemId, reqDTO.getItemId())
                    .eq(HisRunDataInfo::getTpId, reqDTO.getTpId())
                    .eq(HisRunDataInfo::getInstanceId, reqDTO.getInstanceId())
                    .between(HisRunDataInfo::getTimestamp, startTime, DateUtil.getTime(currentDate))
                    .orderByAsc(HisRunDataInfo::getTimestamp)
                    .list();
            List<String> times = new ArrayList<>();
            List<Long> poolSizeList = new ArrayList<>();
            List<Long> activeSizeList = new ArrayList<>();
            List<Long> queueCapacityList = new ArrayList<>();
            List<Long> queueSizeList = new ArrayList<>();
            List<Long> completedTaskCountList = new ArrayList<>();
            List<Long> rejectCountList = new ArrayList<>();
            List<Long> queueRemainingCapacityList = new ArrayList<>();
            List<Long> currentLoadList = new ArrayList<>();
            long countTemp = 0L;
            AtomicBoolean firstFlag = new AtomicBoolean(Boolean.TRUE);
            for (HisRunDataInfo each : hisRunDataInfos) {
                String time = DateUtil.format(new Date(each.getTimestamp()), NORM_TIME_PATTERN);
                times.add(time);
                poolSizeList.add(each.getPoolSize());
                activeSizeList.add(each.getActiveSize());
                queueSizeList.add(each.getQueueSize());
                rejectCountList.add(each.getRejectCount());
                queueRemainingCapacityList.add(each.getQueueRemainingCapacity());
                currentLoadList.add(each.getCurrentLoad());
                queueCapacityList.add(each.getQueueCapacity());
                if (firstFlag.get()) {
                    completedTaskCountList.add(0L);
                    firstFlag.set(Boolean.FALSE);
                    countTemp = each.getCompletedTaskCount();
                    continue;
                }
                long completedTaskCount = each.getCompletedTaskCount();
                long countTask = completedTaskCount - countTemp;
                completedTaskCountList.add(countTask);
                countTemp = each.getCompletedTaskCount();
            }
            return new MonitorActiveRespDTO(times, poolSizeList, activeSizeList, queueSizeList, completedTaskCountList, rejectCountList, queueRemainingCapacityList, currentLoadList, queueCapacityList);
        }
    
        @Override
        public MonitorRespDTO queryThreadPoolLastTaskCount(MonitorQueryReqDTO reqDTO) {
            LocalDateTime currentDate = LocalDateTime.now();
            LocalDateTime dateTime = currentDate.plusMinutes(-properties.getQueryHistoryDataPeriod());
            long startTime = DateUtil.getTime(dateTime);
            HisRunDataInfo hisRunDataInfo = this.lambdaQuery()
                    .eq(HisRunDataInfo::getTenantId, reqDTO.getTenantId())
                    .eq(HisRunDataInfo::getItemId, reqDTO.getItemId())
                    .eq(HisRunDataInfo::getTpId, reqDTO.getTpId())
                    .eq(HisRunDataInfo::getInstanceId, reqDTO.getInstanceId())
                    .orderByDesc(HisRunDataInfo::getTimestamp)
                    .between(HisRunDataInfo::getTimestamp, startTime, DateUtil.getTime(currentDate))
                    .last("LIMIT 1")
                    .one();
            return BeanUtil.convert(hisRunDataInfo, MonitorRespDTO.class);
        }
    
  6. 在 nacos 中可以自定义查询时间以及数据持久化时间配置,由于加了@RefreshScope 注解,修改后会立即生效

    # 是否开启线程池历史数据清洗 false 不开启
    hippo4j:
      core:
        clean-history-data-enable: true
        clean-history-data-period: 14400
        query-history-data-period: 60
    

三、注意事项

  1. 客户端在每次启动时,都会根据一些信息动态的生成实例标识,这也是查询线程池监控数据的查询参数,这就导致了,本次启动后会查询不到之前的线程池监控数据;

标题:Hippo4j 动态线程池监控优化改造
作者:zzzzchen
地址:https://www.dczzs.com/articles/2023/07/25/1690276490935.html