YYGH-14-监测中心

检测中心

最近又有一个新的构思,就是通过SpringBoot Actuator,完成对于所有的service模块的监控,并且在admin显示数据,这个思路来源于阿里云

image-20220812104706694

image-20220812104526052

类似于这样显示一个折线图

思路:

1.这个监测功能整合到sta模块,建立一个数据库service_sta,里面建立一个表ramMonitor,字段(id,服务名称,ram占用,时间),再建立一个表cpuMonitor(id,服务名称,cpu占用率,时间)

2.所有模块引入actuator依赖,并且配置使sta模块可以通过http请求获取服务的运行状态(这里我计划用cloud的openfeign写内部掉用,整好可以借助gateway中的路由)

3.在task模块创建一个每小时执行一次的任务,通过mq传给sta模块

4.sta模块接收到消息之后开始逐个发送请求,获取每一个服务的数据,(初始的构思是一分钟一次)

5.当服务内存占用特别多的时候,msm模块会发送邮件告诉管理员,服务坏了.

6.未来展望(画饼),现在监测中心只是可以对服务进行一个监测功能,这里我希望他在监测到服务异常之后会自动上线新的服务,减少线上的损失

Actuator

这里我主要是通过请求这两个来获取参数

堆内存

localhost:端口号/actuator/metrics/jvm.memory.used?tag=area:heap

系统cpu

localhost:端口号/actuator/metrics/system.cpu.usage

之前有写过一个springboot整合Actuator,这是之前的博客

指标监控 - 小岚 (zhaodapiaoliang.top)

这里面用到了一个叫spring-boot-admin的第三方组件,这里我分析了他的路由

image-20220812164122531

获取了上面两个url,下一步就是,下面是spring-boot-admin的效果图

image-20220812163825155

接下来就是写代码的过程了我会在b站直播:https://space.bilibili.com/443685731

在我们的所有service中添加依赖同时配置所有模块的openFeign依赖

<!--服务监测-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
/**
 * order模块堆内存占用情况
 */
@GetMapping("/actuator/metrics/jvm.memory.used?tag=area:heap")
public String getRam();

/**
 * order模块内存占用率
 * @return
 */
@GetMapping("/actuator/metrics/system.cpu.usage")
public String getCpu();

image-20220820170231506

数据库

建表语句

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for cpu
-- ----------------------------
DROP TABLE IF EXISTS `cpu`;
CREATE TABLE `cpu`  (
  `id` int(16) NOT NULL AUTO_INCREMENT,
  `service_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `cpu_size` double(4, 2) NULL DEFAULT NULL,
  `time` datetime NULL DEFAULT NULL,
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `is_deleted` tinyint(3) NULL DEFAULT 0 COMMENT '逻辑删除(1:已删除,0:未删除)',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 260 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for ram
-- ----------------------------
DROP TABLE IF EXISTS `ram`;
CREATE TABLE `ram`  (
  `id` int(16) NOT NULL AUTO_INCREMENT,
  `service_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `ram_size` int(11) NULL DEFAULT NULL,
  `time` datetime NULL DEFAULT NULL,
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `is_deleted` tinyint(1) UNSIGNED ZEROFILL NOT NULL DEFAULT 0 COMMENT '逻辑删除(1:已删除,0:未删除)',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 244 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

model模块

@Data
public class ActuatorVo {

    private String name;

    private String description;

    private String baseUnit;

    private List<Measurements> measurements ;

    private List<AvailableTags> availableTags ;

}
@Data
public class CpuVo {

    @ApiModelProperty(value = "时间")
    private String time;

    @ApiModelProperty(value = "cpu占用率")
    private double cpuSize;

    @ApiModelProperty(value = "服务名称")
    private String serviceName;
}
@Data
public class Measurements {

    private String statistic;

    private String value;

}
@Data
public class RamVo {
    @ApiModelProperty(value = "时间")
    private String time;

    @ApiModelProperty(value = "占用内存大小")
    private int ramSize;

    @ApiModelProperty(value = "服务名称")
    private String serviceName;
}
@TableName("cpu")
@Data
public class CpuEntity extends BaseEntity {

    @TableField("service_name")
    private String serviceName;

    @TableField("cpu_size")
    private Double cpuSize;

    @TableField("time")
    private Date time;

}
@TableName("ram")
@Data
public class RamEntity extends BaseEntity {


    @TableField("service_name")
    private String serviceName;

    @TableField("ram_size")
    private int ramSize;

    @TableField("time")
    private Date time;

}

sta模块

集成mybatis-plus

image-20220820170434961

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.yygh.sta.mapper.ramMapper">

    <select id="selectActuator" resultType="com.example.yygh.vo.sta.RamVo">
        SELECT
            time AS time,
            ram_size AS RamSize,
            service_name AS serviceName
        FROM
            ram
        ORDER BY
            id DESC
            LIMIT 8
    </select>

</mapper>

建立controller

@RestController
@RequestMapping("/admin/sta")
public class ActuatorController {

    @Autowired
    private cpuService cpuService;

    @Autowired
    private ramService ramService;

    @GetMapping("/armTest")
    public void armTest() {
        ramService.save();
    }

    @GetMapping("/cpuTest")
    public void cpuTest() {
        cpuService.save();
    }

    @GetMapping("getCpu")
    public Result showCpu() {
        return Result.ok(cpuService.show());
    }

    @GetMapping("getRam")
    public Result showRam() {
        return Result.ok(ramService.show());
    }
}

建立service

@Service
public class cpuServiceImpl extends ServiceImpl<cpuMapper, CpuEntity> implements cpuService {


    @Autowired
    private List<cpuActuatorCore> cpuActuatorCores;

    @Autowired
    private RabbitService rabbitService;

    @Override
    public void save() {
        cpuActuatorCores.forEach(i -> {
            CpuEntity cpuEntity = i.saveCpu();
            if (cpuEntity.getCpuSize() > 95) {
                MsmVo msmVo = new MsmVo();
                HashMap<String, Object> map = new HashMap<>();
                map.put("title", cpuEntity.getServiceName() + "cpu");
                map.put("service", cpuEntity.getServiceName() + "cpu");
                msmVo.setParam(map);
                msmVo.setPhone("2590416618@qq.com");
                rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_MSM, MqConst.ROUTING_MSM_ITEM, msmVo);
            }
            baseMapper.insert(cpuEntity);
        });
    }

    @Cacheable(value = "cpu", keyGenerator = "keyGenerator")
    @Override
    public Map<String, Object> show() {
        List<CpuVo> cpuVos = baseMapper.selectActuator();
        Map<String, Object> map = new HashMap<>();
        List<String> servers = cpuVos.stream().map(CpuVo::getServiceName).collect(Collectors.toList());
        List<Double> dataList = cpuVos.stream().map(CpuVo::getCpuSize).collect(Collectors.toList());
        map.put("servers", servers);
        map.put("dataList", dataList);
        return map;
    }
}
@Service
public class ramServiceImpl extends ServiceImpl<ramMapper, RamEntity> implements ramService {

    @Autowired
    private List<ramActuatorCore> ramActuatorCores;

    @Autowired
    private RabbitService rabbitService;

    @Override
    public void save() {
        ramActuatorCores.forEach(i -> {
            RamEntity ramEntity = i.saveRam();
            if (ramEntity.getRamSize() > 1000) {
                MsmVo msmVo = new MsmVo();
                HashMap<String, Object> map = new HashMap<>();
                map.put("static", "actuator");
                map.put("title", ramEntity.getServiceName() + "ram");
                map.put("service", ramEntity.getServiceName() + "ram");
                msmVo.setParam(map);
                msmVo.setPhone("2590416618@qq.com");
                rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_MSM, MqConst.ROUTING_MSM_ITEM, msmVo);
            }
            baseMapper.insert(ramEntity);
        });
    }

    @Cacheable(value = "ram", keyGenerator = "keyGenerator")
    @Override
    public Map<String, Object> show() {
        List<RamVo> cpuVos = baseMapper.selectActuator();
        Map<String, Object> map = new HashMap<>();
        List<String> servers = cpuVos.stream().map(RamVo::getServiceName).collect(Collectors.toList());
        List<Integer> dataList = cpuVos.stream().map(RamVo::getRamSize).collect(Collectors.toList());
        map.put("servers", servers);
        map.put("dataList", dataList);
        return map;
    }
}

image-20220820170634185

public interface cpuActuatorCore {


    CpuEntity saveCpu();
}
@Service
@Slf4j
public class staCpuActuatorCore implements cpuActuatorCore {
    @Autowired
    private staFeignClient staFeignClient;

    @Override
    public CpuEntity saveCpu() {
        String cpu = staFeignClient.getCpu();
        log.info(cpu);
        ActuatorVo jsonObject = JSON.parseObject(cpu, ActuatorVo.class);
        BigDecimal bigDecimal = new BigDecimal(jsonObject.getMeasurements().get(0).getValue());
        Double cpuValue = bigDecimal.doubleValue() * 100;
        log.info(String.valueOf(cpuValue));
        CpuEntity cpuEntity = new CpuEntity();
        cpuEntity.setCpuSize(cpuValue);
        cpuEntity.setTime(new Date());
        cpuEntity.setServiceName("service_sta");
        return cpuEntity;
    }
}

其他服务都是大同小异

建立和msm模块通过rabbitmq实现异步通信的生产者

@Component
@Slf4j
public class StaReceiver {

    @Autowired
    private cpuService cpuService;

    @Autowired
    private ramService ramService;

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_TASK_6, durable = "true"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK),
            key = {MqConst.ROUTING_TASK_6}
    ))
    public void startActuator(String str) {
        log.info("收到信息" + str);
        cpuService.save();
        ramService.save();
    }
}

msm模块

进行了一个判断,找出是监控中心要发送的邮件

    @Override
    @Async
    public void send(MsmVo msmVo) {
        log.info(msmVo.getPhone());
        //判断邮箱是否为空
        if (StringUtils.isEmpty(msmVo.getPhone())) {
            return;
        }

        if (!ObjectUtils.isEmpty(msmVo.getParam().get("static")) && msmVo.getParam().get("static").equals("actuator")) {
            sendActuator(msmVo);
        }
        //1.创建一个简单的的消息邮件
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
        simpleMailMessage.setSubject(msmVo.getParam().get("title") + "预约成功");
        simpleMailMessage.setText(msmVo.getParam().get("name") + "的预约成功请于" + msmVo.getParam().get("reserveDate") + "到医院就诊");
        simpleMailMessage.setTo(msmVo.getPhone());
        simpleMailMessage.setFrom("2590416618@qq.com");
        javaMailSenderImpl.send(simpleMailMessage);
    }

    private void sendActuator(MsmVo msmVo) {
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
        simpleMailMessage.setSubject(msmVo.getParam().get("title") + "服务器报警");
        simpleMailMessage.setText(msmVo.getParam().get("service") + "服务器报警请即使处理");
        simpleMailMessage.setTo(msmVo.getPhone());
        simpleMailMessage.setFrom("2590416618@qq.com");
        javaMailSenderImpl.send(simpleMailMessage);
    }
}

前端

{
  path: '/actuator',
  component: Layout,
  redirect: '/actuator',
  name: 'BasesInfo',
  meta: { title: '监测中心', icon: 'table' },
  alwaysShow: true,
  children: [
    {
      path: 'cpu',
      name: 'CPU监测',
      component: () => import('@/views/statistics/actuator/cpu'),
      meta: { title: 'CPU监测' }
    },
    {
      path: 'ram',
      name: '内存监测',
      component: () => import('@/views/statistics/actuator/ram'),
      meta: { title: '内存监测' }
    }
  ]
},
getCpu() {
  return request({
    url: `${api_name}/getCpu`,
    method: 'get'
  })
},
getRam() {
  return request({
    url: `${api_name}/getRam`,
    method: 'get'
  })
}
<template>
  <div className="app-container">
    <div className="chart-container">
      <div id="chart" ref="chart" className="chart" style="height:500px;width:100%"/>
    </div>
  </div>
</template>

<script>
import echarts from 'echarts'
import statisticsApi from '@/api/orderStatistics'

export default {

  data() {
    return {
      btnDisabled: false,
      chart: null,
      title: '',
      xData: [], // x轴数据
      yData: [] // y轴数据
    }
  },

  created() {
    this.showChart()
  },
  methods: {
    // 初始化图表数据
    showChart() {
      statisticsApi.getRam().then(response => {
        this.yData = response.data.dataList
        this.xData = response.data.servers
        this.setChartData()
      })
    },

    setChartData() {
      // 基于准备好的dom,初始化echarts实例
      var myChart = echarts.init(document.getElementById('chart'))
      // 指定图表的配置项和数据
      var option = {
        title: {
          text: 'CPU性能图'
        },
        tooltip: {},
        legend: {
          data: [this.title]
        },
        xAxis: {
          data: this.xData
        },
        yAxis: {
          minInterval: 1
        },
        series: [{
          name: this.title,
          type: 'line',
          data: this.yData
        }]
      }

      // 使用刚指定的配置项和数据显示图表。
      myChart.setOption(option)
    }
  }
}
</script>
<template>
  <div className="app-container">
    <div className="chart-container">
      <div id="chart" ref="chart" className="chart" style="height:500px;width:100%"/>
    </div>
  </div>
</template>

<script>
import echarts from 'echarts'
import statisticsApi from '@/api/orderStatistics'

export default {

  data() {
    return {
      btnDisabled: false,
      chart: null,
      title: '',
      xData: [], // x轴数据
      yData: [] // y轴数据
    }
  },

  created() {
    this.showChart()
  },
  methods: {
    // 初始化图表数据
    showChart() {
      statisticsApi.getRam().then(response => {
        this.yData = response.data.dataList
        this.xData = response.data.servers
        this.setChartData()
      })
    },

    setChartData() {
      // 基于准备好的dom,初始化echarts实例
      var myChart = echarts.init(document.getElementById('chart'))
      // 指定图表的配置项和数据
      var option = {
        title: {
          text: 'CPU性能图'
        },
        tooltip: {},
        legend: {
          data: [this.title]
        },
        xAxis: {
          data: this.xData
        },
        yAxis: {
          minInterval: 1
        },
        series: [{
          name: this.title,
          type: 'line',
          data: this.yData
        }]
      }

      // 使用刚指定的配置项和数据显示图表。
      myChart.setOption(option)
    }
  }
}
</script>

image-20220820171206893

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值