项目迁移
This commit is contained in:
26
monitor-spring-boot-starter/pom.xml
Normal file
26
monitor-spring-boot-starter/pom.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>hxuanyu-spring-boot-starter-parent</artifactId>
|
||||
<groupId>com.hxuanyu</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>monitor-spring-boot-starter</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.hxuanyu</groupId>
|
||||
<artifactId>notify-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@@ -0,0 +1,20 @@
|
||||
package com.hxuanyu.monitor.annotation;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 监控项注解,标识在监控项实现类上,带有该注解的类会被扫描并根据配置的时间进行检查
|
||||
*
|
||||
* @author hxuanyu
|
||||
*/
|
||||
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Component
|
||||
public @interface MonitorItem {
|
||||
String name() default "";
|
||||
String cron();
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
package com.hxuanyu.monitor.base;
|
||||
|
||||
|
||||
import com.hxuanyu.monitor.common.CheckResult;
|
||||
|
||||
/**
|
||||
* 监控项模型类
|
||||
*
|
||||
* @author hanxuanyu
|
||||
* @version 1.0
|
||||
*/
|
||||
public abstract class BaseMonitorItem {
|
||||
private String monitorItemName;
|
||||
private String cron;
|
||||
|
||||
public BaseMonitorItem() {
|
||||
}
|
||||
|
||||
public BaseMonitorItem(String monitorItemName, String cron) {
|
||||
this.monitorItemName = monitorItemName;
|
||||
this.cron = cron;
|
||||
}
|
||||
|
||||
public String getMonitorItemName() {
|
||||
return monitorItemName;
|
||||
}
|
||||
|
||||
public void setMonitorItemName(String monitorItemName) {
|
||||
this.monitorItemName = monitorItemName;
|
||||
}
|
||||
|
||||
public String getCron() {
|
||||
return cron;
|
||||
}
|
||||
|
||||
public void setCron(String cron) {
|
||||
this.cron = cron;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 监控项检查器,需子类手动实现触发通知的条件,监控服务会定时调用该方法
|
||||
* 并根据返回结果判断是否通知
|
||||
*
|
||||
* @return true:触发,false:不触发
|
||||
*/
|
||||
public abstract CheckResult check();
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BaseMonitorItem{" +
|
||||
"monitorItemName='" + monitorItemName + '\'' +
|
||||
", cron='" + cron + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@@ -0,0 +1,77 @@
|
||||
package com.hxuanyu.monitor.common;
|
||||
|
||||
import com.hxuanyu.notify.enums.NotifyType;
|
||||
|
||||
/**
|
||||
* 触发器通知
|
||||
*
|
||||
* @author hanxuanyu
|
||||
* @version 1.0
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class CheckResult {
|
||||
private boolean triggered;
|
||||
private Object notifyContent;
|
||||
private NotifyType notifyType;
|
||||
|
||||
public CheckResult() {
|
||||
}
|
||||
|
||||
public CheckResult(boolean triggered, Object notifyContent, NotifyType notifyType) {
|
||||
this.triggered = triggered;
|
||||
this.notifyContent = notifyContent;
|
||||
this.notifyType = notifyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Notify{" +
|
||||
"triggered=" + triggered +
|
||||
", notifyContent='" + notifyContent + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* 未触发,直接调用即可,后续用来判断是否触发
|
||||
*
|
||||
* @return 通知对象
|
||||
*/
|
||||
public static CheckResult nonTriggered() {
|
||||
return new CheckResult(false, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知触发,需要传入通知信息
|
||||
*
|
||||
* @param notifyContent 通知内容
|
||||
* @param notifyType 通知类型
|
||||
* @return 返回结果
|
||||
*/
|
||||
public static CheckResult triggered(Object notifyContent, NotifyType notifyType) {
|
||||
return new CheckResult(true, notifyContent, notifyType);
|
||||
}
|
||||
|
||||
public boolean isTriggered() {
|
||||
return triggered;
|
||||
}
|
||||
|
||||
public void setTriggered(boolean triggered) {
|
||||
this.triggered = triggered;
|
||||
}
|
||||
|
||||
public Object getNotifyContent() {
|
||||
return notifyContent;
|
||||
}
|
||||
|
||||
public void setNotifyContent(Object notifyContent) {
|
||||
this.notifyContent = notifyContent;
|
||||
}
|
||||
|
||||
public NotifyType getNotifyType() {
|
||||
return notifyType;
|
||||
}
|
||||
|
||||
public void setNotifyType(NotifyType notifyType) {
|
||||
this.notifyType = notifyType;
|
||||
}
|
||||
}
|
@@ -0,0 +1,126 @@
|
||||
package com.hxuanyu.monitor.config;
|
||||
|
||||
import com.hxuanyu.monitor.utils.BeanUtils;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.SchedulingException;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
import org.springframework.scheduling.config.TriggerTask;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
/**
|
||||
* 定时任务配置类
|
||||
*
|
||||
* @author hanxuanyu
|
||||
* @version 1.0
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "unused"})
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
public class DefaultSchedulingConfigurer implements SchedulingConfigurer {
|
||||
private ScheduledTaskRegistrar taskRegistrar;
|
||||
private Set<ScheduledFuture<?>> scheduledFutures = null;
|
||||
private final Map<String, ScheduledFuture<?>> taskFutures = new ConcurrentHashMap<>();
|
||||
|
||||
@Resource
|
||||
ScheduledExecutorService scheduledExecutorService;
|
||||
|
||||
@Override
|
||||
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
||||
this.taskRegistrar = taskRegistrar;
|
||||
taskRegistrar.setScheduler(scheduledExecutorService);
|
||||
}
|
||||
|
||||
private Set<ScheduledFuture<?>> getScheduledFutures() {
|
||||
if (scheduledFutures == null) {
|
||||
try {
|
||||
// spring版本不同选用不同字段scheduledFutures
|
||||
scheduledFutures = (Set<ScheduledFuture<?>>) BeanUtils.getProperty(taskRegistrar, "scheduledTasks");
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new SchedulingException("not found scheduledFutures field.");
|
||||
}
|
||||
}
|
||||
return scheduledFutures;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加任务
|
||||
*
|
||||
* @param taskId taskId
|
||||
* @param triggerTask triggerTask
|
||||
*/
|
||||
public void addTriggerTask(String taskId, TriggerTask triggerTask) {
|
||||
if (taskFutures.containsKey(taskId)) {
|
||||
throw new SchedulingException("the taskId[" + taskId + "] was added.");
|
||||
}
|
||||
TaskScheduler scheduler = taskRegistrar.getScheduler();
|
||||
if (scheduler == null) {
|
||||
throw new SchedulingException("scheduler为空");
|
||||
}
|
||||
ScheduledFuture<?> future = scheduler.schedule(triggerTask.getRunnable(), triggerTask.getTrigger());
|
||||
getScheduledFutures().add(future);
|
||||
taskFutures.put(taskId, future);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消任务
|
||||
*
|
||||
* @param taskId taskId
|
||||
*/
|
||||
public void cancelTriggerTask(String taskId) {
|
||||
ScheduledFuture<?> future = taskFutures.get(taskId);
|
||||
if (future != null) {
|
||||
future.cancel(true);
|
||||
}
|
||||
taskFutures.remove(taskId);
|
||||
getScheduledFutures().remove(future);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置任务
|
||||
*
|
||||
* @param taskId taskId
|
||||
* @param triggerTask triggerTask
|
||||
*/
|
||||
public void resetTriggerTask(String taskId, TriggerTask triggerTask) {
|
||||
cancelTriggerTask(taskId);
|
||||
addTriggerTask(taskId, triggerTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务编号
|
||||
*
|
||||
* @return 任务编号列表
|
||||
*/
|
||||
public Set<String> taskIds() {
|
||||
return taskFutures.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在任务
|
||||
*
|
||||
* @param taskId taskId
|
||||
* @return true: 存在任务;false:不存在任务
|
||||
*/
|
||||
public boolean hasTask(String taskId) {
|
||||
return this.taskFutures.containsKey(taskId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务调度是否已经初始化完成
|
||||
*
|
||||
* @return true: 初始化完成;false:初始化未完成
|
||||
*/
|
||||
public boolean inited() {
|
||||
return this.taskRegistrar != null && this.taskRegistrar.getScheduler() != null;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,29 @@
|
||||
package com.hxuanyu.monitor.config;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
/**
|
||||
* MonitorCore配置类
|
||||
*
|
||||
* @author hanxuanyu
|
||||
* @version 1.0
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
@ComponentScan("com.hxuanyu.monitor")
|
||||
public class MonitorCoreConfiguration {
|
||||
@Bean("scheduledThreadPoolExecutor")
|
||||
public ScheduledExecutorService scheduledThreadPoolExecutor() {
|
||||
// 使用 ThreadFactoryBuilder 创建自定义线程名称的 ThreadFactory
|
||||
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
|
||||
.setNameFormat("scheduled-%d").build();
|
||||
return new ScheduledThreadPoolExecutor(10, namedThreadFactory);
|
||||
}
|
||||
}
|
@@ -0,0 +1,98 @@
|
||||
package com.hxuanyu.monitor.manager;
|
||||
|
||||
import com.hxuanyu.monitor.annotation.MonitorItem;
|
||||
import com.hxuanyu.monitor.base.BaseMonitorItem;
|
||||
import com.hxuanyu.monitor.common.CheckResult;
|
||||
import com.hxuanyu.monitor.config.DefaultSchedulingConfigurer;
|
||||
import com.hxuanyu.notify.enums.NotifyType;
|
||||
import com.hxuanyu.notify.service.NotifyService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.scheduling.config.TriggerTask;
|
||||
import org.springframework.scheduling.support.CronTrigger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 当Bean注入成功后对Bean进行拉取,并根据注解的值为Item的属性赋值
|
||||
*
|
||||
* @author hanxuanyu
|
||||
* @version 1.0
|
||||
*/
|
||||
@Component
|
||||
public class MonitorItemBeanManager implements ApplicationListener<ContextRefreshedEvent> {
|
||||
private final Logger logger = LoggerFactory.getLogger(MonitorItemBeanManager.class);
|
||||
private final Map<String, BaseMonitorItem> MONITOR_ITEM_MAP = new HashMap<>();
|
||||
|
||||
@Resource
|
||||
DefaultSchedulingConfigurer schedulingConfigurer;
|
||||
|
||||
@Resource
|
||||
NotifyService notifyService;
|
||||
|
||||
public Map<String, BaseMonitorItem> getMonitorItemMap() {
|
||||
return MONITOR_ITEM_MAP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
// 根容器为Spring容器
|
||||
if (event.getApplicationContext().getParent() == null) {
|
||||
logger.info("=====ContextRefreshedEvent====={}", event.getSource().getClass().getName());
|
||||
Map<String, Object> beans = event.getApplicationContext().getBeansWithAnnotation(MonitorItem.class);
|
||||
for (Object bean : beans.values()) {
|
||||
if (bean instanceof BaseMonitorItem) {
|
||||
BaseMonitorItem item = (BaseMonitorItem) bean;
|
||||
MonitorItem annotation = item.getClass().getAnnotation(MonitorItem.class);
|
||||
if (annotation != null) {
|
||||
String cron = annotation.cron();
|
||||
String name = annotation.name();
|
||||
if ("".equals(name)) {
|
||||
name = item.getClass().getSimpleName();
|
||||
}
|
||||
item.setMonitorItemName(name);
|
||||
item.setCron(cron);
|
||||
logger.info("获取到的Bean:{}", item);
|
||||
addMonitorTask(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addMonitorTask(BaseMonitorItem item) {
|
||||
String taskId = "ScheduledTask-" + item.getMonitorItemName();
|
||||
MONITOR_ITEM_MAP.put(taskId, item);
|
||||
logger.info("添加定时任务:{}, 执行周期:{}", taskId, item.getCron());
|
||||
addTask(taskId, item);
|
||||
}
|
||||
|
||||
public void setMonitorTaskCron(String taskId, String cron) {
|
||||
if (MONITOR_ITEM_MAP.containsKey(taskId)) {
|
||||
BaseMonitorItem item = MONITOR_ITEM_MAP.get(taskId);
|
||||
item.setCron(cron);
|
||||
addTask(taskId, item);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteMonitorTask(String taskId) {
|
||||
MONITOR_ITEM_MAP.remove(taskId);
|
||||
schedulingConfigurer.cancelTriggerTask(taskId);
|
||||
}
|
||||
|
||||
private void addTask(String taskId, BaseMonitorItem item) {
|
||||
String cron = item.getCron();
|
||||
schedulingConfigurer.resetTriggerTask(taskId, new TriggerTask(() -> {
|
||||
CheckResult checkResult = item.check();
|
||||
if (checkResult.isTriggered()) {
|
||||
logger.info("定时任务[{}]触发成功,发送通知:[{}]", taskId, checkResult.getNotifyContent());
|
||||
notifyService.notify(checkResult.getNotifyContent(), NotifyType.MAIL_TYPE);
|
||||
}
|
||||
}, new CronTrigger(cron)));
|
||||
}
|
||||
}
|
@@ -0,0 +1,129 @@
|
||||
package com.hxuanyu.monitor.utils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 反射工具,操作Bean对象的属性
|
||||
*
|
||||
* @author hanxuanyu
|
||||
* @version 1.0
|
||||
*/
|
||||
public class BeanUtils {
|
||||
public static Field findField(Class<?> clazz, String name) {
|
||||
try {
|
||||
return clazz.getField(name);
|
||||
} catch (NoSuchFieldException ex) {
|
||||
return findDeclaredField(clazz, name);
|
||||
}
|
||||
}
|
||||
|
||||
public static Field findDeclaredField(Class<?> clazz, String name) {
|
||||
try {
|
||||
return clazz.getDeclaredField(name);
|
||||
} catch (NoSuchFieldException ex) {
|
||||
if (clazz.getSuperclass() != null) {
|
||||
return findDeclaredField(clazz.getSuperclass(), name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Method findMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
|
||||
try {
|
||||
return clazz.getMethod(methodName, paramTypes);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
return findDeclaredMethod(clazz, methodName, paramTypes);
|
||||
}
|
||||
}
|
||||
|
||||
public static Method findDeclaredMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
|
||||
try {
|
||||
return clazz.getDeclaredMethod(methodName, paramTypes);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
if (clazz.getSuperclass() != null) {
|
||||
return findDeclaredMethod(clazz.getSuperclass(), methodName, paramTypes);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Object getProperty(Object obj, String name) throws NoSuchFieldException {
|
||||
Object value;
|
||||
Field field = findField(obj.getClass(), name);
|
||||
if (field == null) {
|
||||
throw new NoSuchFieldException("no such field [" + name + "]");
|
||||
}
|
||||
boolean accessible = field.isAccessible();
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
value = field.get(obj);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
field.setAccessible(accessible);
|
||||
return value;
|
||||
}
|
||||
|
||||
public static void setProperty(Object obj, String name, Object value) throws NoSuchFieldException {
|
||||
Field field = findField(obj.getClass(), name);
|
||||
if (field == null) {
|
||||
throw new NoSuchFieldException("no such field [" + name + "]");
|
||||
}
|
||||
boolean accessible = field.isAccessible();
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
field.set(obj, value);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
field.setAccessible(accessible);
|
||||
}
|
||||
|
||||
public static Map<String, Object> obj2Map(Object obj, Map<String, Object> map) {
|
||||
if (map == null) {
|
||||
map = new HashMap<>(0);
|
||||
}
|
||||
if (obj != null) {
|
||||
try {
|
||||
Class<?> clazz = obj.getClass();
|
||||
do {
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
for (Field field : fields) {
|
||||
int mod = field.getModifiers();
|
||||
if (Modifier.isStatic(mod)) {
|
||||
continue;
|
||||
}
|
||||
boolean accessible = field.isAccessible();
|
||||
field.setAccessible(true);
|
||||
map.put(field.getName(), field.get(obj));
|
||||
field.setAccessible(accessible);
|
||||
}
|
||||
clazz = clazz.getSuperclass();
|
||||
} while (clazz != null);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得父类集合,包含当前class
|
||||
*
|
||||
* @param clazz 类型泛型
|
||||
* @return 父类集合
|
||||
*/
|
||||
public static List<Class<?>> getSuperclassList(Class<?> clazz) {
|
||||
List<Class<?>> clazzes = new ArrayList<>(3);
|
||||
clazzes.add(clazz);
|
||||
clazz = clazz.getSuperclass();
|
||||
while (clazz != null) {
|
||||
clazzes.add(clazz);
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
return Collections.unmodifiableList(clazzes);
|
||||
}
|
||||
}
|
@@ -0,0 +1 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.hxuanyu.monitor.config.MonitorCoreConfiguration
|
Reference in New Issue
Block a user