mirror of https://github.com/longtai-cn/hippo4j
抽象 hippo4j 核心组件, 不依赖 server 端即可完成动态调参、监控、报警等功能. (#103)
parent
4ae492c8b0
commit
9c0d660ef8
@ -0,0 +1,219 @@
|
|||||||
|
package cn.hippo4j.core.executor;
|
||||||
|
|
||||||
|
import cn.hippo4j.common.notify.HippoSendMessageService;
|
||||||
|
import cn.hippo4j.common.notify.NotifyTypeEnum;
|
||||||
|
import cn.hippo4j.common.notify.ThreadPoolNotifyAlarm;
|
||||||
|
import cn.hippo4j.common.notify.request.AlarmNotifyRequest;
|
||||||
|
import cn.hippo4j.common.notify.request.ChangeParameterNotifyRequest;
|
||||||
|
import cn.hippo4j.core.executor.manage.GlobalNotifyAlarmManage;
|
||||||
|
import cn.hippo4j.core.executor.manage.GlobalThreadPoolManage;
|
||||||
|
import cn.hippo4j.core.toolkit.CalculateUtil;
|
||||||
|
import cn.hippo4j.core.toolkit.IdentifyUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.CommandLineRunner;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread pool alarm notify.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/8/15 14:13
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ThreadPoolNotifyAlarmHandler implements Runnable, CommandLineRunner {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final HippoSendMessageService hippoSendMessageService;
|
||||||
|
|
||||||
|
@Value("${spring.profiles.active:UNKNOWN}")
|
||||||
|
private String active;
|
||||||
|
|
||||||
|
@Value("${spring.dynamic.thread-pool.item-id}")
|
||||||
|
private String itemId;
|
||||||
|
|
||||||
|
@Value("${spring.application.name}")
|
||||||
|
private String applicationName;
|
||||||
|
|
||||||
|
@Value("${spring.dynamic.thread-pool.check-state-interval:5}")
|
||||||
|
private Integer checkStateInterval;
|
||||||
|
|
||||||
|
private final ScheduledExecutorService ALARM_NOTIFY_EXECUTOR = new ScheduledThreadPoolExecutor(
|
||||||
|
1,
|
||||||
|
r -> new Thread(r, "client.alarm.notify")
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(String... args) throws Exception {
|
||||||
|
ALARM_NOTIFY_EXECUTOR.scheduleWithFixedDelay(this, 0, checkStateInterval, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
List<String> listThreadPoolId = GlobalThreadPoolManage.listThreadPoolId();
|
||||||
|
listThreadPoolId.forEach(threadPoolId -> {
|
||||||
|
ThreadPoolNotifyAlarm threadPoolNotifyAlarm = GlobalNotifyAlarmManage.get(threadPoolId);
|
||||||
|
if (threadPoolNotifyAlarm != null && threadPoolNotifyAlarm.getIsAlarm()) {
|
||||||
|
DynamicThreadPoolWrapper wrapper = GlobalThreadPoolManage.getExecutorService(threadPoolId);
|
||||||
|
ThreadPoolExecutor executor = wrapper.getExecutor();
|
||||||
|
checkPoolCapacityAlarm(threadPoolId, executor);
|
||||||
|
checkPoolActivityAlarm(threadPoolId, executor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check thread pool capacity alarm.
|
||||||
|
*
|
||||||
|
* @param threadPoolId
|
||||||
|
* @param threadPoolExecutor
|
||||||
|
*/
|
||||||
|
public void checkPoolCapacityAlarm(String threadPoolId, ThreadPoolExecutor threadPoolExecutor) {
|
||||||
|
if (hippoSendMessageService == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadPoolNotifyAlarm threadPoolNotifyAlarm = GlobalNotifyAlarmManage.get(threadPoolId);
|
||||||
|
BlockingQueue blockIngQueue = threadPoolExecutor.getQueue();
|
||||||
|
|
||||||
|
int queueSize = blockIngQueue.size();
|
||||||
|
int capacity = queueSize + blockIngQueue.remainingCapacity();
|
||||||
|
int divide = CalculateUtil.divide(queueSize, capacity);
|
||||||
|
boolean isSend = threadPoolNotifyAlarm.getIsAlarm()
|
||||||
|
&& divide > threadPoolNotifyAlarm.getCapacityAlarm();
|
||||||
|
if (isSend) {
|
||||||
|
AlarmNotifyRequest alarmNotifyRequest = buildAlarmNotifyReq(threadPoolExecutor);
|
||||||
|
alarmNotifyRequest.setThreadPoolId(threadPoolId);
|
||||||
|
hippoSendMessageService.sendAlarmMessage(NotifyTypeEnum.CAPACITY, alarmNotifyRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check thread pool activity alarm.
|
||||||
|
*
|
||||||
|
* @param threadPoolId
|
||||||
|
* @param threadPoolExecutor
|
||||||
|
*/
|
||||||
|
public void checkPoolActivityAlarm(String threadPoolId, ThreadPoolExecutor threadPoolExecutor) {
|
||||||
|
int activeCount = threadPoolExecutor.getActiveCount();
|
||||||
|
int maximumPoolSize = threadPoolExecutor.getMaximumPoolSize();
|
||||||
|
int divide = CalculateUtil.divide(activeCount, maximumPoolSize);
|
||||||
|
|
||||||
|
ThreadPoolNotifyAlarm threadPoolNotifyAlarm = GlobalNotifyAlarmManage.get(threadPoolId);
|
||||||
|
boolean isSend = threadPoolNotifyAlarm.getIsAlarm()
|
||||||
|
&& divide > threadPoolNotifyAlarm.getLivenessAlarm();
|
||||||
|
if (isSend) {
|
||||||
|
AlarmNotifyRequest alarmNotifyRequest = buildAlarmNotifyReq(threadPoolExecutor);
|
||||||
|
alarmNotifyRequest.setThreadPoolId(threadPoolId);
|
||||||
|
hippoSendMessageService.sendAlarmMessage(NotifyTypeEnum.ACTIVITY, alarmNotifyRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check pool rejected alarm.
|
||||||
|
*
|
||||||
|
* @param threadPoolId
|
||||||
|
*/
|
||||||
|
public void checkPoolRejectedAlarm(String threadPoolId) {
|
||||||
|
ThreadPoolExecutor threadPoolExecutor = GlobalThreadPoolManage.getExecutorService(threadPoolId).getExecutor();
|
||||||
|
checkPoolRejectedAlarm(threadPoolId, threadPoolExecutor);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check pool rejected alarm.
|
||||||
|
*
|
||||||
|
* @param threadPoolId
|
||||||
|
* @param threadPoolExecutor
|
||||||
|
*/
|
||||||
|
public void checkPoolRejectedAlarm(String threadPoolId, ThreadPoolExecutor threadPoolExecutor) {
|
||||||
|
if (threadPoolExecutor instanceof DynamicThreadPoolExecutor) {
|
||||||
|
AlarmNotifyRequest alarmNotifyRequest = buildAlarmNotifyReq(threadPoolExecutor);
|
||||||
|
alarmNotifyRequest.setThreadPoolId(threadPoolId);
|
||||||
|
hippoSendMessageService.sendAlarmMessage(NotifyTypeEnum.REJECT, alarmNotifyRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send pool config change.
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
public void sendPoolConfigChange(ChangeParameterNotifyRequest request) {
|
||||||
|
request.setActive(active.toUpperCase());
|
||||||
|
String appName = StrUtil.isBlank(itemId) ? applicationName : itemId;
|
||||||
|
request.setAppName(appName);
|
||||||
|
request.setIdentify(IdentifyUtil.getIdentify());
|
||||||
|
|
||||||
|
hippoSendMessageService.sendChangeMessage(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build alarm notify req.
|
||||||
|
*
|
||||||
|
* @param threadPoolExecutor
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public AlarmNotifyRequest buildAlarmNotifyReq(ThreadPoolExecutor threadPoolExecutor) {
|
||||||
|
AlarmNotifyRequest request = new AlarmNotifyRequest();
|
||||||
|
|
||||||
|
String appName = StrUtil.isBlank(itemId) ? applicationName : itemId;
|
||||||
|
request.setAppName(appName);
|
||||||
|
|
||||||
|
// 核心线程数
|
||||||
|
int corePoolSize = threadPoolExecutor.getCorePoolSize();
|
||||||
|
// 最大线程数
|
||||||
|
int maximumPoolSize = threadPoolExecutor.getMaximumPoolSize();
|
||||||
|
// 线程池当前线程数 (有锁)
|
||||||
|
int poolSize = threadPoolExecutor.getPoolSize();
|
||||||
|
// 活跃线程数 (有锁)
|
||||||
|
int activeCount = threadPoolExecutor.getActiveCount();
|
||||||
|
// 同时进入池中的最大线程数 (有锁)
|
||||||
|
int largestPoolSize = threadPoolExecutor.getLargestPoolSize();
|
||||||
|
// 线程池中执行任务总数量 (有锁)
|
||||||
|
long completedTaskCount = threadPoolExecutor.getCompletedTaskCount();
|
||||||
|
|
||||||
|
request.setActive(active.toUpperCase());
|
||||||
|
request.setIdentify(IdentifyUtil.getIdentify());
|
||||||
|
request.setCorePoolSize(corePoolSize);
|
||||||
|
request.setMaximumPoolSize(maximumPoolSize);
|
||||||
|
request.setPoolSize(poolSize);
|
||||||
|
request.setActiveCount(activeCount);
|
||||||
|
request.setLargestPoolSize(largestPoolSize);
|
||||||
|
request.setCompletedTaskCount(completedTaskCount);
|
||||||
|
|
||||||
|
BlockingQueue<Runnable> queue = threadPoolExecutor.getQueue();
|
||||||
|
// 队列元素个数
|
||||||
|
int queueSize = queue.size();
|
||||||
|
// 队列类型
|
||||||
|
String queueType = queue.getClass().getSimpleName();
|
||||||
|
// 队列剩余容量
|
||||||
|
int remainingCapacity = queue.remainingCapacity();
|
||||||
|
// 队列容量
|
||||||
|
int queueCapacity = queueSize + remainingCapacity;
|
||||||
|
request.setQueueName(queueType);
|
||||||
|
request.setCapacity(queueCapacity);
|
||||||
|
request.setQueueSize(queueSize);
|
||||||
|
request.setRemainingCapacity(remainingCapacity);
|
||||||
|
|
||||||
|
RejectedExecutionHandler rejectedExecutionHandler = threadPoolExecutor instanceof DynamicThreadPoolExecutor
|
||||||
|
? ((DynamicThreadPoolExecutor) threadPoolExecutor).getRedundancyHandler()
|
||||||
|
: threadPoolExecutor.getRejectedExecutionHandler();
|
||||||
|
request.setRejectedExecutionHandlerName(rejectedExecutionHandler.getClass().getSimpleName());
|
||||||
|
|
||||||
|
long rejectCount = threadPoolExecutor instanceof DynamicThreadPoolExecutor
|
||||||
|
? ((DynamicThreadPoolExecutor) threadPoolExecutor).getRejectCountNum()
|
||||||
|
: -1L;
|
||||||
|
request.setRejectCountNum(rejectCount);
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package cn.hippo4j.core.executor.manage;
|
||||||
|
|
||||||
|
import cn.hippo4j.common.notify.ThreadPoolNotifyAlarm;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global notify alarm manage.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2022/2/24 20:12
|
||||||
|
*/
|
||||||
|
public class GlobalNotifyAlarmManage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify alarm map.
|
||||||
|
*/
|
||||||
|
private static final Map<String, ThreadPoolNotifyAlarm> NOTIFY_ALARM_MAP = new ConcurrentHashMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static ThreadPoolNotifyAlarm get(String key) {
|
||||||
|
return NOTIFY_ALARM_MAP.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param val
|
||||||
|
*/
|
||||||
|
public static void put(String key, ThreadPoolNotifyAlarm val) {
|
||||||
|
NOTIFY_ALARM_MAP.put(key, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package cn.hippo4j.starter.toolkit;
|
package cn.hippo4j.core.toolkit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate util.
|
* Calculate util.
|
@ -1,4 +1,4 @@
|
|||||||
package cn.hippo4j.starter.toolkit.inet;
|
package cn.hippo4j.core.toolkit.inet;
|
||||||
|
|
||||||
import cn.hippo4j.core.executor.support.ThreadPoolBuilder;
|
import cn.hippo4j.core.executor.support.ThreadPoolBuilder;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
@ -1,4 +1,4 @@
|
|||||||
package cn.hippo4j.starter.toolkit.inet;
|
package cn.hippo4j.core.toolkit.inet;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
@ -1,179 +0,0 @@
|
|||||||
package cn.hippo4j.starter.alarm;
|
|
||||||
|
|
||||||
import cn.hippo4j.common.config.ApplicationContextHolder;
|
|
||||||
import cn.hippo4j.common.model.PoolParameterInfo;
|
|
||||||
import cn.hippo4j.common.notify.AlarmControlDTO;
|
|
||||||
import cn.hippo4j.common.notify.AlarmControlHandler;
|
|
||||||
import cn.hippo4j.common.notify.NotifyTypeEnum;
|
|
||||||
import cn.hippo4j.common.toolkit.GroupKey;
|
|
||||||
import cn.hippo4j.common.toolkit.JSONUtil;
|
|
||||||
import cn.hippo4j.common.web.base.Result;
|
|
||||||
import cn.hippo4j.core.executor.DynamicThreadPoolExecutor;
|
|
||||||
import cn.hippo4j.core.executor.manage.GlobalThreadPoolManage;
|
|
||||||
import cn.hippo4j.starter.config.BootstrapProperties;
|
|
||||||
import cn.hippo4j.starter.remote.HttpAgent;
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static cn.hippo4j.common.constant.Constants.BASE_PATH;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base send message service.
|
|
||||||
*
|
|
||||||
* @author chen.ma
|
|
||||||
* @date 2021/8/15 15:34
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class BaseSendMessageService implements InitializingBean, SendMessageService {
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private final HttpAgent httpAgent;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private final BootstrapProperties properties;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private final AlarmControlHandler alarmControlHandler;
|
|
||||||
|
|
||||||
public final static Map<String, List<NotifyDTO>> ALARM_NOTIFY_CONFIG = Maps.newHashMap();
|
|
||||||
|
|
||||||
private final Map<String, SendMessageHandler> sendMessageHandlers = Maps.newHashMap();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendAlarmMessage(NotifyTypeEnum typeEnum, DynamicThreadPoolExecutor executor) {
|
|
||||||
String threadPoolId = executor.getThreadPoolId();
|
|
||||||
String buildKey = StrUtil.builder(executor.getThreadPoolId(), "+", "ALARM").toString();
|
|
||||||
List<NotifyDTO> notifyList = ALARM_NOTIFY_CONFIG.get(buildKey);
|
|
||||||
if (CollUtil.isEmpty(notifyList)) {
|
|
||||||
log.warn("Please configure alarm notification on the server. key :: [{}]", threadPoolId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyList.forEach(each -> {
|
|
||||||
try {
|
|
||||||
SendMessageHandler messageHandler = sendMessageHandlers.get(each.getPlatform());
|
|
||||||
if (messageHandler == null) {
|
|
||||||
log.warn("Please configure alarm notification on the server. key :: [{}]", threadPoolId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSendAlarm(each.getTpId(), each.setTypeEnum(typeEnum))) {
|
|
||||||
messageHandler.sendAlarmMessage(each, executor);
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
log.warn("Failed to send thread pool alarm notification. key :: [{}]", threadPoolId, ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendChangeMessage(PoolParameterInfo parameter) {
|
|
||||||
String threadPoolId = parameter.getTpId();
|
|
||||||
String buildKey = StrUtil.builder(parameter.getTpId(), "+", "CONFIG").toString();
|
|
||||||
List<NotifyDTO> notifyList = ALARM_NOTIFY_CONFIG.get(buildKey);
|
|
||||||
if (CollUtil.isEmpty(notifyList)) {
|
|
||||||
log.warn("Please configure alarm notification on the server. key :: [{}]", threadPoolId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyList.forEach(each -> {
|
|
||||||
try {
|
|
||||||
SendMessageHandler messageHandler = sendMessageHandlers.get(each.getPlatform());
|
|
||||||
if (messageHandler == null) {
|
|
||||||
log.warn("Please configure alarm notification on the server. key :: [{}]", threadPoolId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
messageHandler.sendChangeMessage(each, parameter);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
log.warn("Failed to send thread pool change notification. key :: [{}]", threadPoolId, ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterPropertiesSet() {
|
|
||||||
Map<String, SendMessageHandler> sendMessageHandlerMap =
|
|
||||||
ApplicationContextHolder.getBeansOfType(SendMessageHandler.class);
|
|
||||||
sendMessageHandlerMap.values().forEach(each -> sendMessageHandlers.put(each.getType(), each));
|
|
||||||
|
|
||||||
List<String> threadPoolIds = GlobalThreadPoolManage.listThreadPoolId();
|
|
||||||
if (CollUtil.isEmpty(threadPoolIds)) {
|
|
||||||
log.warn("The client does not have a dynamic thread pool instance configured.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> groupKeys = Lists.newArrayList();
|
|
||||||
threadPoolIds.forEach(each -> {
|
|
||||||
String groupKey = GroupKey.getKeyTenant(each, properties.getItemId(), properties.getNamespace());
|
|
||||||
groupKeys.add(groupKey);
|
|
||||||
});
|
|
||||||
|
|
||||||
Result result = null;
|
|
||||||
try {
|
|
||||||
result = httpAgent.httpPostByDiscovery(BASE_PATH + "/notify/list/config", new ThreadPoolNotifyReqDTO(groupKeys));
|
|
||||||
} catch (Throwable ex) {
|
|
||||||
log.error("Get dynamic thread pool notify configuration error. message :: {}", ex.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result != null && result.isSuccess() && result.getData() != null) {
|
|
||||||
String resultDataStr = JSONUtil.toJSONString(result.getData());
|
|
||||||
List<ThreadPoolNotify> resultData = JSONUtil.parseArray(resultDataStr, ThreadPoolNotify.class);
|
|
||||||
resultData.forEach(each -> ALARM_NOTIFY_CONFIG.put(each.getNotifyKey(), each.getNotifyList()));
|
|
||||||
|
|
||||||
ALARM_NOTIFY_CONFIG.forEach((key, val) ->
|
|
||||||
val.stream().filter(each -> StrUtil.equals("ALARM", each.getType()))
|
|
||||||
.forEach(each -> alarmControlHandler.initCacheAndLock(each.getTpId(), each.getPlatform(), each.getInterval()))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSendAlarm(String threadPoolId, NotifyDTO notifyInfo) {
|
|
||||||
AlarmControlDTO alarmControl = AlarmControlDTO.builder()
|
|
||||||
.threadPool(threadPoolId)
|
|
||||||
.platform(notifyInfo.getPlatform())
|
|
||||||
.typeEnum(notifyInfo.getTypeEnum())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return alarmControlHandler.isSendAlarm(alarmControl);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
static class ThreadPoolNotifyReqDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* groupKeys
|
|
||||||
*/
|
|
||||||
private List<String> groupKeys;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
static class ThreadPoolNotify {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通知 Key
|
|
||||||
*/
|
|
||||||
private String notifyKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 报警配置
|
|
||||||
*/
|
|
||||||
private List<NotifyDTO> notifyList;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package cn.hippo4j.starter.alarm;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message type enum.
|
|
||||||
*
|
|
||||||
* @author chen.ma
|
|
||||||
* @date 2021/8/16 20:50
|
|
||||||
*/
|
|
||||||
public enum MessageTypeEnum {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通知类型
|
|
||||||
*/
|
|
||||||
CHANGE,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 容量报警
|
|
||||||
*/
|
|
||||||
CAPACITY,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 活跃度报警
|
|
||||||
*/
|
|
||||||
LIVENESS,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 拒绝策略报警
|
|
||||||
*/
|
|
||||||
REJECT
|
|
||||||
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
package cn.hippo4j.starter.alarm;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Alarm config.
|
|
||||||
*
|
|
||||||
* @author chen.ma
|
|
||||||
* @date 2021/8/15 16:09
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class NotifyConfig {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* type
|
|
||||||
*/
|
|
||||||
private String type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* url
|
|
||||||
*/
|
|
||||||
private String url;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* token
|
|
||||||
*/
|
|
||||||
private String token;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* receives
|
|
||||||
*/
|
|
||||||
private String receives;
|
|
||||||
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package cn.hippo4j.starter.alarm;
|
|
||||||
|
|
||||||
import cn.hippo4j.common.model.PoolParameterInfo;
|
|
||||||
import cn.hippo4j.core.executor.DynamicThreadPoolExecutor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send message handler.
|
|
||||||
*
|
|
||||||
* @author chen.ma
|
|
||||||
* @date 2021/8/15 15:44
|
|
||||||
*/
|
|
||||||
public interface SendMessageHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get type.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
String getType();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send alarm message.
|
|
||||||
*
|
|
||||||
* @param notifyConfig
|
|
||||||
* @param threadPoolExecutor
|
|
||||||
*/
|
|
||||||
void sendAlarmMessage(NotifyDTO notifyConfig, DynamicThreadPoolExecutor threadPoolExecutor);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send change message.
|
|
||||||
*
|
|
||||||
* @param notifyConfig
|
|
||||||
* @param parameter
|
|
||||||
*/
|
|
||||||
void sendChangeMessage(NotifyDTO notifyConfig, PoolParameterInfo parameter);
|
|
||||||
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
package cn.hippo4j.starter.alarm;
|
|
||||||
|
|
||||||
import cn.hippo4j.common.model.PoolParameterInfo;
|
|
||||||
import cn.hippo4j.common.notify.NotifyTypeEnum;
|
|
||||||
import cn.hippo4j.core.executor.DynamicThreadPoolExecutor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send msg.
|
|
||||||
*
|
|
||||||
* @author chen.ma
|
|
||||||
* @date 2021/8/15 15:31
|
|
||||||
*/
|
|
||||||
public interface SendMessageService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send alarm message.
|
|
||||||
*
|
|
||||||
* @param typeEnum
|
|
||||||
* @param threadPoolExecutor
|
|
||||||
*/
|
|
||||||
void sendAlarmMessage(NotifyTypeEnum typeEnum, DynamicThreadPoolExecutor threadPoolExecutor);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send change message.
|
|
||||||
*
|
|
||||||
* @param parameter
|
|
||||||
*/
|
|
||||||
void sendChangeMessage(PoolParameterInfo parameter);
|
|
||||||
|
|
||||||
}
|
|
@ -1,134 +0,0 @@
|
|||||||
package cn.hippo4j.starter.alarm;
|
|
||||||
|
|
||||||
import cn.hippo4j.common.config.ApplicationContextHolder;
|
|
||||||
import cn.hippo4j.common.model.PoolParameterInfo;
|
|
||||||
import cn.hippo4j.common.notify.NotifyTypeEnum;
|
|
||||||
import cn.hippo4j.core.executor.DynamicThreadPoolExecutor;
|
|
||||||
import cn.hippo4j.starter.config.MessageAlarmConfiguration;
|
|
||||||
import cn.hippo4j.starter.toolkit.CalculateUtil;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Alarm manage.
|
|
||||||
*
|
|
||||||
* @author chen.ma
|
|
||||||
* @date 2021/8/15 14:13
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class ThreadPoolAlarmManage {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送消息
|
|
||||||
*/
|
|
||||||
private static final SendMessageService SEND_MESSAGE_SERVICE;
|
|
||||||
|
|
||||||
static {
|
|
||||||
SEND_MESSAGE_SERVICE = Optional.ofNullable(ApplicationContextHolder.getInstance())
|
|
||||||
.map(each -> each.getBean(MessageAlarmConfiguration.SEND_MESSAGE_BEAN_NAME, SendMessageService.class))
|
|
||||||
.orElse(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check thread pool capacity alarm.
|
|
||||||
*
|
|
||||||
* @param threadPoolExecutor
|
|
||||||
*/
|
|
||||||
public static void checkPoolCapacityAlarm(DynamicThreadPoolExecutor threadPoolExecutor) {
|
|
||||||
if (SEND_MESSAGE_SERVICE == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO 通知改造
|
|
||||||
* https://github.com/acmenlt/dynamic-threadpool/issues/100
|
|
||||||
*/
|
|
||||||
ThreadPoolAlarm threadPoolAlarm = new ThreadPoolAlarm(null, 80, 80);
|
|
||||||
BlockingQueue blockIngQueue = threadPoolExecutor.getQueue();
|
|
||||||
|
|
||||||
int queueSize = blockIngQueue.size();
|
|
||||||
int capacity = queueSize + blockIngQueue.remainingCapacity();
|
|
||||||
int divide = CalculateUtil.divide(queueSize, capacity);
|
|
||||||
boolean isSend = threadPoolAlarm.getIsAlarm()
|
|
||||||
&& divide > threadPoolAlarm.getCapacityAlarm()
|
|
||||||
&& isSendMessage(threadPoolExecutor, MessageTypeEnum.CAPACITY);
|
|
||||||
if (isSend) {
|
|
||||||
SEND_MESSAGE_SERVICE.sendAlarmMessage(NotifyTypeEnum.CAPACITY, threadPoolExecutor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check thread pool activity alarm.
|
|
||||||
*
|
|
||||||
* @param isCore
|
|
||||||
* @param threadPoolExecutor
|
|
||||||
*/
|
|
||||||
public static void checkPoolLivenessAlarm(boolean isCore, DynamicThreadPoolExecutor threadPoolExecutor) {
|
|
||||||
if (isCore || SEND_MESSAGE_SERVICE == null || !isSendMessage(threadPoolExecutor, MessageTypeEnum.LIVENESS)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int activeCount = threadPoolExecutor.getActiveCount();
|
|
||||||
int maximumPoolSize = threadPoolExecutor.getMaximumPoolSize();
|
|
||||||
int divide = CalculateUtil.divide(activeCount, maximumPoolSize);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO 通知改造
|
|
||||||
* https://github.com/acmenlt/dynamic-threadpool/issues/100
|
|
||||||
*/
|
|
||||||
ThreadPoolAlarm threadPoolAlarm = new ThreadPoolAlarm(null, 80, 80);
|
|
||||||
boolean isSend = threadPoolAlarm.getIsAlarm()
|
|
||||||
&& divide > threadPoolAlarm.getLivenessAlarm()
|
|
||||||
&& isSendMessage(threadPoolExecutor, MessageTypeEnum.LIVENESS);
|
|
||||||
if (isSend) {
|
|
||||||
SEND_MESSAGE_SERVICE.sendAlarmMessage(NotifyTypeEnum.ACTIVITY, threadPoolExecutor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check thread pool reject policy alarm.
|
|
||||||
*
|
|
||||||
* @param threadPoolExecutor
|
|
||||||
*/
|
|
||||||
public static void checkPoolRejectAlarm(DynamicThreadPoolExecutor threadPoolExecutor) {
|
|
||||||
if (SEND_MESSAGE_SERVICE == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO 通知改造
|
|
||||||
* https://github.com/acmenlt/dynamic-threadpool/issues/100
|
|
||||||
*/
|
|
||||||
ThreadPoolAlarm threadPoolAlarm = new ThreadPoolAlarm(null, 80, 80);
|
|
||||||
if (threadPoolAlarm.getIsAlarm() && isSendMessage(threadPoolExecutor, MessageTypeEnum.REJECT)) {
|
|
||||||
SEND_MESSAGE_SERVICE.sendAlarmMessage(NotifyTypeEnum.REJECT, threadPoolExecutor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send thread pool configuration change message.
|
|
||||||
*
|
|
||||||
* @param parameter
|
|
||||||
*/
|
|
||||||
public static void sendPoolConfigChange(PoolParameterInfo parameter) {
|
|
||||||
if (SEND_MESSAGE_SERVICE == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SEND_MESSAGE_SERVICE.sendChangeMessage(parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is send message.
|
|
||||||
*
|
|
||||||
* @param threadPoolExecutor
|
|
||||||
* @param typeEnum
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static boolean isSendMessage(DynamicThreadPoolExecutor threadPoolExecutor, MessageTypeEnum typeEnum) {
|
|
||||||
// ignore
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,185 +0,0 @@
|
|||||||
package cn.hippo4j.starter.alarm.ding;
|
|
||||||
|
|
||||||
import cn.hippo4j.common.enums.EnableEnum;
|
|
||||||
import cn.hippo4j.common.model.InstanceInfo;
|
|
||||||
import cn.hippo4j.common.model.PoolParameterInfo;
|
|
||||||
import cn.hippo4j.starter.alarm.NotifyDTO;
|
|
||||||
import cn.hippo4j.common.notify.NotifyPlatformEnum;
|
|
||||||
import cn.hippo4j.starter.alarm.SendMessageHandler;
|
|
||||||
import cn.hippo4j.core.executor.DynamicThreadPoolExecutor;
|
|
||||||
import cn.hippo4j.core.executor.manage.GlobalThreadPoolManage;
|
|
||||||
import cn.hippo4j.core.executor.support.QueueTypeEnum;
|
|
||||||
import cn.hippo4j.core.executor.support.RejectedTypeEnum;
|
|
||||||
import cn.hippo4j.core.executor.DynamicThreadPoolWrapper;
|
|
||||||
import cn.hutool.core.date.DateUtil;
|
|
||||||
import com.dingtalk.api.DefaultDingTalkClient;
|
|
||||||
import com.dingtalk.api.DingTalkClient;
|
|
||||||
import com.dingtalk.api.request.OapiRobotSendRequest;
|
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.taobao.api.ApiException;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static cn.hippo4j.starter.alarm.ding.DingAlarmConstants.DING_ALARM_TXT;
|
|
||||||
import static cn.hippo4j.starter.alarm.ding.DingAlarmConstants.DING_NOTICE_TXT;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send ding notification message.
|
|
||||||
*
|
|
||||||
* @author chen.ma
|
|
||||||
* @date 2021/8/15 15:49
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class DingSendMessageHandler implements SendMessageHandler {
|
|
||||||
|
|
||||||
private final String active;
|
|
||||||
|
|
||||||
private final InstanceInfo instanceInfo;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getType() {
|
|
||||||
return NotifyPlatformEnum.DING.name();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendAlarmMessage(NotifyDTO notifyConfig, DynamicThreadPoolExecutor pool) {
|
|
||||||
dingAlarmSendMessage(notifyConfig, pool);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendChangeMessage(NotifyDTO notifyConfig, PoolParameterInfo parameter) {
|
|
||||||
dingChangeSendMessage(notifyConfig, parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dingAlarmSendMessage(NotifyDTO notifyConfig, DynamicThreadPoolExecutor pool) {
|
|
||||||
String[] receives = notifyConfig.getReceives().split(",");
|
|
||||||
String afterReceives = Joiner.on(", @").join(receives);
|
|
||||||
|
|
||||||
BlockingQueue<Runnable> queue = pool.getQueue();
|
|
||||||
String text = String.format(
|
|
||||||
DING_ALARM_TXT,
|
|
||||||
// 环境
|
|
||||||
active.toUpperCase(),
|
|
||||||
// 线程池ID
|
|
||||||
pool.getThreadPoolId(),
|
|
||||||
// 应用名称
|
|
||||||
instanceInfo.getAppName(),
|
|
||||||
// 实例信息
|
|
||||||
instanceInfo.getIdentify(),
|
|
||||||
// 报警类型
|
|
||||||
notifyConfig.getTypeEnum(),
|
|
||||||
// 核心线程数
|
|
||||||
pool.getCorePoolSize(),
|
|
||||||
// 最大线程数
|
|
||||||
pool.getMaximumPoolSize(),
|
|
||||||
// 当前线程数
|
|
||||||
pool.getPoolSize(),
|
|
||||||
// 活跃线程数
|
|
||||||
pool.getActiveCount(),
|
|
||||||
// 最大任务数
|
|
||||||
pool.getLargestPoolSize(),
|
|
||||||
// 线程池任务总量
|
|
||||||
pool.getCompletedTaskCount(),
|
|
||||||
// 队列类型名称
|
|
||||||
queue.getClass().getSimpleName(),
|
|
||||||
// 队列容量
|
|
||||||
queue.size() + queue.remainingCapacity(),
|
|
||||||
// 队列元素个数
|
|
||||||
queue.size(),
|
|
||||||
// 队列剩余个数
|
|
||||||
queue.remainingCapacity(),
|
|
||||||
// 拒绝策略名称
|
|
||||||
pool.getRejectedExecutionHandler().getClass().getSimpleName(),
|
|
||||||
// 拒绝策略次数
|
|
||||||
pool.getRejectCountNum(),
|
|
||||||
// 告警手机号
|
|
||||||
afterReceives,
|
|
||||||
// 报警频率
|
|
||||||
notifyConfig.getInterval(),
|
|
||||||
// 当前时间
|
|
||||||
DateUtil.now()
|
|
||||||
);
|
|
||||||
|
|
||||||
execute(notifyConfig, DingAlarmConstants.DING_ALARM_TITLE, text, Lists.newArrayList(receives));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dingChangeSendMessage(NotifyDTO notifyConfig, PoolParameterInfo parameter) {
|
|
||||||
String threadPoolId = parameter.getTpId();
|
|
||||||
DynamicThreadPoolWrapper poolWrap = GlobalThreadPoolManage.getExecutorService(threadPoolId);
|
|
||||||
if (poolWrap == null) {
|
|
||||||
log.warn("Thread pool is empty when sending change notification, threadPoolId :: {}", threadPoolId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] receives = notifyConfig.getReceives().split(",");
|
|
||||||
String afterReceives = Joiner.on(", @").join(receives);
|
|
||||||
|
|
||||||
ThreadPoolExecutor customPool = poolWrap.getExecutor();
|
|
||||||
/**
|
|
||||||
* hesitant e.g. ➲ ➜ ⇨ ➪
|
|
||||||
*/
|
|
||||||
String text = String.format(
|
|
||||||
DING_NOTICE_TXT,
|
|
||||||
// 环境
|
|
||||||
active.toUpperCase(),
|
|
||||||
// 线程池名称
|
|
||||||
threadPoolId,
|
|
||||||
// 应用名称
|
|
||||||
instanceInfo.getAppName(),
|
|
||||||
// 实例信息
|
|
||||||
instanceInfo.getIdentify(),
|
|
||||||
// 核心线程数
|
|
||||||
customPool.getCorePoolSize() + " ➲ " + parameter.getCoreSize(),
|
|
||||||
// 最大线程数
|
|
||||||
customPool.getMaximumPoolSize() + " ➲ " + parameter.getMaxSize(),
|
|
||||||
// 核心线程超时
|
|
||||||
customPool.allowsCoreThreadTimeOut() + " ➲ " + EnableEnum.getBool(parameter.getAllowCoreThreadTimeOut()),
|
|
||||||
// 线程存活时间
|
|
||||||
customPool.getKeepAliveTime(TimeUnit.SECONDS) + " ➲ " + parameter.getKeepAliveTime(),
|
|
||||||
// 阻塞队列
|
|
||||||
QueueTypeEnum.getBlockingQueueNameByType(parameter.getQueueType()),
|
|
||||||
// 阻塞队列容量
|
|
||||||
(customPool.getQueue().size() + customPool.getQueue().remainingCapacity()) + " ➲ " + parameter.getCapacity(),
|
|
||||||
// 拒绝策略
|
|
||||||
customPool.getRejectedExecutionHandler().getClass().getSimpleName(),
|
|
||||||
RejectedTypeEnum.getRejectedNameByType(parameter.getRejectedType()),
|
|
||||||
// 告警手机号
|
|
||||||
afterReceives,
|
|
||||||
// 当前时间
|
|
||||||
DateUtil.now()
|
|
||||||
);
|
|
||||||
|
|
||||||
execute(notifyConfig, DingAlarmConstants.DING_NOTICE_TITLE, text, Lists.newArrayList(receives));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void execute(NotifyDTO notifyConfig, String title, String text, List<String> mobiles) {
|
|
||||||
String serverUrl = DingAlarmConstants.DING_ROBOT_SERVER_URL + notifyConfig.getSecretKey();
|
|
||||||
DingTalkClient dingTalkClient = new DefaultDingTalkClient(serverUrl);
|
|
||||||
OapiRobotSendRequest request = new OapiRobotSendRequest();
|
|
||||||
request.setMsgtype("markdown");
|
|
||||||
|
|
||||||
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
|
|
||||||
markdown.setTitle(title);
|
|
||||||
markdown.setText(text);
|
|
||||||
|
|
||||||
OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
|
|
||||||
at.setAtMobiles(mobiles);
|
|
||||||
|
|
||||||
request.setAt(at);
|
|
||||||
request.setMarkdown(markdown);
|
|
||||||
|
|
||||||
try {
|
|
||||||
dingTalkClient.execute(request);
|
|
||||||
} catch (ApiException ex) {
|
|
||||||
log.error("Ding failed to send message", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package cn.hippo4j.starter.alarm.lark;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lark alarm constants.
|
|
||||||
*
|
|
||||||
* @author imyzt
|
|
||||||
* @date 2021-11-23 19:29
|
|
||||||
*/
|
|
||||||
public class LarkAlarmConstants {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lark bot url
|
|
||||||
*/
|
|
||||||
public static final String LARK_BOT_URL = "https://open.feishu.cn/open-apis/bot/v2/hook/";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lark 报警 json文件路径
|
|
||||||
*/
|
|
||||||
public static final String ALARM_JSON_PATH = "classpath:properties/lark/alarm.json";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lark 配置变更通知 json文件路径
|
|
||||||
*/
|
|
||||||
public static final String NOTICE_JSON_PATH = "classpath:properties/lark/notice.json";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lark at format. openid
|
|
||||||
* 当配置openid时,机器人可以@人
|
|
||||||
*/
|
|
||||||
public static final String LARK_AT_FORMAT_OPENID = "<at id='%s'></at>";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lark at format. username
|
|
||||||
* 当配置username时,只能蓝色字体展示@username,被@人无@提醒
|
|
||||||
*/
|
|
||||||
public static final String LARK_AT_FORMAT_USERNAME = "<at id=''>%s</at>";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* lark openid prefix
|
|
||||||
*/
|
|
||||||
public static final String LARK_OPENID_PREFIX = "ou_";
|
|
||||||
}
|
|
@ -1,191 +0,0 @@
|
|||||||
package cn.hippo4j.starter.alarm.wechat;
|
|
||||||
|
|
||||||
import cn.hippo4j.common.enums.EnableEnum;
|
|
||||||
import cn.hippo4j.common.model.InstanceInfo;
|
|
||||||
import cn.hippo4j.common.model.PoolParameterInfo;
|
|
||||||
import cn.hippo4j.common.toolkit.JSONUtil;
|
|
||||||
import cn.hippo4j.starter.alarm.NotifyDTO;
|
|
||||||
import cn.hippo4j.common.notify.NotifyPlatformEnum;
|
|
||||||
import cn.hippo4j.starter.alarm.SendMessageHandler;
|
|
||||||
import cn.hippo4j.core.executor.DynamicThreadPoolExecutor;
|
|
||||||
import cn.hippo4j.core.executor.manage.GlobalThreadPoolManage;
|
|
||||||
import cn.hippo4j.core.executor.support.QueueTypeEnum;
|
|
||||||
import cn.hippo4j.core.executor.support.RejectedTypeEnum;
|
|
||||||
import cn.hippo4j.core.executor.DynamicThreadPoolWrapper;
|
|
||||||
import cn.hutool.core.date.DateUtil;
|
|
||||||
import cn.hutool.http.HttpRequest;
|
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.experimental.Accessors;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import java.util.concurrent.BlockingQueue;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static cn.hippo4j.starter.alarm.wechat.WeChatAlarmConstants.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WeChat send message handler.
|
|
||||||
*
|
|
||||||
* @author chen.ma
|
|
||||||
* @date 2021/11/26 20:06
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class WeChatSendMessageHandler implements SendMessageHandler {
|
|
||||||
|
|
||||||
private final String active;
|
|
||||||
|
|
||||||
private final InstanceInfo instanceInfo;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getType() {
|
|
||||||
return NotifyPlatformEnum.WECHAT.name();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendAlarmMessage(NotifyDTO notifyConfig, DynamicThreadPoolExecutor pool) {
|
|
||||||
String[] receives = notifyConfig.getReceives().split(",");
|
|
||||||
String afterReceives = Joiner.on(", @").join(receives);
|
|
||||||
|
|
||||||
BlockingQueue<Runnable> queue = pool.getQueue();
|
|
||||||
String text = String.format(
|
|
||||||
WE_CHAT_ALARM_TXT,
|
|
||||||
// 环境
|
|
||||||
active.toUpperCase(),
|
|
||||||
// 线程池ID
|
|
||||||
pool.getThreadPoolId(),
|
|
||||||
// 应用名称
|
|
||||||
instanceInfo.getAppName(),
|
|
||||||
// 实例信息
|
|
||||||
instanceInfo.getIdentify(),
|
|
||||||
// 报警类型
|
|
||||||
notifyConfig.getTypeEnum(),
|
|
||||||
// 核心线程数
|
|
||||||
pool.getCorePoolSize(),
|
|
||||||
// 最大线程数
|
|
||||||
pool.getMaximumPoolSize(),
|
|
||||||
// 当前线程数
|
|
||||||
pool.getPoolSize(),
|
|
||||||
// 活跃线程数
|
|
||||||
pool.getActiveCount(),
|
|
||||||
// 最大任务数
|
|
||||||
pool.getLargestPoolSize(),
|
|
||||||
// 线程池任务总量
|
|
||||||
pool.getCompletedTaskCount(),
|
|
||||||
// 队列类型名称
|
|
||||||
queue.getClass().getSimpleName(),
|
|
||||||
// 队列容量
|
|
||||||
queue.size() + queue.remainingCapacity(),
|
|
||||||
// 队列元素个数
|
|
||||||
queue.size(),
|
|
||||||
// 队列剩余个数
|
|
||||||
queue.remainingCapacity(),
|
|
||||||
// 拒绝策略名称
|
|
||||||
pool.getRejectedExecutionHandler().getClass().getSimpleName(),
|
|
||||||
// 拒绝策略次数
|
|
||||||
pool.getRejectCountNum(),
|
|
||||||
// 告警手机号
|
|
||||||
afterReceives,
|
|
||||||
// 报警频率
|
|
||||||
notifyConfig.getInterval(),
|
|
||||||
// 当前时间
|
|
||||||
DateUtil.now()
|
|
||||||
);
|
|
||||||
|
|
||||||
execute(notifyConfig, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendChangeMessage(NotifyDTO notifyConfig, PoolParameterInfo parameter) {
|
|
||||||
String threadPoolId = parameter.getTpId();
|
|
||||||
DynamicThreadPoolWrapper poolWrap = GlobalThreadPoolManage.getExecutorService(threadPoolId);
|
|
||||||
if (poolWrap == null) {
|
|
||||||
log.warn("Thread pool is empty when sending change notification, threadPoolId :: {}", threadPoolId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] receives = notifyConfig.getReceives().split(",");
|
|
||||||
String afterReceives = Joiner.on("><@").join(receives);
|
|
||||||
|
|
||||||
ThreadPoolExecutor customPool = poolWrap.getExecutor();
|
|
||||||
String text = String.format(
|
|
||||||
WE_CHAT_NOTICE_TXT,
|
|
||||||
// 环境
|
|
||||||
active.toUpperCase(),
|
|
||||||
// 线程池名称
|
|
||||||
threadPoolId,
|
|
||||||
// 应用名称
|
|
||||||
instanceInfo.getAppName(),
|
|
||||||
// 实例信息
|
|
||||||
instanceInfo.getIdentify(),
|
|
||||||
// 核心线程数
|
|
||||||
customPool.getCorePoolSize() + " ➲ " + parameter.getCoreSize(),
|
|
||||||
// 最大线程数
|
|
||||||
customPool.getMaximumPoolSize() + " ➲ " + parameter.getMaxSize(),
|
|
||||||
// 核心线程超时
|
|
||||||
customPool.allowsCoreThreadTimeOut() + " ➲ " + EnableEnum.getBool(parameter.getAllowCoreThreadTimeOut()),
|
|
||||||
// 线程存活时间
|
|
||||||
customPool.getKeepAliveTime(TimeUnit.SECONDS) + " ➲ " + parameter.getKeepAliveTime(),
|
|
||||||
// 阻塞队列
|
|
||||||
QueueTypeEnum.getBlockingQueueNameByType(parameter.getQueueType()),
|
|
||||||
// 阻塞队列容量
|
|
||||||
(customPool.getQueue().size() + customPool.getQueue().remainingCapacity()) + " ➲ " + parameter.getCapacity(),
|
|
||||||
// 拒绝策略
|
|
||||||
customPool.getRejectedExecutionHandler().getClass().getSimpleName(),
|
|
||||||
RejectedTypeEnum.getRejectedNameByType(parameter.getRejectedType()),
|
|
||||||
// 告警手机号
|
|
||||||
afterReceives,
|
|
||||||
// 当前时间
|
|
||||||
DateUtil.now()
|
|
||||||
);
|
|
||||||
|
|
||||||
execute(notifyConfig, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void execute(NotifyDTO notifyConfig, String text) {
|
|
||||||
String serverUrl = WE_CHAT_SERVER_URL + notifyConfig.getSecretKey();
|
|
||||||
|
|
||||||
try {
|
|
||||||
WeChatReqDTO weChatReq = new WeChatReqDTO();
|
|
||||||
weChatReq.setMsgtype("markdown");
|
|
||||||
|
|
||||||
Markdown markdown = new Markdown();
|
|
||||||
markdown.setContent(text);
|
|
||||||
weChatReq.setMarkdown(markdown);
|
|
||||||
|
|
||||||
HttpRequest.post(serverUrl).body(JSONUtil.toJSONString(weChatReq)).execute();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
log.error("WeChat failed to send message", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@Accessors(chain = true)
|
|
||||||
public static class WeChatReqDTO {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* msgType
|
|
||||||
*/
|
|
||||||
private String msgtype;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* markdown
|
|
||||||
*/
|
|
||||||
private Markdown markdown;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class Markdown {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* content
|
|
||||||
*/
|
|
||||||
private String content;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package cn.hippo4j.starter.config;
|
|
||||||
|
|
||||||
import cn.hippo4j.common.model.InstanceInfo;
|
|
||||||
import cn.hippo4j.common.notify.AlarmControlHandler;
|
|
||||||
import cn.hippo4j.starter.alarm.BaseSendMessageService;
|
|
||||||
import cn.hippo4j.starter.alarm.SendMessageHandler;
|
|
||||||
import cn.hippo4j.starter.alarm.SendMessageService;
|
|
||||||
import cn.hippo4j.starter.alarm.ding.DingSendMessageHandler;
|
|
||||||
import cn.hippo4j.starter.alarm.lark.LarkSendMessageHandler;
|
|
||||||
import cn.hippo4j.starter.alarm.wechat.WeChatSendMessageHandler;
|
|
||||||
import cn.hippo4j.starter.remote.HttpAgent;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.DependsOn;
|
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message alarm config.
|
|
||||||
*
|
|
||||||
* @author chen.ma
|
|
||||||
* @date 2021/8/15 15:39
|
|
||||||
*/
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class MessageAlarmConfiguration {
|
|
||||||
|
|
||||||
private final BootstrapProperties properties;
|
|
||||||
|
|
||||||
private final InstanceInfo instanceInfo;
|
|
||||||
|
|
||||||
private ConfigurableEnvironment environment;
|
|
||||||
|
|
||||||
public static final String ACTIVE_DEFAULT = "unknown";
|
|
||||||
|
|
||||||
public static final String SEND_MESSAGE_BEAN_NAME = "hippo4JSendMessageService";
|
|
||||||
|
|
||||||
@DependsOn("hippo4JApplicationContextHolder")
|
|
||||||
@Bean(MessageAlarmConfiguration.SEND_MESSAGE_BEAN_NAME)
|
|
||||||
public SendMessageService hippo4JSendMessageService(HttpAgent httpAgent, AlarmControlHandler alarmControlHandler) {
|
|
||||||
return new BaseSendMessageService(httpAgent, properties, alarmControlHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SendMessageHandler dingSendMessageHandler() {
|
|
||||||
String active = environment.getProperty("spring.profiles.active", ACTIVE_DEFAULT);
|
|
||||||
return new DingSendMessageHandler(active, instanceInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SendMessageHandler larkSendMessageHandler() {
|
|
||||||
String active = environment.getProperty("spring.profiles.active", ACTIVE_DEFAULT);
|
|
||||||
return new LarkSendMessageHandler(active, instanceInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public SendMessageHandler weChatSendMessageHandler() {
|
|
||||||
String active = environment.getProperty("spring.profiles.active", ACTIVE_DEFAULT);
|
|
||||||
return new WeChatSendMessageHandler(active, instanceInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public AlarmControlHandler alarmControlHandler() {
|
|
||||||
return new AlarmControlHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,66 @@
|
|||||||
|
package cn.hippo4j.starter.config;
|
||||||
|
|
||||||
|
import cn.hippo4j.common.notify.*;
|
||||||
|
import cn.hippo4j.common.notify.platform.DingSendMessageHandler;
|
||||||
|
import cn.hippo4j.common.notify.platform.LarkSendMessageHandler;
|
||||||
|
import cn.hippo4j.common.notify.platform.WeChatSendMessageHandler;
|
||||||
|
import cn.hippo4j.core.executor.ThreadPoolNotifyAlarmHandler;
|
||||||
|
import cn.hippo4j.core.refresh.ThreadPoolDynamicRefresh;
|
||||||
|
import cn.hippo4j.starter.notify.ServerNotifyConfigBuilder;
|
||||||
|
import cn.hippo4j.starter.remote.HttpAgent;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message notify config.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2021/8/15 15:39
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class MessageNotifyConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public AlarmControlHandler alarmControlHandler() {
|
||||||
|
return new AlarmControlHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public NotifyConfigBuilder notifyConfigBuilder(HttpAgent httpAgent,
|
||||||
|
BootstrapProperties properties,
|
||||||
|
AlarmControlHandler alarmControlHandler) {
|
||||||
|
return new ServerNotifyConfigBuilder(httpAgent, properties, alarmControlHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public HippoSendMessageService hippoSendMessageService(NotifyConfigBuilder notifyConfigBuilder,
|
||||||
|
AlarmControlHandler alarmControlHandler) {
|
||||||
|
return new BaseSendMessageServiceImpl(notifyConfigBuilder, alarmControlHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ThreadPoolNotifyAlarmHandler threadPoolNotifyAlarmHandler(HippoSendMessageService hippoSendMessageService) {
|
||||||
|
return new ThreadPoolNotifyAlarmHandler(hippoSendMessageService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SendMessageHandler dingSendMessageHandler() {
|
||||||
|
return new DingSendMessageHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SendMessageHandler larkSendMessageHandler() {
|
||||||
|
return new LarkSendMessageHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SendMessageHandler weChatSendMessageHandler() {
|
||||||
|
return new WeChatSendMessageHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ThreadPoolDynamicRefresh threadPoolDynamicRefresh(ThreadPoolNotifyAlarmHandler threadPoolNotifyAlarmHandler) {
|
||||||
|
return new ThreadPoolDynamicRefresh(threadPoolNotifyAlarmHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
package cn.hippo4j.starter.core;
|
|
||||||
|
|
||||||
import cn.hippo4j.core.refresh.ThreadPoolDynamicRefresh;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Config adapter.
|
|
||||||
*
|
|
||||||
* @author chen.ma
|
|
||||||
* @date 2021/6/22 21:29
|
|
||||||
*/
|
|
||||||
public class ConfigAdapter {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback Config.
|
|
||||||
*
|
|
||||||
* @param config
|
|
||||||
*/
|
|
||||||
public void callbackConfig(String config) {
|
|
||||||
ThreadPoolDynamicRefresh.refreshDynamicPool(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,78 @@
|
|||||||
|
package cn.hippo4j.starter.notify;
|
||||||
|
|
||||||
|
import cn.hippo4j.common.notify.AlarmControlHandler;
|
||||||
|
import cn.hippo4j.common.notify.NotifyConfigBuilder;
|
||||||
|
import cn.hippo4j.common.notify.NotifyConfigDTO;
|
||||||
|
import cn.hippo4j.common.notify.ThreadPoolNotifyDTO;
|
||||||
|
import cn.hippo4j.common.notify.request.ThreadPoolNotifyRequest;
|
||||||
|
import cn.hippo4j.common.toolkit.GroupKey;
|
||||||
|
import cn.hippo4j.common.toolkit.JSONUtil;
|
||||||
|
import cn.hippo4j.common.web.base.Result;
|
||||||
|
import cn.hippo4j.core.executor.manage.GlobalThreadPoolManage;
|
||||||
|
import cn.hippo4j.starter.config.BootstrapProperties;
|
||||||
|
import cn.hippo4j.starter.remote.HttpAgent;
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static cn.hippo4j.common.constant.Constants.BASE_PATH;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server notify config builder.
|
||||||
|
*
|
||||||
|
* @author chen.ma
|
||||||
|
* @date 2022/2/24 19:57
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class ServerNotifyConfigBuilder implements NotifyConfigBuilder {
|
||||||
|
|
||||||
|
private final HttpAgent httpAgent;
|
||||||
|
|
||||||
|
private final BootstrapProperties properties;
|
||||||
|
|
||||||
|
private final AlarmControlHandler alarmControlHandler;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<NotifyConfigDTO>> buildNotify() {
|
||||||
|
Map<String, List<NotifyConfigDTO>> resultMap = Maps.newHashMap();
|
||||||
|
List<String> threadPoolIds = GlobalThreadPoolManage.listThreadPoolId();
|
||||||
|
if (CollUtil.isEmpty(threadPoolIds)) {
|
||||||
|
log.warn("The client does not have a dynamic thread pool instance configured.");
|
||||||
|
return resultMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> groupKeys = Lists.newArrayList();
|
||||||
|
threadPoolIds.forEach(each -> {
|
||||||
|
String groupKey = GroupKey.getKeyTenant(each, properties.getItemId(), properties.getNamespace());
|
||||||
|
groupKeys.add(groupKey);
|
||||||
|
});
|
||||||
|
|
||||||
|
Result result = null;
|
||||||
|
try {
|
||||||
|
result = httpAgent.httpPostByDiscovery(BASE_PATH + "/notify/list/config", new ThreadPoolNotifyRequest(groupKeys));
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
log.error("Get dynamic thread pool notify configuration error. message :: {}", ex.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != null && result.isSuccess() && result.getData() != null) {
|
||||||
|
String resultDataStr = JSONUtil.toJSONString(result.getData());
|
||||||
|
List<ThreadPoolNotifyDTO> resultData = JSONUtil.parseArray(resultDataStr, ThreadPoolNotifyDTO.class);
|
||||||
|
resultData.forEach(each -> resultMap.put(each.getNotifyKey(), each.getNotifyList()));
|
||||||
|
|
||||||
|
resultMap.forEach((key, val) ->
|
||||||
|
val.stream().filter(each -> StrUtil.equals("ALARM", each.getType()))
|
||||||
|
.forEach(each -> alarmControlHandler.initCacheAndLock(each.getThreadPoolId(), each.getPlatform(), each.getInterval()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in new issue