SpringBootSQL监控

零、人在地球

一个成熟的生产环境会存在很多sql,测试环境不能完全复现出生产环境的情况。因此我们需要一些数据监控内容

一般用于监控:

①一些重点sql是否正常(某些极端数据导致查询异常)

②一些跑批任务的执行结果

③一些表是否有数据(比如按日更新监控增量)

全部的sql都可以监控,出现异常直接发送预警消息

一、代码

1、依赖

  implementation 'org.springframework.boot:spring-boot-starter-quartz'

2、SQL表结构

T_JIANKONG 监控主表

CREATE TABLE T_JIANKONG(`id` char(32) NOT NULL COMMENT '主键',`childTaskName` varchar(64) NOT NULL COMMENT '子任务名称',`taskID` varchar(32) NOT NULL COMMENT '主任务ID 用户关联 推送、数据接收等需求的ID',`schema` varchar(255) DEFAULT NULL COMMENT '数据库名',`tableName` varchar(255) DEFAULT NULL COMMENT '表明,单表监控时填写',`cron` varchar(32) DEFAULT NULL COMMENT '执行时间',`executeSql` text COMMENT '执行sql',`excuteResult` text COMMENT '执行结果',`excuteTime` varchar(32) DEFAULT NULL COMMENT '执行时间',`principal` varchar(255) DEFAULT NULL COMMENT '推送地址:1,个人企微ID,2,企微群',`principalName` varchar(255) DEFAULT NULL COMMENT '负责人名称',`sendType` varchar(4) NOT NULL COMMENT '发送类型 1: 个人  2 发送群',`formula` varchar(255) DEFAULT NULL COMMENT '判断公式',`compareResult` varchar(255) DEFAULT NULL COMMENT '比较结果',`checkResult` varchar(32) DEFAULT NULL COMMENT '结果判断',`stateCode` varchar(100) DEFAULT '0' COMMENT '是否删除 0正常 1删除',PRIMARY KEY (`id`,`childTaskName`) USING BTREE,KEY `INX_TASKID` (`taskID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据监控表';

T_JIANKONG_LOG 监控日志表


CREATE TABLE T_JIANKONG_LOG(`id` char(32) NOT NULL COMMENT '主键',`childTaskName` varchar(64) DEFAULT NULL COMMENT '子任务名称',`excuteResult` text COMMENT '执行结果',`excuteTime` datetime DEFAULT NULL COMMENT '执行时间',`checkResult` varchar(32) DEFAULT NULL COMMENT '结果判断',`isSend` varchar(32) DEFAULT NULL COMMENT '是否发送告警信息',`stateCode` varchar(100) DEFAULT '0' COMMENT '是否删除 1正常 2删除',PRIMARY KEY (`id`) USING BTREE,KEY `INX_TASKID` (`childTaskName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据监控历史表';

3、监控代码

主体类 EntityJob.java

public class EntityJob {private String id;private String cron;private String taskName;private String childTaskName;private String executeResult;private String executeSql;private String excuteTime;private String principal;private String sendType;private String formula;private String compareResult;private String checkResult;private String stateCode;private String principalName;private String schema;private String beanName;
}

监控服务Service  DatamonitorService.java

@Service
public class DatamonitorService {public static final Logger logger = LoggerFactory.getLogger(DatamonitorService.class);@Resourceprivate SqlSession sqlSession;@Resourceprivate Scheduler scheduler ;private static final String DEFAULT_JOB_GROUP = "default_job_group";@PostConstructpublic void init(){Map<String,String> params = org.elasticsearch.common.collect.Map.of("stateCode","0");List<EntityJob> list = sqlSession.selectList("com.longze.gsh.dao.getDataMonitor",params);for(EntityJob m : list){createJob(m);}}public void createJob (EntityJob entityJob){try {if (!CronExpression.isValidExpression(entityJob.getCron())){XxlJobHelper.log("cron is invalid");return ;}String sql =  entityJob.getExecuteSql().toUpperCase(Locale.ROOT);if(sql.contains("UPDATE") || sql.contains("DELETE") ||  sql.contains("DROP") ||  sql.contains("TRUNCATE") ||  sql.contains("INSERT") ){throw new SchedulerException("非法sql语句,只能使用select 语句");}if(!sql.contains("COUNT") && !sql.contains(" LIMIT ") ){throw new SchedulerException("数据监控只能使用count 语句");}// 构建任务JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class).withIdentity(JobKey.jobKey(entityJob.getId(),DEFAULT_JOB_GROUP)).build() ;// 构建Cron调度器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(entityJob.getCron()).withMisfireHandlingInstructionDoNothing() ;// 不补偿// 任务触发器CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(entityJob.getId()).withSchedule(scheduleBuilder).build() ;jobDetail.getJobDataMap().put(entityJob.getId(),entityJob);scheduler.scheduleJob(jobDetail,trigger) ;logger.info("添加新任务:"+ entityJob.getChildTaskName());} catch (SchedulerException e){logger.error("添加任务失败,任务名称:"+entityJob.getTaskName()+" : "+ e.getMessage());}}public void updateJob(EntityJob entityJob) {try {// 查询触发器Keyif (!CronExpression.isValidExpression(entityJob.getCron())){XxlJobHelper.log("cron is invalid");throw new SchedulerException("cron is invalid");}//JobKey jobKey = getJobKey(entityJob);// 可能会更新 sql 和  cron  简单处理直接重建任务deleteJob(entityJob);createJob(entityJob);//            TriggerKey triggerKey = new TriggerKey(jobKey.getName(), jobKey.getGroup());
//            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(entityJob.getCron());
//            CronTrigger trigger = TriggerBuilder.newTrigger()
//                    .withIdentity(entityJob.getId())
//                    .withSchedule(cronScheduleBuilder)
//                    .build();
//            scheduler.rescheduleJob(triggerKey, trigger);} catch (SchedulerException e) {XxlJobHelper.log("updateJob Fail,{}",e) ;}}public void deleteJob(EntityJob job) throws SchedulerException {JobKey key = getJobKey(job);scheduler.pauseJob(key);scheduler.unscheduleJob(TriggerKey.triggerKey(key.getName(), key.getGroup()));scheduler.deleteJob(key);}//获取JobDetail,JobDetail是任务的定义,而Job是任务的执行逻辑,JobDetail里会引用一个Job Class来定义public JobDetail getJobDetail(JobKey jobKey, String description, JobDataMap map) {return JobBuilder.newJob(QuartzJob.class).withIdentity(jobKey).withDescription(description).setJobData(map).storeDurably().build();}//获取Trigger (Job的触发器,执行规则)public Trigger getTrigger(String triggerID) {return TriggerBuilder.newTrigger().withIdentity(triggerID).build();}//获取JobKey,包含Name和Grouppublic JobKey getJobKey(EntityJob job) {return JobKey.jobKey(job.getId());}}

4、定时器类

public class QuartzJob implements Job {@Resourceprivate com.alibaba.druid.pool.DruidDataSource dataSource;@Autowiredprivate Map<String, MonitorProcessFactory> factoryMap;@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);String updateSql = "update T_JIANKONG SET excuteResult='%s',excuteTime=NOW(),checkResult='%s' where id='%s' ";String insertSql = "INSERT INTO `T_JIANKONG_LOG`(`id`, `childTaskName`, `excuteResult`, `excuteTime`, " +"`checkResult`, `isSend`, `stateCode`) VALUES (UUID_FACTORY(), '%s', '%s', NOW(), '%s', '%s', '0')";String content = "监控任务:%s \n" +"监控结果:%s \n" +"期待结果:%s \n" +"负责人:%s \n" +"执行时间:%s";JobDetail jobDetail = context.getJobDetail();JobKey jobKey = context.getTrigger().getJobKey();JobDataMap jobDataMap = jobDetail.getJobDataMap();EntityJob entityJob = (EntityJob) jobDataMap.get(jobKey.getName());XxlJobHelper.log("执行检测 :" + entityJob.getTaskName());long r = 0;String checkResult = "";String isSend = "0";MonitorProcessFactory processFactory = factoryMap.get(entityJob.getBeanName());if (!StringUtils.isEmpty(entityJob.getBeanName()) && null != processFactory) {checkResult = processFactory.check(entityJob) ? "1" : "0";}try {String excuteSql = entityJob.getExecuteSql().replaceAll("\\{schema}", entityJob.getSchema());r = jdbcTemplate.query(excuteSql, rs -> {if (rs.next()) {return rs.getLong(1);} else {return 0l;}});checkResult = check(entityJob.getFormula(), entityJob.getCompareResult(), r) ? "1" : "0";//发送预警if (checkResult.equals("0") && !StringUtils.isEmpty(entityJob.getPrincipal())) {if (entityJob.getSendType().equals("2")) {// 发送企微群RobotText robotText = new RobotText();robotText.setContent(String.format(content, entityJob.getChildTaskName(), r, entityJob.getFormula()+entityJob.getCompareResult(), entityJob.getPrincipalName(),DateUtil.formatDate(new Date(), DateUtil.DEFAULT_DATETIME_PATTERN)));robotPushService.sendRobotText("QuartzJob", entityJob.getPrincipal(), robotText, "1", null, null);} else {Text text = new Text();text.setContent(String.format(content, entityJob.getChildTaskName(), r, entityJob.getCompareResult(), entityJob.getPrincipalName(),DateUtil.formatDate(new Date(), DateUtil.DEFAULT_DATETIME_PATTERN)));wechatOfficService.sendTemplateText(entityJob.getPrincipal(), Arrays.asList(entityJob.getPrincipal()), text, entityJob.getChildTaskName(), null);}}} catch (Exception e) {// GET MSGcheckResult = "-1";} finally {//UPDATEString executeSql = String.format(updateSql, r, checkResult, entityJob.getId());jdbcTemplate.execute(executeSql);//插入历史记录表String executeSql2 = String.format(insertSql, entityJob.getChildTaskName(), r, checkResult, isSend);jdbcTemplate.execute(executeSql2);}}private boolean check(String formula, String value, long result) {if (formula.equals("等于")) {return Long.parseLong(value) == (result);}if (formula.equals("大于")) {return Long.parseLong(value) < (result);}if (formula.equals("小于")) {return Long.parseLong(value) > (result);}return true;}
}

5、比较方法工厂

可以根据设置的监控配置,设置自定义比较规则

MonitorCheckFactory.java
@Component
public interface MonitorCheckFactory {// 特殊判断处理方法Boolean check(EntityJob entityJob);}
Test1Monitor.java
@Service("Test1Monitor")
public class Test1Monitor implements MonitorProcessFactory {
//    @Resource
//    private DruidDataSource dataSource;@Overridepublic Boolean check(EntityJob entityJob) {
//        String executeSql = entityJob.getExecuteSql().replaceAll("\\{schema}", entityJob.getSchema());
//        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//        jdbcTemplate.execute(executeSql);return null;}
}

6、每次启动项目,会加载所有配置中监控sql,如果在项目启动中修改了数据库中配置的sql,正在执行的监控不会及时更新,需要一个任务单独更改跑批

DataMonitorSchedule.java
@Component
public class DataMonitorSchedule {@Resourceprivate SqlSession sqlSession;@Resourceprivate DatamonitorService datamonitorService;@Resourceprivate com.alibaba.druid.pool.DruidDataSource dataSource;@XxlJob("updateDataMonitor")public void updateJob() {String childTaskName ="";if(!StringUtils.isEmpty(XxlJobHelper.getJobParam())){childTaskName = XxlJobHelper.getJobParam();}Map<String,String> params = org.elasticsearch.common.collect.Map.of("stateCode","0","childTaskName",childTaskName);List<EntityJob> list = sqlSession.selectList("com.longze.gsh.dao.getDataMonitor",params);for(EntityJob m : list){datamonitorService.updateJob(m);}}
}

7、sql mapper文件

 <select id="getDataMonitor" resultType="com.longze.gsh.dataMonitor.EntityJob" parameterType="map">select * from T_JIANKONG where 1=1<if test="stateCode!=null and stateCode!=''">and stateCode=#{stateCode}</if><if test="taskID!=null and taskID!=''">and taskID = #{taskID}</if><if test="childTaskName!=null and childTaskName!=''">and childTaskName = #{childTaskName}</if></select>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/241225.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】bind绑定包装器全解(代码演示,例题演示)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Linux》…

WPF StackPanel

StackPanel是一个控件容器&#xff0c;它按照一个方向&#xff08;水平或垂直&#xff09;堆叠子元素&#xff0c;使得它们沿一个轴线对齐。你可以在StackPanel中放置其他控件&#xff0c;如按钮、标签、文本框、图片等等。这些控件的排列方式由StackPanel按照指定的方向自动确…

5.OpenResty系列之深入理解(一)

本文基于Centos8进行实践&#xff0c;请读者自行安装OpenResty。 1. 内部调用 进入默认安装路径 cd /usr/local/openresty/nginx/conf vim nginx.conflocation /sum {# 只允许内部调用internal;content_by_lua_block {local args ngx.req.get_uri_args()ngx.print(tonumber…

java进阶学习笔记

学习java深度学习&#xff0c;提升编程思维&#xff0c;适合掌握基础知识的工作者学习 1.反射和代理1.1 概念介绍1.2应用场景1.3 反射-reflect1.3.1 获得类-Class1.3.2 获得类的字段-Field1.3.3 动态访问和修改对象实例的字段1.3.4 获得类方法-Method1.3.5 调用方法.invoke1.3.…

评论回复功能数据库设计

1. 评论的场景 类似csdn博客评论 2. 建表sql CREATE TABLE comment (id varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT id,parent_id varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 父级评论id&#xff08;…

【Kafka每日一问】kafka三种压缩方式差别?

Kafka 提供了三种压缩算法&#xff0c;分别是GZIP、Snappy 和 LZ4。 这三种压缩算法的差异主要在以下方面&#xff1a; 压缩比&#xff1a;GZIP 压缩比最高&#xff0c;DEFLATE 算法&#xff0c;但压缩和解压缩速度相对较慢&#xff1b;Snappy 压缩比次之&#xff0c;但压缩和…

Kubernetes pod ip 暴露

1. k8s pod 和 service 网络暴露 借助 iptables 的路由转发功能&#xff0c;打通k8s集群内的pod和service网络&#xff0c;与外部网络联通 # 查看集群的 pod 网段和 service 网段 kubectl -n kube-system describe cm kubeadm-config networking:dnsDomain: cluster.localpod…

数据结构和算法笔记3:双指针法(快慢指针)

双指针法&#xff08;快慢指针法&#xff09;在数组、字符串和链表的操作中是非常常见的&#xff0c;这里结合力扣上的题进行可一下梳理&#xff0c;主要的思路是我们要明确快指针指的是什么&#xff0c;慢指针指的是什么。 1. 移除元素类问题 27. 移除元素 要我们移除目标元…

Vue2和Vue3组件间通信方式汇总(2)------$emit

组件间通信方式是前端必不可少的知识点&#xff0c;前端开发经常会遇到组件间通信的情况&#xff0c;而且也是前端开发面试常问的知识点之一。接下来开始组件间通信方式第二弹------$emit,并讲讲分别在Vue2、Vue3中的表现。 Vue2Vue3组件间通信方式汇总&#xff08;1&#xff0…

《LeetCode力扣练习》代码随想录——双指针法(三数之和---Java)

《LeetCode力扣练习》代码随想录——双指针法&#xff08;三数之和—Java&#xff09; 刷题思路来源于 代码随想录 15. 三数之和 双指针 class Solution {public List<List<Integer>> threeSum(int[] nums) {List<List<Integer>> resultnew ArrayList&…

Debezium日常分享系列之:Debezium 2.5.0.Final发布

Debezium日常分享系列之&#xff1a;Debezium 2.5.0.Final发布 一、重大改变1.MySQL2.MongoDB3.JDBC4.Core 二、改进和变化1.Redis 架构历史重试现在受到限制2.初始快照的附加通知3.重新选择列4.增量快照水印的 INSERT/DELETE 语义5.MongoDB无缝大文档处理6.Mysql 8.2 support7…

1.反射,泛型

1.2.1 反射机制 Java的反射机制是指在程序的运行状态中&#xff0c;可以构造任意一个类的对象&#xff0c;可以了解任意一个对象所属的类&#xff0c;可以了解任意一个类的成员变量和方法&#xff0c;可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象…

JavaEE进阶学习:Spring MVC 程序开发

1.什么是 Spring MVC Spring Web MVC 是基于Servlet API 构建的原始 Web 框架&#xff0c;从一开始就包含在Spring 框架中。它的正式名称 “Spring Web MVC” 来自其源模块的名称(Spring-webmvc)&#xff0c;但它通常被称为“Spring MVC”。 从上述定义我们可以得出两个关键信…

如何改善与 Next Paint (INP) 的交互

但谷歌也会关注访问者到达后你的网站体验有多好。 在过去的几年里&#xff0c;谷歌已经彻底改变了哪些页面体验信号被收集并用作排名因素。 在引入核心网络指标后&#xff0c;谷歌逐渐调整了它们的衡量方式&#xff0c;以便更好地反映真实的用户体验。 然而&#xff0c;随着…

深入了解Pandas的数据类型

大家好&#xff0c;Pandas是一个功能强大的数据处理和分析库&#xff0c;它提供了丰富的数据类型&#xff0c;使得数据操作更加灵活和高效。本文我们将深入了解Pandas的数据类型&#xff0c;包括Series和DataFrame。 1.Series Series是Pandas中最基本的数据类型&#xff0c;它…

Unity中获取时间戳、日期、时间、毫秒、秒以相互转换、自定义格式时间

Unity中获取时间戳、日期、时间、毫秒、秒以相互转换、自定义格式时间 介绍时间戳是什么什么时候用时间戳 获取时间获取当前时间获取时间戳日期转时间戳时间戳转日期将时间戳转换为多久之前星期自定义格式时间 总结 介绍 这里附带一个时间戳和时间转换的网址 时间戳是什么 时…

【Spring实战】01 配置单数据源

文章目录 1. 定义2. 准备3. 打印连接信息4. 实战1&#xff09;创建表2&#xff09;添加数据3&#xff09;查询数据3&#xff09;执行 5. 详细代码总结 在我们常见的应用程序中&#xff0c;与数据库的交互是不可避免的一部分。Spring 提供了简单而强大的数据访问抽象&#xff0c…

九州金榜|家庭教育幼小衔接家长如何做?

孩子从幼儿园升入小学&#xff0c;很多家长会非常忧虑&#xff0c;进入小学便是孩子学校生涯正式开始&#xff0c;这个阶段作为家长会非常焦虑&#xff0c;会考虑孩子能不能适应小学生活&#xff1f;学习跟不跟得上&#xff0c;一般这个时候&#xff0c;大部分家长就会考虑给孩…

数值分析期末复习

第一章 科学计算 误差 解题步骤 先求绝对误差: ∣ x − x ∗ ∣ |x - x^*| ∣x−x∗∣求相对误差限: ∣ x − x ∗ ∣ x ∗ \frac{|x\,\,-\,\,x^*|}{x^*} x∗∣x−x∗∣​求有效数字 ∣ x − x ∗ ∣ 需要小于它自身的半个单位 |x-x^*|\text{需要小于它自身的半个单位} ∣…

Python入门学习篇(六)——for循环while循环

1 for循环 1.1 常规for循环 1.1.1 语法结构 for 变量名 in 可迭代对象:# 遍历对象时执行的代码 else:# 当for循环全部正常运行完(没有报错和执行break)后执行的代码1.1.2 示例代码 print("----->学生检查系统<------") student_lists["张三",&qu…