聊聊PowerJob的Alarmable
序
本文主要研究一下PowerJob的Alarmable
Alarmable
tech/powerjob/server/extension/Alarmable.java
public interface Alarmable {void onFailed(Alarm alarm, List<UserInfoDO> targetUserList);
}
Alarmable接口定义了onFailed方法,其入参为alarm及targetUserList
Alarm
public interface Alarm extends PowerSerializable {String fetchTitle();default String fetchContent() {StringBuilder sb = new StringBuilder();JSONObject content = JSONObject.parseObject(JSONObject.toJSONString(this));content.forEach((key, originWord) -> {sb.append(key).append(": ");String word = String.valueOf(originWord);if (StringUtils.endsWithIgnoreCase(key, "time") || StringUtils.endsWithIgnoreCase(key, "date")) {try {if (originWord instanceof Long) {word = CommonUtils.formatTime((Long) originWord);}}catch (Exception ignore) {}}sb.append(word).append(OmsConstant.LINE_SEPARATOR);});return sb.toString();}
}
Alarm定义了fetchTitle方法,提供了fetchContent默认方法,它有两个实现类分别是JobInstanceAlarm、WorkflowInstanceAlarm
DingTalkAlarmService
tech/powerjob/server/extension/defaultimpl/alarm/impl/DingTalkAlarmService.java
@Slf4j
@Service
@RequiredArgsConstructor
public class DingTalkAlarmService implements Alarmable {private final Environment environment;private Long agentId;private DingTalkUtils dingTalkUtils;private Cache<String, String> mobile2UserIdCache;private static final int CACHE_SIZE = 8192;/*** 防止缓存击穿*/private static final String EMPTY_TAG = "EMPTY";@Overridepublic void onFailed(Alarm alarm, List<UserInfoDO> targetUserList) {if (dingTalkUtils == null) {return;}Set<String> userIds = Sets.newHashSet();targetUserList.forEach(user -> {String phone = user.getPhone();if (StringUtils.isEmpty(phone)) {return;}try {String userId = mobile2UserIdCache.get(phone, () -> {try {return dingTalkUtils.fetchUserIdByMobile(phone);} catch (PowerJobException ignore) {return EMPTY_TAG;} catch (Exception ignore) {return null;}});if (!EMPTY_TAG.equals(userId)) {userIds .add(userId);}}catch (Exception ignore) {}});userIds.remove(null);if (!userIds.isEmpty()) {String userListStr = SJ.COMMA_JOINER.skipNulls().join(userIds);List<DingTalkUtils.MarkdownEntity> markdownEntities = Lists.newLinkedList();markdownEntities.add(new DingTalkUtils.MarkdownEntity("server", NetUtils.getLocalHost()));String content = alarm.fetchContent().replaceAll(OmsConstant.LINE_SEPARATOR, OmsConstant.COMMA);markdownEntities.add(new DingTalkUtils.MarkdownEntity("content", content));try {dingTalkUtils.sendMarkdownAsync(alarm.fetchTitle(), markdownEntities, userListStr, agentId);}catch (Exception e) {log.error("[DingTalkAlarmService] send ding message failed, reason is {}", e.getMessage());}}}@PostConstructpublic void init() {String agentId = environment.getProperty(PowerJobServerConfigKey.DING_AGENT_ID);String appKey = environment.getProperty(PowerJobServerConfigKey.DING_APP_KEY);String appSecret = environment.getProperty(PowerJobServerConfigKey.DING_APP_SECRET);log.info("[DingTalkAlarmService] init with appKey:{},appSecret:{},agentId:{}", appKey, appSecret, agentId);if (StringUtils.isAnyBlank(agentId, appKey, appSecret)) {log.warn("[DingTalkAlarmService] cannot get agentId, appKey, appSecret at the same time, this service is unavailable");return;}if (!StringUtils.isNumeric(agentId)) {log.warn("[DingTalkAlarmService] DingTalkAlarmService is unavailable due to invalid agentId: {}", agentId);return;}this.agentId = Long.valueOf(agentId);dingTalkUtils = new DingTalkUtils(appKey, appSecret);mobile2UserIdCache = CacheBuilder.newBuilder().maximumSize(CACHE_SIZE).softValues().build();log.info("[DingTalkAlarmService] init DingTalkAlarmService successfully!");}}
DingTalkAlarmService实现了Alarmable接口,其onFailed遍历targetUserList获取userId,最后通过dingTalkUtils.sendMarkdownAsync发送
MailAlarmService
tech/powerjob/server/extension/defaultimpl/alarm/impl/MailAlarmService.java
@Slf4j
@Service
public class MailAlarmService implements Alarmable {@Resourceprivate Environment environment;private JavaMailSender javaMailSender;@Value("${spring.mail.username:''}")private String from;@Overridepublic void onFailed(Alarm alarm, List<UserInfoDO> targetUserList) {if (CollectionUtils.isEmpty(targetUserList) || javaMailSender == null || StringUtils.isEmpty(from)) {return;}SimpleMailMessage sm = new SimpleMailMessage();try {sm.setFrom(from);sm.setTo(targetUserList.stream().map(UserInfoDO::getEmail).filter(Objects::nonNull).toArray(String[]::new));sm.setSubject(alarm.fetchTitle());sm.setText(alarm.fetchContent());javaMailSender.send(sm);}catch (Exception e) {log.warn("[MailAlarmService] send mail failed, reason is {}", e.getMessage());}}@Autowired(required = false)public void setJavaMailSender(JavaMailSender javaMailSender) {this.javaMailSender = javaMailSender;}}
MailAlarmService实现了Alarmable接口,其onFailed方法构建SimpleMailMessage,然后通过spring的javaMailSender.send发送
WebHookAlarmService
tech/powerjob/server/extension/defaultimpl/alarm/impl/WebHookAlarmService.java
@Slf4j
@Service
public class WebHookAlarmService implements Alarmable {private static final String HTTP_PROTOCOL_PREFIX = "http://";private static final String HTTPS_PROTOCOL_PREFIX = "https://";@Overridepublic void onFailed(Alarm alarm, List<UserInfoDO> targetUserList) {if (CollectionUtils.isEmpty(targetUserList)) {return;}targetUserList.forEach(user -> {String webHook = user.getWebHook();if (StringUtils.isEmpty(webHook)) {return;}// 自动添加协议头if (!webHook.startsWith(HTTP_PROTOCOL_PREFIX) && !webHook.startsWith(HTTPS_PROTOCOL_PREFIX)) {webHook = HTTP_PROTOCOL_PREFIX + webHook;}MediaType jsonType = MediaType.parse(OmsConstant.JSON_MEDIA_TYPE);RequestBody requestBody = RequestBody.create(jsonType, JSONObject.toJSONString(alarm));try {String response = HttpUtils.post(webHook, requestBody);log.info("[WebHookAlarmService] invoke webhook[url={}] successfully, response is {}", webHook, response);}catch (Exception e) {log.warn("[WebHookAlarmService] invoke webhook[url={}] failed!", webHook, e);}});}
}
WebHookAlarmService实现了Alarmable接口,其onFailed方法遍历targetUserList,挨个执行HttpUtils.post(webHook, requestBody),用的是okhttp3来实现http请求回调
小结
PowerJob的Alarmable接口定义了onFailed方法,其入参为alarm及targetUserList;它有三个实现类,分别是DingTalkAlarmService(用的是DingTalkClient
)、MailAlarmService(用的是spring的JavaMailSender
)、WebHookAlarmService(用的是okhttp3的OkHttpClient
)。
相关文章:
聊聊PowerJob的Alarmable
序 本文主要研究一下PowerJob的Alarmable Alarmable tech/powerjob/server/extension/Alarmable.java public interface Alarmable {void onFailed(Alarm alarm, List<UserInfoDO> targetUserList); }Alarmable接口定义了onFailed方法,其入参为alarm及tar…...

系列三十五、获取Excel中的总记录数
一、获取Excel中的总记录数 1.1、概述 使用EasyExcel开发进行文件上传时,通常会碰到一个问题,那就是Excel中的记录数太多,使用传统的方案进行文件上传,很容易就超时了,这时可以通过对用户上传的Excel中的数量进行限制…...

VMware workstation安装debian-12.1.0虚拟机并配置网络
VMware workstation安装debian-12.1.0虚拟机并配置网络 Debian 是一个完全自由的操作系统!Debian 有一个由普罗大众组成的社区!该文档适用于在VMware workstation平台安装debian-12.1.0虚拟机。 1.安装准备 1.1安装平台 Windows 11 1.2软件信息 软…...

centos下系统全局检测工具dstat使用
目录 一:没有需要安装 二:dstat命令参数 三、监测界面各参数含义(部分) 四、dstat的高级用法 一:没有需要安装 yum install dstat 二:dstat命令参数 有默认选项,执行dstat命令不加任何参数…...
无人机群ros通信
单架无人机与地面站通信 在一个局域网内获取无人机的机载电脑ip 通过地面站ssh到机载电脑,实现通信 多架无人机与地面站通信 在ROS基础上,配置主机和从机,实现主机和从机的话题联通 配置hosts 在主机和从机的/etc/hosts文件中,…...

LeetCode刷题:142. 环形链表 II
题目: 是否独立解决:否,参考了解题思路解决问题,思考了用快慢指针,栈,统计链表数量定位尾巴节点(因为是环形链表所以是死循环,链表数量用while循环统计不出来)都没解决 解…...

Laravel 使用rdkafka_laravel详细教程(实操避坑)
一、选择rdkafka 首先要看版本兼容问题,我的是Laravel5.6,PHP是7.3.13,所以需要下载兼容此的rdkafka,去 Packagist 搜索 kafka ,我用的是 enqueue/rdkafka选择里面0.10.5版本, 二、安装rdkafka 在 Larav…...
439 - Knight Moves (UVA)
题目链接如下: Online Judge UVA439 骑士的移动 - 锦依卫议事堂 - 洛谷博客 这里有好几个特别厉害的解法...先存着慢慢看。 我的代码如下: #include <iostream> #include <deque> #include <string> // #define debugstruct node{…...

数据结构(c)冒泡排序
本文除了最下面的代码是我写的,其余是网上抄写的。 冒泡排序 什么是冒泡排序? 冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交…...

并发编程之并发容器
目录 并发容器 CopyOnWriteArrayList 应用场景 常用方法 读多写少场景使用CopyOnWriteArrayList举例 CopyOnWriteArrayList原理 CopyOnWriteArrayList 的缺陷 扩展迭代器fail-fast与fail-safe机制 ConcurrentHashMap 应用场景 常用方法 并发场景下线程安全举例 Con…...

K8s---存储卷(动态pv和pvc)
当我要发布pvc可以生成pv,还可以共享服务器上直接生成挂载目录。pvc直接绑定pv。 动态pv需要两个组件 1、卷插件:k8s本生支持的动态pv创建不包括nfs,需要声明和安装一个外部插件 Provisioner: 存储分配器。动态创建pv,然后根据pvc的请求自动…...
JS判断对象是否为空对象的几种方法
通过JSON自带的stringify()方法判断 function isEmptyObj(obj) {return JSON.stringify(obj) {} } console.log(对象是否为空:, isEmptyObj({}))for in 循环判断 function isEmptyObj(obj) {for(let item in obj) {return true}return false } console.log(对…...
算法通关村第十五关—用4KB内存寻找重复元素(青铜)
用4KB内存寻找重复元素 用4KB内存寻找重复元素 题目要求:给定一个数组,包含从1到N的整数,N最大为32000,数组可能还有重复值,且N的取值不定,若只有4KB的内存可用,该如何打印数组中所有重复元素。…...
【PHP】判断字符串是否是有效的base64编码
目录 1.检测长度: 2.检查字符集: 3.判断是否能正常解码 在PHP中,判断一个字符串是否是有效的Base64编码,可以通过以下几种方法: 1.检测长度: Base64编码的字符串长度必须是4的倍数(对于标准…...
鼎盛合|测量精度SOC芯片开发中的技术问题整理
SOC芯片近几年的发展势头迅猛,许多行业中俱可见其身影。SOC芯片并不是传统意义上的芯片,它是一个由多种功能集成的一个芯片。SOC芯片自身在出厂时便带有部分程序,是为了方便设计开发而针对某些行业设计的特定功能。如芯海的SOC芯片大多数则是…...

sql | 学生参加各科考试次数
学生表: Students------------------------ | Column Name | Type | ------------------------ | student_id | int | | student_name | varchar | ------------------------ 在 SQL 中,主键为 student_id(学生ID)。 该表内的每…...
uniapp(vue2)+VoerkaI18n多语言
今天我学习了VoerkaI18n国际化插件,它是一个适用于Javascript/Vue/React/Solid/ReactNative的国际化全流程解决方案。VoerkaI18n可以帮助我们轻松地实现应用程序的多语言支持,使得应用程序可以适应不同的语言环境。 比较吸引我的是集成自动翻译,t(“中华…...
C51--测速小车
测速小车: 测速模块: 用途: 广泛用于电机转速检测,脉冲计数,位置限位等。 高低电平: 有遮挡,输出高电平; 无遮挡,输出低电平。 接线: VCC——正极 GND——接…...
ORACLE报错:ORA-04091 表XXX发生了变化,触发器/函数不能读它
ORACLE报错:ORA-04091 表发XXX生了变化,触发器/函数不能读它 问题描述问题分析解决办法拓展:自治事务的特点 问题描述 在开发校验函数FUNCTION的时候,用数据跑批测试的时候报错。经排查这个校验函数FUNCTION的被一个存储过程中的update语句调…...

Arm LDM和STM的寻址方式
A32指令集中包含多数据传输指令LDM和STM,也就是单条指令可以传输多个寄存器的值与内存交互,这对于数据块传输以及寄存器的压入栈很有帮助。LDM和STM指令可分别用于实现堆栈的pop和push操作。对于堆栈操作,基寄存器通常是堆栈指针(SP)。 LDM和…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...

1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...

C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing
Muffin 论文 现有方法 CRADLE 和 LEMON,依赖模型推理阶段输出进行差分测试,但在训练阶段是不可行的,因为训练阶段直到最后才有固定输出,中间过程是不断变化的。API 库覆盖低,因为各个 API 都是在各种具体场景下使用。…...