feat:add lark send message handler

pull/10/head
imyzt 3 years ago
parent 5494c035c6
commit 7915df10a4

@ -32,9 +32,9 @@ import java.util.concurrent.TimeUnit;
@AllArgsConstructor
public class DingSendMessageHandler implements SendMessageHandler {
private String active;
private final String active;
private InstanceInfo instanceInfo;
private final InstanceInfo instanceInfo;
@Override
public String getType() {
@ -68,7 +68,7 @@ public class DingSendMessageHandler implements SendMessageHandler {
"<font color='#708090' size=2>最大线程数:%d</font> \n\n " +
"<font color='#708090' size=2>当前线程数:%d</font> \n\n " +
"<font color='#708090' size=2>活跃线程数:%d</font> \n\n " +
"<font color='#708090' size=2>最大线程数:%d</font> \n\n " +
"<font color='#708090' size=2>最大任务数:%d</font> \n\n " +
"<font color='#708090' size=2>线程池任务总量:%d</font> \n\n " +
" --- \n\n " +
"<font color='#708090' size=2>队列类型:%s</font> \n\n " +
@ -214,7 +214,7 @@ public class DingSendMessageHandler implements SendMessageHandler {
try {
dingTalkClient.execute(request);
} catch (ApiException ex) {
log.error("Ding failed to send message", ex.getMessage());
log.error("Ding failed to send message", ex);
}
}

@ -8,6 +8,14 @@ package cn.hippo4j.starter.alarm;
*/
public enum NotifyPlatformEnum {
DING
/**
*
*/
DING,
/**
*
*/
LARK,
;
}

@ -0,0 +1,30 @@
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
*/
public static final String LARK_AT_FORMAT = "<at email=''>%s</at>";
}

@ -0,0 +1,187 @@
package cn.hippo4j.starter.alarm.lark;
import cn.hippo4j.common.model.InstanceInfo;
import cn.hippo4j.common.model.PoolParameterInfo;
import cn.hippo4j.starter.alarm.NotifyDTO;
import cn.hippo4j.starter.alarm.NotifyPlatformEnum;
import cn.hippo4j.starter.alarm.SendMessageHandler;
import cn.hippo4j.starter.core.DynamicThreadPoolExecutor;
import cn.hippo4j.starter.core.GlobalThreadPoolManage;
import cn.hippo4j.starter.toolkit.thread.QueueTypeEnum;
import cn.hippo4j.starter.toolkit.thread.RejectedTypeEnum;
import cn.hippo4j.starter.wrapper.DynamicThreadPoolWrapper;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ResourceUtils;
import java.io.FileNotFoundException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static cn.hippo4j.starter.alarm.lark.LarkAlarmConstants.*;
/**
* Send lark notification message.
*
* @author imyzt
* @date 2021/11/22 21:12
*/
@Slf4j
@AllArgsConstructor
public class LarkSendMessageHandler implements SendMessageHandler {
private final String active;
private final InstanceInfo instanceInfo;
@Override
public String getType() {
return NotifyPlatformEnum.LARK.name();
}
@Override
public void sendAlarmMessage(NotifyDTO notifyConfig, DynamicThreadPoolExecutor pool) {
larkAlarmSendMessage(notifyConfig, pool);
}
@Override
public void sendChangeMessage(NotifyDTO notifyConfig, PoolParameterInfo parameter) {
larkChangeSendMessage(notifyConfig, parameter);
}
@SneakyThrows
private void larkAlarmSendMessage(NotifyDTO notifyConfig, DynamicThreadPoolExecutor pool) {
String afterReceives = getReceives(notifyConfig);
BlockingQueue<Runnable> queue = pool.getQueue();
String larkAlarmJson = getLarkJson(ALARM_JSON_PATH);
String text = String.format(larkAlarmJson,
// 环境
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.getRejectCount(),
// 告警姓名
afterReceives,
// 当前时间
DateUtil.now(),
// 报警频率
notifyConfig.getInterval()
);
execute(notifyConfig, text);
}
@SneakyThrows
private void larkChangeSendMessage(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 afterReceives = getReceives(notifyConfig);
ThreadPoolExecutor customPool = poolWrap.getExecutor();
String larkNoticeJson = getLarkJson(NOTICE_JSON_PATH);
/**
* hesitant e.g.
*/
String text = String.format(larkNoticeJson,
// 环境
active.toUpperCase(),
// 线程池名称
threadPoolId,
// 应用名称
instanceInfo.getAppName(),
// 实例信息
instanceInfo.getIdentify(),
// 核心线程数
customPool.getCorePoolSize() + " ➲ " + parameter.getCoreSize(),
// 最大线程数
customPool.getMaximumPoolSize() + " ➲ " + parameter.getMaxSize(),
// 线程存活时间
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 String getLarkJson(String filePath) throws FileNotFoundException {
return FileUtil.readString(ResourceUtils.getFile(filePath), StandardCharsets.UTF_8);
}
private String getReceives(NotifyDTO notifyConfig) {
if (StrUtil.isBlank(notifyConfig.getReceives())) {
return "";
}
return Arrays.stream(notifyConfig.getReceives().split(","))
.map(receive -> String.format(LARK_AT_FORMAT, receive))
.collect(Collectors.joining(" "));
}
private void execute(NotifyDTO notifyConfig, String text) {
String serverUrl = LARK_BOT_URL + notifyConfig.getSecretKey();
try {
HttpRequest.post(serverUrl).body(text).execute();
} catch (Exception ex) {
log.error("Lark failed to send message", ex);
}
}
}

@ -2,6 +2,7 @@ package cn.hippo4j.starter.config;
import cn.hippo4j.common.model.InstanceInfo;
import cn.hippo4j.starter.alarm.*;
import cn.hippo4j.starter.alarm.lark.LarkSendMessageHandler;
import cn.hippo4j.starter.remote.HttpAgent;
import lombok.AllArgsConstructor;
import org.apache.logging.log4j.util.Strings;
@ -38,6 +39,12 @@ public class MessageAlarmConfig {
return new DingSendMessageHandler(active, instanceInfo);
}
@Bean
public SendMessageHandler larkSendMessageHandler() {
String active = environment.getProperty("spring.profiles.active", Strings.EMPTY);
return new LarkSendMessageHandler(active, instanceInfo);
}
@Bean
public AlarmControlHandler alarmControlHandler() {
return new AlarmControlHandler();

@ -0,0 +1,185 @@
{
"msg_type": "interactive",
"card": {
"config": {
"wide_screen_mode": true
},
"header": {
"template": "red",
"title": {
"content": "[🔥警报] %s 动态线程池运行告警",
"tag": "plain_text"
}
},
"elements": [
{
"fields": [
{
"is_short": true,
"text": {
"content": "** 线程池ID** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 应用名称:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 应用实例:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 报警类型:** %s",
"tag": "lark_md"
}
}
],
"tag": "div"
},
{
"tag": "hr"
},
{
"fields": [
{
"is_short": true,
"text": {
"content": "** 核心线程数:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 最大线程数:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 当前线程数:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 活跃线程数:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 最大任务数:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 线程池任务总量:** %s",
"tag": "lark_md"
}
}
],
"tag": "div"
},
{
"tag": "hr"
},
{
"fields": [
{
"is_short": true,
"text": {
"content": "** 队列类型:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 队列容量:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 队列元素个数:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 队列剩余个数:** %s",
"tag": "lark_md"
}
}
],
"tag": "div"
},
{
"tag": "hr"
},
{
"fields": [
{
"is_short": true,
"text": {
"content": "** 拒绝策略:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 拒绝策略执行次数:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"user_id": "** OWNER** %s",
"tag": "at"
}
},
{
"is_short": true,
"text": {
"content": "** 播报时间: ** %s",
"tag": "lark_md"
}
}
],
"tag": "div"
},
{
"tag": "hr"
},
{
"tag": "note",
"elements": [
{
"tag": "plain_text",
"content": "提示: %s 分钟内此线程池不会重复告警(可配置)"
}
]
}
]
}
}

@ -0,0 +1,143 @@
{
"msg_type": "interactive",
"card": {
"config": {
"wide_screen_mode": true
},
"header": {
"template": "greed",
"title": {
"content": "[通知] %s 动态线程池参数变更",
"tag": "plain_text"
}
},
"elements": [
{
"fields": [
{
"is_short": true,
"text": {
"content": "** 线程池ID** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 应用名称:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 应用实例:** %s",
"tag": "lark_md"
}
}
],
"tag": "div"
},
{
"tag": "hr"
},
{
"fields": [
{
"is_short": true,
"text": {
"content": "** 核心线程数:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 最大线程数:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 线程存活时间:** %s / SECONDS",
"tag": "lark_md"
}
}
],
"tag": "div"
},
{
"tag": "hr"
},
{
"fields": [
{
"is_short": true,
"text": {
"content": "** 队列类型:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 队列容量:** %s",
"tag": "lark_md"
}
}
],
"tag": "div"
},
{
"tag": "hr"
},
{
"fields": [
{
"is_short": true,
"text": {
"content": "** AGO 拒绝策略:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** NOW 拒绝策略执行次数:** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** OWNER** %s",
"tag": "lark_md"
}
},
{
"is_short": true,
"text": {
"content": "** 播报时间: ** %s",
"tag": "lark_md"
}
}
],
"tag": "div"
},
{
"tag": "hr"
},
{
"tag": "note",
"elements": [
{
"tag": "plain_text",
"content": "提示:动态线程池配置变更实时通知(无限制)"
}
]
}
]
}
}
Loading…
Cancel
Save