SpringBoot + 自定义告警规则 + 企业微信/钉钉通知:异常指标自动推送,运维响应更及时
前言
在企业级应用中,系统的稳定性和可靠性是至关重要的。然而,即使是最精心设计的系统也可能遇到各种异常情况,如服务宕机、数据库连接失败、API 响应超时等。如何及时发现并处理这些异常,成为了运维工作的重要挑战。
想象一下这样的场景:凌晨三点,系统突然出现异常,服务器负载急剧上升,数据库连接池耗尽,导致用户无法正常访问系统。如果运维人员没有及时收到告警通知,问题可能会持续恶化,甚至导致整个系统崩溃,造成严重的业务损失。
自定义告警规则和企业微信/钉钉通知是解决这个问题的有效方案。通过设置自定义告警规则,我们可以实时监控系统的关键指标,当指标超出阈值时,自动通过企业微信或钉钉向运维人员发送通知,确保运维响应更及时。
本文将详细介绍如何在 SpringBoot 项目中实现自定义告警规则和企业微信/钉钉通知,构建一个智能的监控告警系统。
一、告警系统的核心概念
1.1 什么是告警系统
告警系统是一种用于监控系统运行状态、检测异常并及时通知相关人员的系统。它通常包括以下组件:
- 监控数据源:收集系统运行的各种指标数据
- 告警规则:定义什么情况下触发告警
- 告警触发:当指标符合告警规则时触发告警
- 告警通知:将告警信息发送给相关人员
- 告警管理:管理告警的生命周期,包括确认、处理、关闭等
1.2 告警级别
告警级别通常分为以下几类:
| 级别 | 说明 | 处理方式 |
|---|---|---|
| 紧急 | 系统崩溃、服务不可用等严重问题 | 立即通知,24小时内处理 |
| 高 | 系统性能下降、错误率升高等 | 4小时内处理 |
| 中 | 系统警告、资源不足等 | 24小时内处理 |
| 低 | 系统提示、建议等 | 72小时内处理 |
1.3 告警通知方式
常见的告警通知方式包括:
- 企业微信:适合企业内部通知,支持群聊、机器人消息等
- 钉钉:适合企业内部通知,支持群聊、机器人消息等
- 邮件:适合正式通知,详细信息
- 短信:适合紧急通知,快速响应
- 电话:适合非常紧急的情况
二、SpringBoot 告警系统架构
2.1 系统架构
一个完整的 SpringBoot 告警系统通常包括以下组件:
- 数据采集层:收集系统运行的各种指标数据
- 指标处理层:处理和分析采集到的指标数据
- 告警规则层:根据预设的规则判断是否触发告警
- 通知发送层:将告警信息发送给相关人员
- 告警管理层:管理告警的生命周期
2.2 核心流程
- 数据采集:通过各种方式采集系统运行的指标数据
- 指标处理:对采集到的数据进行处理和分析
- 规则匹配:将处理后的数据与告警规则进行匹配
- 告警触发:当数据符合告警规则时,触发告警
- 通知发送:将告警信息通过企业微信/钉钉等方式发送给相关人员
- 告警管理:记录告警信息,支持告警的确认、处理、关闭等操作
三、自定义告警规则实现
3.1 告警规则定义
告警规则通常包括以下要素:
- 规则名称:规则的唯一标识
- 指标名称:要监控的指标名称
- 比较操作:大于、小于、等于、不等于等
- 阈值:触发告警的阈值
- 持续时间:指标持续超过阈值的时间
- 告警级别:告警的严重程度
- 通知方式:告警通知的方式
- 通知接收人:告警通知的接收人
3.2 告警规则配置
@Data
@ConfigurationProperties(prefix = "alert.rule")
public class AlertRuleConfig {
private List<AlertRule> rules = new ArrayList<>();
@Data
public static class AlertRule {
private String name;
private String metric;
private String operation;
private double threshold;
private int durationSeconds;
private String level;
private List<String> notifyMethods;
private List<String> notifyRecipients;
}
}
3.3 告警规则引擎
@Service
public class AlertRuleEngine {
@Autowired
private AlertRuleConfig alertRuleConfig;
@Autowired
private AlertNotificationService alertNotificationService;
private final Map<String, List<AlertRule>> metricRules = new HashMap<>();
@PostConstruct
public void init() {
// 初始化规则映射
for (AlertRule rule : alertRuleConfig.getRules()) {
metricRules.computeIfAbsent(rule.getMetric(), k -> new ArrayList<>()).add(rule);
}
}
public void checkMetric(String metric, double value) {
List<AlertRule> rules = metricRules.get(metric);
if (rules != null) {
for (AlertRule rule : rules) {
if (evaluateRule(rule, value)) {
Alert alert = createAlert(rule, value);
alertNotificationService.sendAlert(alert);
}
}
}
}
private boolean evaluateRule(AlertRule rule, double value) {
switch (rule.getOperation()) {
case ">":
return value > rule.getThreshold();
case ">=":
return value >= rule.getThreshold();
case "<":
return value < rule.getThreshold();
case "<=":
return value <= rule.getThreshold();
case "=":
return value == rule.getThreshold();
default:
return false;
}
}
private Alert createAlert(AlertRule rule, double value) {
Alert alert = new Alert();
alert.setName(rule.getName());
alert.setMetric(rule.getMetric());
alert.setValue(value);
alert.setThreshold(rule.getThreshold());
alert.setLevel(rule.getLevel());
alert.setNotifyMethods(rule.getNotifyMethods());
alert.setNotifyRecipients(rule.getNotifyRecipients());
alert.setCreateTime(Instant.now());
return alert;
}
}
四、企业微信/钉钉通知集成
4.1 企业微信通知
企业微信机器人通知需要先创建一个群聊机器人,获取 webhook 地址。
@Service
public class WeChatNotificationService implements NotificationService {
private static final String WECHAT_API_URL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=%s";
@Autowired
private RestTemplate restTemplate;
@Value("${wechat.webhook.key}")
private String webhookKey;
@Override
public void sendNotification(Alert alert) {
String url = String.format(WECHAT_API_URL, webhookKey);
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("msgtype", "markdown");
Map<String, String> markdown = new HashMap<>();
markdown.put("content", buildWeChatMarkdownContent(alert));
requestBody.put("markdown", markdown);
restTemplate.postForObject(url, requestBody, String.class);
}
private String buildWeChatMarkdownContent(Alert alert) {
StringBuilder content = new StringBuilder();
content.append("## 告警通知\n");
content.append(String.format("**告警名称**:%s\n", alert.getName()));
content.append(String.format("**告警级别**:%s\n", alert.getLevel()));
content.append(String.format("**监控指标**:%s\n", alert.getMetric()));
content.append(String.format("**当前值**:%s\n", alert.getValue()));
content.append(String.format("**阈值**:%s\n", alert.getThreshold()));
content.append(String.format("**告警时间**:%s\n", alert.getCreateTime().toString()));
content.append("**处理建议**:请及时处理,避免影响系统运行\n");
return content.toString();
}
}
4.2 钉钉通知
钉钉机器人通知需要先创建一个群聊机器人,获取 webhook 地址。
@Service
public class DingTalkNotificationService implements NotificationService {
private static final String DINGTALK_API_URL = "https://oapi.dingtalk.com/robot/send?access_token=%s";
@Autowired
private RestTemplate restTemplate;
@Value("${dingtalk.webhook.token}")
private String webhookToken;
@Override
public void sendNotification(Alert alert) {
String url = String.format(DINGTALK_API_URL, webhookToken);
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("msgtype", "markdown");
Map<String, String> markdown = new HashMap<>();
markdown.put("title", "告警通知");
markdown.put("text", buildDingTalkMarkdownContent(alert));
requestBody.put("markdown", markdown);
restTemplate.postForObject(url, requestBody, String.class);
}
private String buildDingTalkMarkdownContent(Alert alert) {
StringBuilder content = new StringBuilder();
content.append("# 告警通知\n");
content.append(String.format("## 告警名称:%s\n", alert.getName()));
content.append(String.format("## 告警级别:%s\n", alert.getLevel()));
content.append(String.format("## 监控指标:%s\n", alert.getMetric()));
content.append(String.format("## 当前值:%s\n", alert.getValue()));
content.append(String.format("## 阈值:%s\n", alert.getThreshold()));
content.append(String.format("## 告警时间:%s\n", alert.getCreateTime().toString()));
content.append("## 处理建议:请及时处理,避免影响系统运行\n");
return content.toString();
}
}
五、告警通知服务
5.1 通知服务接口
public interface NotificationService {
void sendNotification(Alert alert);
}
5.2 告警通知服务
@Service
public class AlertNotificationService {
@Autowired
private WeChatNotificationService weChatNotificationService;
@Autowired
private DingTalkNotificationService dingTalkNotificationService;
public void sendAlert(Alert alert) {
for (String method : alert.getNotifyMethods()) {
switch (method.toLowerCase()) {
case "wechat":
weChatNotificationService.sendNotification(alert);
break;
case "dingtalk":
dingTalkNotificationService.sendNotification(alert);
break;
default:
break;
}
}
}
}
六、指标采集与监控
6.1 指标采集
Spring Boot Actuator 提供了丰富的指标采集功能,我们可以通过扩展来采集自定义指标。
@Service
public class MetricCollector {
@Autowired
private MeterRegistry meterRegistry;
public void collectMetric(String name, double value) {
Counter.builder(name)
.register(meterRegistry)
.increment(value);
Gauge.builder(name + ".gauge", () -> value)
.register(meterRegistry);
}
}
6.2 定时监控
我们可以使用 Spring 的 @Scheduled 注解来定时采集和监控指标。
@Service
@Slf4j
public class MetricMonitor {
@Autowired
private MetricCollector metricCollector;
@Autowired
private AlertRuleEngine alertRuleEngine;
@Scheduled(fixedRate = 5000) // 每 5 秒采集一次
public void collectMetrics() {
// 采集 CPU 使用率
double cpuUsage = getCpuUsage();
metricCollector.collectMetric("system.cpu.usage", cpuUsage);
alertRuleEngine.checkMetric("system.cpu.usage", cpuUsage);
// 采集内存使用率
double memoryUsage = getMemoryUsage();
metricCollector.collectMetric("system.memory.usage", memoryUsage);
alertRuleEngine.checkMetric("system.memory.usage", memoryUsage);
// 采集数据库连接池使用率
double dbPoolUsage = getDbPoolUsage();
metricCollector.collectMetric("database.pool.usage", dbPoolUsage);
alertRuleEngine.checkMetric("database.pool.usage", dbPoolUsage);
// 采集 API 响应时间
double apiResponseTime = getApiResponseTime();
metricCollector.collectMetric("api.response.time", apiResponseTime);
alertRuleEngine.checkMetric("api.response.time", apiResponseTime);
}
private double getCpuUsage() {
// 实现 CPU 使用率采集逻辑
return 0.5; // 模拟值
}
private double getMemoryUsage() {
// 实现内存使用率采集逻辑
return 0.6; // 模拟值
}
private double getDbPoolUsage() {
// 实现数据库连接池使用率采集逻辑
return 0.7; // 模拟值
}
private double getApiResponseTime() {
// 实现 API 响应时间采集逻辑
return 100.0; // 模拟值(毫秒)
}
}
七、告警管理
7.1 告警实体
@Data
public class Alert {
private String id;
private String name;
private String metric;
private double value;
private double threshold;
private String level;
private List<String> notifyMethods;
private List<String> notifyRecipients;
private Instant createTime;
private Instant resolveTime;
private String status; // PENDING, PROCESSING, RESOLVED, CLOSED
private String handler;
private String notes;
}
7.2 告警存储
@Service
public class AlertStorageService {
private final List<Alert> alerts = new ArrayList<>();
public void saveAlert(Alert alert) {
alert.setId(UUID.randomUUID().toString());
alert.setStatus("PENDING");
alerts.add(alert);
}
public List<Alert> getAlerts() {
return alerts;
}
public Alert getAlertById(String id) {
return alerts.stream()
.filter(alert -> alert.getId().equals(id))
.findFirst()
.orElse(null);
}
public void updateAlertStatus(String id, String status) {
Alert alert = getAlertById(id);
if (alert != null) {
alert.setStatus(status);
if ("RESOLVED".equals(status)) {
alert.setResolveTime(Instant.now());
}
}
}
}
7.3 告警管理控制器
@RestController
@RequestMapping("/api/alert")
public class AlertController {
@Autowired
private AlertStorageService alertStorageService;
@GetMapping("/list")
public List<Alert> getAlerts() {
return alertStorageService.getAlerts();
}
@GetMapping("/detail/{id}")
public Alert getAlertDetail(@PathVariable String id) {
return alertStorageService.getAlertById(id);
}
@PostMapping("/process/{id}")
public void processAlert(@PathVariable String id, @RequestBody Map<String, String> body) {
alertStorageService.updateAlertStatus(id, "PROCESSING");
}
@PostMapping("/resolve/{id}")
public void resolveAlert(@PathVariable String id, @RequestBody Map<String, String> body) {
alertStorageService.updateAlertStatus(id, "RESOLVED");
}
@PostMapping("/close/{id}")
public void closeAlert(@PathVariable String id, @RequestBody Map<String, String> body) {
alertStorageService.updateAlertStatus(id, "CLOSED");
}
}
八、完整实现
8.1 项目依赖
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Micrometer -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<!-- Spring Boot Configuration Processor -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
8.2 配置文件
spring:
application:
name: alert-notification-demo
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
# 告警规则配置
alert:
rule:
rules:
- name: CPU 使用率过高
metric: system.cpu.usage
operation: >
threshold: 0.8
durationSeconds: 60
level: 高
notifyMethods:
- wechat
- dingtalk
notifyRecipients:
- admin
- name: 内存使用率过高
metric: system.memory.usage
operation: >
threshold: 0.9
durationSeconds: 60
level: 高
notifyMethods:
- wechat
- dingtalk
notifyRecipients:
- admin
- name: 数据库连接池使用率过高
metric: database.pool.usage
operation: >
threshold: 0.85
durationSeconds: 60
level: 中
notifyMethods:
- wechat
notifyRecipients:
- admin
- name: API 响应时间过长
metric: api.response.time
operation: >
threshold: 500
durationSeconds: 30
level: 中
notifyMethods:
- dingtalk
notifyRecipients:
- admin
# 企业微信配置
wechat:
webhook:
key: your-wechat-webhook-key
# 钉钉配置
dingtalk:
webhook:
token: your-dingtalk-webhook-token
# 定时任务配置
scheduling:
enabled: true
8.3 主应用类
@SpringBootApplication
@EnableScheduling
@EnableConfigurationProperties(AlertRuleConfig.class)
public class AlertNotificationDemoApplication {
public static void main(String[] args) {
SpringApplication.run(AlertNotificationDemoApplication.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
九、最佳实践
9.1 告警规则设计
原则:
- 告警规则应该具体明确,避免模糊不清
- 告警阈值应该根据实际情况设置,避免误报
- 告警级别应该与问题的严重程度相匹配
- 告警通知应该发送给相关的人员
建议:
- 对于关键指标,设置更严格的阈值
- 对于非关键指标,设置更宽松的阈值
- 对于不同级别的告警,使用不同的通知方式
- 定期调整告警规则,适应系统变化
9.2 通知策略
原则:
- 通知应该及时、准确、清晰
- 通知内容应该包含足够的信息,便于运维人员快速理解问题
- 通知方式应该根据告警级别选择
- 避免过多的通知,防止告警疲劳
建议:
- 紧急告警:使用电话 + 短信 + 企业微信/钉钉
- 高告警:使用短信 + 企业微信/钉钉
- 中告警:使用企业微信/钉钉
- 低告警:使用企业微信/钉钉
9.3 告警管理
原则:
- 告警应该有明确的处理流程
- 告警应该有状态跟踪
- 告警应该有历史记录
- 告警应该有统计分析
建议:
- 建立告警处理流程,明确各角色的职责
- 使用告警管理系统,跟踪告警的状态
- 定期分析告警数据,找出系统的薄弱环节
- 对频繁发生的告警进行优化,减少告警次数
9.4 监控指标选择
原则:
- 监控指标应该与业务相关
- 监控指标应该能够反映系统的真实状态
- 监控指标应该易于理解和分析
- 监控指标应该有明确的阈值
建议:
- 系统层面:CPU 使用率、内存使用率、磁盘使用率、网络流量
- 应用层面:响应时间、错误率、QPS、并发数
- 数据库层面:连接池使用率、查询响应时间、慢查询次数
- 业务层面:订单量、注册用户数、转化率
十、总结
自定义告警规则和企业微信/钉钉通知是构建智能监控告警系统的重要组成部分。在实际项目中,我们应该根据系统的特点和业务需求,设计合理的告警规则,选择合适的通知方式,建立完善的告警管理流程,确保系统的稳定性和可靠性。
互动话题:
- 你的项目中是如何实现告警系统的?
- 你认为告警系统最大的挑战是什么?
- 你有遇到过因为告警不及时导致的问题吗?
欢迎在评论区留言讨论!更多技术文章,欢迎关注公众号:服务端技术精选
标题:SpringBoot + 自定义告警规则 + 企业微信/钉钉通知:异常指标自动推送,运维响应更及时
作者:jiangyi
地址:http://jiangyi.space/articles/2026/04/02/1774778917879.html
公众号:服务端技术精选
- 前言
- 一、告警系统的核心概念
- 1.1 什么是告警系统
- 1.2 告警级别
- 1.3 告警通知方式
- 二、SpringBoot 告警系统架构
- 2.1 系统架构
- 2.2 核心流程
- 三、自定义告警规则实现
- 3.1 告警规则定义
- 3.2 告警规则配置
- 3.3 告警规则引擎
- 四、企业微信/钉钉通知集成
- 4.1 企业微信通知
- 4.2 钉钉通知
- 五、告警通知服务
- 5.1 通知服务接口
- 5.2 告警通知服务
- 六、指标采集与监控
- 6.1 指标采集
- 6.2 定时监控
- 七、告警管理
- 7.1 告警实体
- 7.2 告警存储
- 7.3 告警管理控制器
- 八、完整实现
- 8.1 项目依赖
- 8.2 配置文件
- 8.3 主应用类
- 九、最佳实践
- 9.1 告警规则设计
- 9.2 通知策略
- 9.3 告警管理
- 9.4 监控指标选择
- 十、总结
评论