项目迁移

This commit is contained in:
2022-01-21 23:03:41 +08:00
commit bece31356d
30 changed files with 2438 additions and 0 deletions

View 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>

View File

@@ -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();
}

View File

@@ -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 + '\'' +
'}';
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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)));
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.hxuanyu.monitor.config.MonitorCoreConfiguration