前言
相信做java后端的coder都对SpEL有所了解。
本篇blog不是详细介绍SpEL的定义、功能和详细的使用方法的文章,是想和大家分享一下实际业务中遇到的问题和最终使用SpEL做为解决方案的思考过程。
在笔者所处的物联网行业中平台端需要对设备端上报的数据进行监控和告警。告警规则需要支持后台配置,可动态配置告警指标和告警阈值以及告警短信和邮件的消息模板。
这个业务需要中需要动态配置3个数据
- 告警指标
- 告警阈值
- 告警消息模板
1. 告警指标
告警指标由业务决定了其所在领域内的关注点,程序需要能够根据配置取得对应的值
2. 告警阈值
告警阈值决定了告警的触发条件类似于规则引擎中规则命中和工作流引擎中的网关。
会遇到简单的关系运算如 temperature > 40(温度大于40°C触发告警),多条件关系运算 temperature > 25 & humidity > 30 (温度大于25°C并且湿度大于30%触发告警)等业务需求。
3. 告警消息模板
产生了相应的告警事件后需要将告警信息通过短信或者邮件发送出去,根据市场需求平台需要支持客户可以按照自己的行业特性自行定义告警的内容。
基于SpEL强大的运行时执行,解决监控告警需求
解决动态获取告警指标值
SpEL 支持访问属性,数组,集合,可以根据一个特定的对象实例求其内部的属性值
比如需要获取设备上报的电流值
DeviceStatusDTO deviceStatusDto = DeviceStatusDTO.builder()
.ueSn("P004000000")
.productCode("6")
.storeId(20140L)
.ueType(4)
.voltage(30.0)
.electricity(10.0)
.temperature(40.0)
.lastReportTime(new Date())
.build();
ExpressionParser parser = new SpelExpressionParser();
Expression expTargetValue = parser.parseExpression("electricity");
// targetValue 即为deviceStatusDto对象中的electricity属性值
Object targetValue = expTargetValue.getValue(deviceStatusDto);
解决告警阈值即告警规则的命中问题
SpEL 支持关系运算
DeviceStatusDTO deviceStatusDto = DeviceStatusDTO.builder()
.ueSn("P004000000")
.productCode("6")
.storeId(20140L)
.ueType(4)
.voltage(30.0)
.electricity(10.0)
.temperature(40.0)
.lastReportTime(new Date())
.build();
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("temperature > 55");
boolean fire = exp.getValue(deviceStatusDto, Boolean.class);
if(fire) {
// 触发告警逻辑
...
}
告警消息模板
SpEL 支持对象调用等对象操作
DeviceStatusDTO deviceStatusDto = DeviceStatusDTO.builder()
.ueSn("P004000000")
.productCode("6")
.storeId(20140L)
.ueType(4)
.voltage(30.0)
.electricity(10.0)
.temperature(40.0)
.lastReportTime(new Date())
.build();
// 根据SpringEL告警模板生成告警内容
ExpressionParser parser = new SpelExpressionParser();
Expression expAlarmDesc = parser.parseExpression("'温度告警SN:' + ueSn + '温度:' + temperature + '℃'");
String alarmDesc = expAlarmDesc.getValue(deviceStatusDto, String.class);
使用告警配置表存储SpEL表达式满足市场可自由配置化需求
CREATE TABLE `e_ue_alarm_config` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`version` int(10) NOT NULL COMMENT '版本',
`create_time` datetime NOT NULL COMMENT '创建时间',
`modify_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`product_code` varchar(32) NOT NULL COMMENT '产品编码',
`alarm_target` varchar(255) NOT NULL COMMENT '告警指标(SprEL表达式)',
`alarm_condition` varchar(255) NOT NULL COMMENT '告警条件(SprEL表达式)',
`alarm_channel` varchar(255) NOT NULL COMMENT '告警通道 邮件:E_MAIL、App推送: PUSH、短信: SMS ,多个使用,号分割',
`alarm_receiver` varchar(255) NOT NULL COMMENT '告警接收人',
`alarm_template` varchar(500) NOT NULL COMMENT '告警内容模板(SprEL表达式',
`alarm_frequency` int(10) NOT NULL COMMENT '告警频率(告警间隔时间 单位:分钟)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4;