xxl-job默认的告警通知方式是发送邮件,工作中不易及时的收到通知并作出响应,所以想要调整下告警的通知方式,查阅文档资料,发现可以扩展xxl-job的源码实现支持。
主要思路就是根据xxl-job现有的告警机制,扩展实现企业微信机器人的告警功能,接下来就记录下xxl-job新增企业微信告警通知功能。
从官方网站上下载xxl-job源码,导入IDEA。 官方地址:https://github.com/xuxueli/xxl-job 或 https://gitee.com/xuxueli0323/xxl-job。 xxl-job现有的告警功能。可以发现只要实现JobAlarm这个接口就可以了。
@Component
public class JobAlarmer implements ApplicationContextAware , InitializingBean { private static Logger logger = LoggerFactory . getLogger ( JobAlarmer . class ) ; private ApplicationContext applicationContext; private List < JobAlarm > jobAlarmList; @Override public void setApplicationContext ( ApplicationContext applicationContext) throws BeansException { this . applicationContext = applicationContext; } @Override public void afterPropertiesSet ( ) throws Exception { Map < String , JobAlarm > serviceBeanMap = applicationContext. getBeansOfType ( JobAlarm . class ) ; if ( serviceBeanMap != null && serviceBeanMap. size ( ) > 0 ) { jobAlarmList = new ArrayList < JobAlarm > ( serviceBeanMap. values ( ) ) ; } } public boolean alarm ( XxlJobInfo info, XxlJobLog jobLog) { boolean result = false ; if ( jobAlarmList!= null && jobAlarmList. size ( ) > 0 ) { result = true ; for ( JobAlarm alarm: jobAlarmList) { boolean resultItem = false ; try { resultItem = alarm. doAlarm ( info, jobLog) ; } catch ( Exception e) { logger. error ( e. getMessage ( ) , e) ; } if ( ! resultItem) { result = false ; } } } return result; } }
扩展实现企业微信机器人的告警功能。 在企业微信终端某个群组添加机器人之后,创建者可以在机器人详情页看到该机器人特有的webhookurl。开发者可以向这个地址发起HTTP POST请求,即可实现给该群组发送消息。相关资料可以参考:https://developer.work.weixin.qq.com/document/path/91770 后续可能还会有其他告警通知的需求(比如钉钉告警通知等),就先抽取一个类出来,方便后续扩展。
public abstract class AbstractJobAlarm implements JobAlarm { protected static final Logger logger = LoggerFactory . getLogger ( AbstractJobAlarm . class ) ; protected static final SimpleDateFormat SDF = new SimpleDateFormat ( "yyyy-MM-dd HH:mm:ss" ) ; protected RestTemplate restTemplate = new RestTemplate ( ) ; public abstract String containsKeyword ( ) ; public abstract String genJobAlarmRequestUrl ( String keyword) ; public abstract Object genJobAlarmRequestBody ( XxlJobInfo xxlJobInfo, XxlJobLog xxlJobLog) ; @Override public boolean doAlarm ( XxlJobInfo xxlJobInfo, XxlJobLog xxlJobLog) { if ( null == xxlJobInfo) { return true ; } String alarmEmail = xxlJobInfo. getAlarmEmail ( ) ; if ( null == alarmEmail || "" . equals ( alarmEmail) || ! alarmEmail. contains ( containsKeyword ( ) ) ) { return true ; } boolean alarmResult = true ; Object jobAlarmRequestBody = genJobAlarmRequestBody ( xxlJobInfo, xxlJobLog) ; String [ ] keywordArray = alarmEmail. split ( "," ) ; for ( int i = 0 , len = keywordArray. length; i < len; i++ ) { try { String jobAlarmRequestUrl = genJobAlarmRequestUrl ( keywordArray[ i] ) ; logger. info ( "job alarm request url {} body {}" , jobAlarmRequestUrl, jobAlarmRequestBody) ; restTemplate. postForEntity ( jobAlarmRequestUrl, jobAlarmRequestBody, Object . class ) ; } catch ( Exception e) { logger. error ( ">>>>>>>>>>> xxl-job, job fail alarm send error, job log id:{}" , xxlJobLog. getId ( ) , e) ; alarmResult = false ; } } return alarmResult; } protected String buildJobAlarmContentV1 ( XxlJobInfo xxlJobInfo, XxlJobLog xxlJobLog) { StringBuilder content = new StringBuilder ( ) ; content. append ( "【告警信息】\n" ) ; content. append ( "负责人: " ) . append ( buildAuthor ( xxlJobInfo. getAuthor ( ) ) ) . append ( "\n" ) ; content. append ( "描述: " ) . append ( xxlJobInfo. getJobDesc ( ) ) . append ( "\n" ) ; content. append ( "JOB_ID: " ) . append ( xxlJobLog. getJobId ( ) ) . append ( "\n" ) ; content. append ( "JOB_GROUP: " ) . append ( xxlJobLog. getJobGroup ( ) ) . append ( "\n" ) ; content. append ( "执行器名称: " ) . append ( xxlJobInfo. getExecutorHandler ( ) ) . append ( "\n" ) ; content. append ( "执行器IP: " ) . append ( xxlJobLog. getExecutorAddress ( ) ) . append ( "\n" ) ; content. append ( "任务参数: " ) . append ( xxlJobLog. getExecutorParam ( ) ) . append ( "\n" ) ; content. append ( "告警时间: " ) . append ( SDF . format ( new Date ( ) ) ) . append ( "\n" ) ; content. append ( "LOG_ID: " ) . append ( xxlJobLog. getId ( ) ) . append ( "\n" ) ; Date triggerTime = xxlJobLog. getTriggerTime ( ) ; if ( null != triggerTime) { content. append ( "TRIGGER_TIME: " ) . append ( SDF . format ( triggerTime) ) . append ( "\n" ) ; } String triggerMsg = xxlJobLog. getTriggerMsg ( ) ; if ( null != triggerMsg && ! "" . equals ( triggerMsg. trim ( ) ) ) { content. append ( "TRIGGER_MSG: " ) . append ( triggerMsg. replace ( "<br>" , "\n" ) ) . append ( "\n" ) ; } Date handleTime = xxlJobLog. getHandleTime ( ) ; if ( null != handleTime) { content. append ( "HANDLE_TIME: " ) . append ( SDF . format ( handleTime) ) . append ( "\n" ) ; } String handleMsg = xxlJobLog. getHandleMsg ( ) ; if ( null != handleMsg && ! "" . equals ( handleMsg. trim ( ) ) ) { content. append ( "HANDLE_MSG: " ) . append ( handleMsg) . append ( "\n" ) ; } return content. toString ( ) ; } private String buildAuthor ( String author) { return null == author || "" . equals ( author) ? author : String . join ( "," , Arrays . stream ( author. split ( "," ) ) . map ( v -> "@" + v) . collect ( Collectors . toList ( ) ) ) ; } }
public class QyWeiXinJobAlarm extends AbstractJobAlarm { @Value ( "${qyweixin.robot.webhook.url.prefix:https://qyapi.weixin.qq.com/cgi-bin/webhook/send?}" ) private String webhookUrlPrefix; @Override public String containsKeyword ( ) { return "key" ; } @Override public String genJobAlarmRequestUrl ( String keyword) { return webhookUrlPrefix + keyword; } @Override public Object genJobAlarmRequestBody ( XxlJobInfo xxlJobInfo, XxlJobLog xxlJobLog) { HashMap < String , Object > requestBodyMap = new HashMap < > ( ) ; try { ExecutorBiz executorBiz = XxlJobScheduler . getExecutorBiz ( xxlJobLog. getExecutorAddress ( ) ) ; if ( null != executorBiz) { ReturnT < LogResult > resultReturnT = executorBiz. log ( new LogParam ( xxlJobLog. getTriggerTime ( ) . getTime ( ) , xxlJobLog. getId ( ) , 1 ) ) ; if ( null != resultReturnT) { LogResult logResult = resultReturnT. getContent ( ) ; if ( null != logResult && null != logResult. getLogContent ( ) ) { logger. info ( "return result log {}" , logResult. getLogContent ( ) ) ; } } } } catch ( Exception e) { logger. error ( e. getMessage ( ) , e) ; } requestBodyMap. put ( "msgtype" , "text" ) ; HashMap < String , Object > textMap = new HashMap < > ( ) ; textMap. put ( "content" , buildJobAlarmContentV1 ( xxlJobInfo, xxlJobLog) ) ; String author = xxlJobInfo. getAuthor ( ) ; if ( null != author && ! "" . equals ( author) ) { List < String > authors = Arrays . stream ( author. split ( "," ) ) . collect ( Collectors . toList ( ) ) ; textMap. put ( "mentioned_list" , authors) ; textMap. put ( "mentioned_mobile_list" , authors) ; } requestBodyMap. put ( "text" , textMap) ; return requestBodyMap; } }
自此,企业微信告警通知扩展实现完成,重新打包部署即可。没有调整修改前端的界面,所以只需要在原先的邮件文本框输入key=xxxxxx,key=xxxxxx即可。