
凌晨 2 点,线上告警暴增,传统运维平台只能看到密密麻麻的日志和告警,根因定位全靠人工翻日志。花了 45 分钟才定位到是一台 Redis 节点内存溢出导致级联故障。那次事故后,我下定决心要搭建一个 AI 辅助的运维平台——让告警自动聚类、根因自动推理、修复建议一键执行。
我负责的运维平台需要对接 40+ 台服务器的监控数据、告警规则和自动化任务,后端架构必须兼顾性能和扩展性。

服务 | 职责 | 关键接口 |
|---|---|---|
AlertService | 告警接收、分级、分发 | POST /api/alerts, GET /api/alerts/{id} |
TaskService | 运维任务创建、调度、追踪 | POST /api/tasks, PUT /api/tasks/{id}/status |
MetricService | 指标采集、存储、查询 | GET /api/metrics/query, WS /ws/metrics |
AuthService | 用户认证、角色鉴权 | POST /api/auth/login, GET /api/auth/permissions |


图:Spring Boot 项目目录结构,清晰的模块分层
为什么告警 API 要单独设计而不是复用通用 CRUD?告警有状态机(待处理→处理中→已解决)、分级策略、聚合逻辑,通用 CRUD 无法满足业务需求。
/**
* 告警控制器
* 支持告警创建、查询、状态流转
*/
@RestController
@RequestMapping("/api/alerts")
@Tag(name = "告警管理", description = "AIOps 告警相关接口")
publicclass AlertController {
@Autowired
private AlertService alertService;
@PostMapping
@Operation(summary = "创建告警")
@PreAuthorize("hasRole('OPERATOR') or hasRole('ADMIN')")
public Result<AlertVO> createAlert(
@RequestBody @Valid AlertCreateDTO dto) {
return Result.success(alertService.createAlert(dto));
}
@GetMapping("/{id}")
@Operation(summary = "查询告警详情")
public Result<AlertVO> getAlert(@PathVariable Long id) {
return Result.success(alertService.getAlert(id));
}
@PutMapping("/{id}/status")
@Operation(summary = "更新告警状态")
@PreAuthorize("hasRole('OPERATOR') or hasRole('ADMIN')")
public Result<AlertVO> updateStatus(
@PathVariable Long id,
@RequestBody @Valid AlertStatusUpdateDTO dto) {
return Result.success(alertService.updateStatus(id, dto));
}
@GetMapping
@Operation(summary = "分页查询告警列表")
public Result<PageResult<AlertVO>> listAlerts(
@Valid AlertQueryDTO query) {
return Result.success(alertService.listAlerts(query));
}
}
为什么任务 API 要支持回调?Hermes Agent 执行任务后需要回调更新状态,否则前端无法实时追踪任务进度。
/**
* 任务控制器
* 支持任务创建、调度、回调通知
*/
@RestController
@RequestMapping("/api/tasks")
publicclass TaskController {
@Autowired
private TaskService taskService;
@PostMapping
@PreAuthorize("hasRole('OPERATOR') or hasRole('ADMIN')")
public Result<TaskVO> createTask(
@RequestBody @Valid TaskCreateDTO dto) {
return Result.success(taskService.createTask(dto));
}
@PostMapping("/callback")
@Operation(summary = "Agent 任务回调接口")
public Result<Void> taskCallback(
@RequestBody TaskCallbackDTO dto,
@RequestHeader("X-Agent-Token") String token) {
taskService.handleCallback(dto, token);
return Result.success();
}
}
规范项 | 要求 | 示例 |
|---|---|---|
URL 命名 | 小写 + 短横线 | /api/alerts, /api/task-templates |
HTTP 方法 | CRUD 对应 | GET 查询, POST 创建, PUT 更新 |
版本管理 | URI 前缀 | /api/v1/alerts, /api/v2/alerts |
分页参数 | 统一格式 | page=1&size=20&sort=createdAt,desc |
错误响应 | 标准结构 | {code, message, details} |
我设计的权限模型包含 4 个角色,覆盖运维团队常见的职责分工:

为什么用方法级鉴权而不是 URL 级?运维平台接口多、权限细,URL 级鉴权配置冗长且难以维护,方法级鉴权更灵活。
/**
* Spring Security 配置
* JWT + RBAC 权限控制
*/
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
publicclass SecurityConfig {
@Autowired
private JwtAuthenticationFilter jwtFilter;
@Bean
public SecurityFilterChain filterChain(
HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(s ->
s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/ws/**").permitAll()
.requestMatchers("/api/**").authenticated()
.anyRequest().denyAll()
)
.addFilterBefore(jwtFilter,
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories
.createDelegatingPasswordEncoder();
}
}
/**
* 权限校验服务
* 支持角色检查和数据范围过滤
*/
@Service
publicclass AuthService {
@Autowired
private UserRoleMapper userRoleMapper;
public boolean hasPermission(Long userId,
String resource, String action) {
List<String> roles = userRoleMapper
.findRolesByUserId(userId);
// 超级管理员直接放行
if (roles.contains("ADMIN")) {
returntrue;
}
// 查询角色-权限映射
return userRoleMapper.existsPermission(
roles, resource, action);
}
/**
* 数据范围过滤
* 运维工程师只能看到自己负责的服务
*/
public List<Long> filterDataScope(Long userId) {
return userRoleMapper.findAccessibleServiceIds(userId);
}
}
为什么用 WebSocket 而不是 SSE?AIOps 场景需要双向通信——前端不仅接收实时告警,还要发送确认、执行等操作,WebSocket 更合适。
/**
* WebSocket 配置
* 支持告警推送和指标实时流
*/
@Configuration
@EnableWebSocketMessageBroker
publicclass WebSocketConfig implements
WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(
MessageBrokerRegistry registry) {
// 服务端推送前缀
registry.enableSimpleBroker("/topic", "/queue");
// 客户端发送前缀
registry.setApplicationDestinationPrefixes("/app");
// 用户点对点推送前缀
registry.setUserDestinationPrefix("/user");
}
@Override
public void registerStompEndpoints(
StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setAllowedOriginPatterns("*")
.withSockJS();
}
}
/**
* 告警推送服务
* 告警创建后自动推送至在线用户
*/
@Service
@Slf4j
publicclass AlertPushService {
@Autowired
private SimpMessagingTemplate messagingTemplate;
/**
* 推送告警给指定角色用户
*/
public void pushAlert(AlertVO alert) {
// 推送到告警主题,所有订阅者收到
messagingTemplate.convertAndSend(
"/topic/alerts", alert);
// P0 级告警额外推送到运维主管
if ("P0".equals(alert.getSeverity())) {
messagingTemplate.convertAndSend(
"/topic/alerts/p0", alert);
log.warn("P0 告警推送: {}", alert.getTitle());
}
}
/**
* 推送指标数据流
*/
public void pushMetrics(MetricVO metric) {
messagingTemplate.convertAndSend(
"/topic/metrics/" + metric.getServiceName(),
metric);
}
}
为什么封装 SDK 而不是直接 HTTP 调用?Agent 连接需要心跳保活、断线重连、Token 刷新,SDK 封装这些底层逻辑后,业务层只需关注任务编排。
/**
* Hermes Agent SDK 客户端
* 封装 Agent 连接、任务下发、回调处理
*/
@Component
@Slf4j
publicclass HermesAgentClient {
privatefinal RestTemplate restTemplate;
privatefinal String agentBaseUrl;
@Value("${hermes.agent.base-url}")
private String baseUrl;
@Value("${hermes.agent.token}")
private String agentToken;
public HermesAgentClient(RestTemplateBuilder builder) {
this.restTemplate = builder
.rootUri(baseUrl)
.defaultHeader("X-Agent-Token", agentToken)
.build();
}
/**
* 下发任务到 Agent
*/
public TaskResult dispatchTask(TaskDispatchDTO dto) {
try {
ResponseEntity<TaskResult> resp = restTemplate
.postForEntity("/api/v1/tasks/dispatch",
dto, TaskResult.class);
return resp.getBody();
} catch (RestClientException e) {
log.error("任务下发失败: {}", e.getMessage());
thrownew ServiceException("任务下发失败");
}
}
/**
* 查询 Agent 健康状态
*/
public AgentHealthVO healthCheck() {
return restTemplate.getForObject(
"/api/v1/health", AgentHealthVO.class);
}
}
现象:前端 Dashboard 每隔 2-3 分钟断开一次 WebSocket,重新连接后丢失中间的告警数据。
原因:阿里云 SLB 默认 TCP 空闲超时 90 秒,长时间无消息时 SLB 主动断开连接。
解决:在服务端增加心跳机制,每 30 秒发送一次 ping:
@Scheduled(fixedRate = 30000)
public void sendHeartbeat() {
messagingTemplate.convertAndSend(
"/topic/heartbeat",
Map.of("ts", System.currentTimeMillis()));
}
提醒:同时调整 SLB 的 TCP 空闲超时为 300 秒,给心跳更多容错空间。
现象:运维主管修改了角色权限后,相应用户仍然报 403,需要重启服务才生效。
原因:权限数据缓存在 Redis 中,修改权限后没有主动刷新缓存。
解决:权限变更时发布缓存失效事件:
@Autowired
private ApplicationEventPublisher eventPublisher;
public void refreshRolePermissions(Long roleId) {
// 清除缓存
redisTemplate.delete("perm:role:" + roleId);
// 发布事件,通知所有实例刷新
eventPublisher.publishEvent(
new PermissionRefreshEvent(roleId));
}
提醒:多实例部署时,确保事件通过 Redis Pub/Sub 同步到所有节点。
现象:日志中发现大量重复的回调请求,部分已关闭的任务被重新打开。
原因:Agent 回调接口仅校验 Token,没有幂等防重机制,网络抖动时 Agent 会重试回调。
解决:增加幂等性校验,基于 taskId + status 生成排重键:
public void handleCallback(TaskCallbackDTO dto, String token) {
String idempotentKey = "callback:"
+ dto.getTaskId() + ":" + dto.getStatus();
// 幂等校验:5 分钟内相同回调只处理一次
Boolean first = redisTemplate.opsForValue()
.setIfAbsent(idempotentKey, "1",
Duration.ofMinutes(5));
if (Boolean.FALSE.equals(first)) {
log.info("忽略重复回调: {}", idempotentKey);
return;
}
// 正常处理回调逻辑
taskService.updateTaskStatus(dto);
}
提醒:Agent SDK 也需要在客户端设置合理的重试间隔(建议 5 秒起步,指数退避)。

图:Swagger OpenAPI 文档,平台所有接口统一管理
接口 | 并发数 | 平均 RT | P99 RT | QPS |
|---|---|---|---|---|
POST /api/alerts | 50 | 35ms | 120ms | 1,200 |
GET /api/alerts | 100 | 22ms | 85ms | 3,800 |
POST /api/tasks | 50 | 48ms | 150ms | 850 |
GET /api/metrics/query | 200 | 65ms | 280ms | 2,500 |
WS /ws/metrics | 50 连接 | - | - | 稳定推送 |
测试环境:阿里云 ECS 2C4G * 2 实例 | MySQL RDS 4C8G | Redis 4G 主从版
为什么需要量化对比?光看 API 性能不够直观,与传统运维平台的对比才能真正体现 AIOps 的价值。以下数据来自同一团队的实测对比。
运维场景 | 传统运维平台 | AIOps 平台 | 提升幅度 |
|---|---|---|---|
告警根因定位 | 45min(人工翻日志) | 3min(AI 自动推理) | 降低 93% |
重复告警处理 | 人工逐条确认 | 自动聚类合并,压缩率 75% | 效率提升 4 倍 |
故障修复 MTTR | 60min | 12min(建议一键执行) | 降低 80% |
巡检报告生成 | 2h/次(手工统计) | 30s 自动生成 | 降低 99.6% |
夜间值班人力 | 2 人轮班 | 1 人 + AI 辅助 | 人力降低 50% |
平台上线后需要监控服务和 API 可用性:
监控项 | 采集方式 | 更新间隔 | 告警阈值 | 严重级别 |
|---|---|---|---|---|
平台 API 可用性 | Prometheus up | 15s | == 0 持续 30s | P0 |
API P99 延迟 | Micrometer metrics | 60s | > 1s | P1 |
WebSocket 连接数 | Spring Actuator | 60s | 峰值 > 200 | P2 |
JVM 堆内存使用率 | Actuator /metrics | 60s | > 85% | P1 |
接口错误率 | 日志聚合 | 60s | > 1% | P1 |
数据库连接池使用率 | HikariCP metrics | 60s | > 80% | P2 |
# Systemd Service 配置 - AIOps 平台生产部署
cat > /etc/systemd/system/aiops-platform.service << 'EOF'
[Unit]
Description=AIOps Platform Backend Service
After=network.target mysql.service redis.service
[Service]
Type=simple
User=aiops
Group=aiops
WorkingDirectory=/opt/aiops-platform
ExecStart=/usr/bin/java -jar \
-Xms4g -Xmx4g \
-XX:+UseG1GC \
-XX:+HeapDumpOnOutOfMemoryError \
-Dspring.profiles.active=production \
/opt/aiops-platform/aiops-backend.jar
ExecStop=/bin/kill -15 $MAINPID
Restart=always
RestartSec=10s
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
EOF
# Crontab 配置 - 平台维护任务
0 3 * * * /opt/scripts/platform_log_rotate.sh # 每天凌晨日志轮转
0 4 * * 0 /opt/scripts/platform_db_backup.sh # 每周日数据库备份
*/5 * * * * /opt/scripts/platform_health_check.sh # 每5分钟健康检查
本文覆盖了 AIOps 平台后端开发的四个核心环节:
适用场景:需要从零搭建 AIOps 平台后端的中小团队
不适用场景:已有成熟运维平台仅需轻度定制的团队
💬 你在搭建运维平台后端时遇到过哪些棘手问题?欢迎评论区交流~ 💡 下期预告:Vue3 打造 AIOps Dashboard——实时监控面板的开发实战,从数据可视化到交互设计全流程拆解。