7.9 KiB
7.9 KiB
Bug修复详细记录
ADXP的Docker容器无法关闭的问题
问题描述
现象:
- 在 Ubuntu24.04 系统中部署的 ADXP 的 Docker 容器,启动后无法用 docker compose down 关闭
- 错误提示:Error response from daemon: cannot stop container: adxp-adapter: permission denied
错误原因
- 由于apparmor软件的行为导致
修复方法
sudo apt purge --auto-remove apparmor
sudo service docker restart
docker system prune --all --volumes
** 其他可能的方案 **
sudo systemctl restart docker.socket docker.service
sudo docker image rm -f $(sudo docker image ls -q)
航班进出港通知定时任务不执行问题
问题描述
现象:
- 新增了航班进出港接口实现,但在日志中没有看到任何访问信息
- 服务端没有收到任何请求
- 其他所有定时任务接口都工作正常,只有航班进出港接口完全没有启动
预期行为:
- 定时任务应该每1秒执行一次
- 应该有日志输出显示API调用和数据处理过程
- 应该发布WebSocket事件
问题分析过程
1. 初步排查 - 代码逻辑对比
对比正常工作的接口和不工作的接口实现:
正常工作的接口:
// 航空器数据采集
@Scheduled(fixedRateString = "${data.collector.interval}")
public void collectAircraftData() {
List<Aircraft> newAircrafts = dataCollectorDao.collectAircraftData(airportAircraftEndpoint, airportBaseUrl);
// ...
}
// 车辆数据采集
@Scheduled(fixedRateString = "${data.collector.interval}")
public void collectVehicleData() {
List<AirportVehicle> vehicles = dataCollectorDao.collectVehicleData(airportVehicleEndpoint, airportBaseUrl);
// ...
}
不工作的接口:
// 航班进出港通知采集
@Scheduled(fixedRateString = "${data.collector.flight-notification.interval}")
public void collectFlightNotificationData() {
List<FlightNotificationDTO> notifications = dataCollectorDao.getFlightNotifications(flightNotificationEndpoint, airportBaseUrl);
// ...
}
发现差异:参数传递模式相同,代码逻辑相同,唯一差异在配置键名。
2. 配置分析 - Spring属性绑定问题
检查所有定时任务的配置键名:
正常工作的配置:
${data.collector.interval}✅${data.collector.route.interval}✅${data.collector.detection.interval:1000}✅
不工作的配置:
${data.collector.flight-notification.interval}❌
关键发现:连字符在嵌套属性中的问题!
3. 配置文件结构分析
data:
collector:
interval: 250 # ✅ 工作正常
route: # ✅ 工作正常
interval: 5000
detection: # ✅ 工作正常
interval: 1000
flight-notification: # ❌ 不工作
interval: 1000
根本原因:Spring Boot中@Value/@Scheduled表达式与@ConfigurationProperties对连字符的处理规则不同。
Spring Boot配置绑定的两套规则
@ConfigurationProperties - 支持relaxed binding(宽松绑定)
@ConfigurationProperties(prefix = "my.app")
public class MyProperties {
private String firstName; // 可以绑定 first-name
}
配置文件:
my:
app:
first-name: "John" # ✅ 支持连字符,会自动转换为firstName
@Value/@Scheduled - 直接属性解析,连字符限制
@Scheduled(fixedRateString = "${my.app.first-name.interval}") // ❌ 可能失败
@Value("${my.app.first-name.interval}") // ❌ 可能失败
差异原因:
- @ConfigurationProperties使用Spring的relaxed binding机制,会自动转换kebab-case到camelCase
- @Value和@Scheduled直接使用Environment.getProperty(),需要精确匹配属性路径
- 当属性路径中包含连字符时,可能影响属性解析器的路径分割逻辑
解决方案
1. 修改配置文件
将连字符键名改为无连字符形式:
# 修改前(不工作)
flight-notification:
interval: 1000
# 修改后(正常工作)
flightnotification:
interval: 1000
2. 修改代码配置引用
// 修改前
@Scheduled(fixedRateString = "${data.collector.flight-notification.interval:1000}")
// 修改后
@Scheduled(fixedRateString = "${data.collector.flightnotification.interval:1000}")
验证方法
1. 添加调试日志验证定时任务执行
@Scheduled(fixedRateString = "${data.collector.flightnotification.interval:1000}")
public void collectFlightNotificationData() {
log.info("🔥 定时任务启动检查: 航班进出港通知采集任务执行中...");
// ...
}
2. 运行集成测试验证
测试结果显示调试日志成功输出:
🔥 定时任务启动检查: 航班进出港通知采集任务执行中...
✈️ 成功获取航班进出港通知数据,数量: 2
✈️ 采集到 2 条航班进出港通知
🛬 处理航班进出港通知: 航班号=CA8901, 类型=OUT, 跑道=35, 机位=201
📡 发布航班进出港通知WebSocket事件: 航班号=CA8901, 事件类型=TAKEOFF
✅ 航班进出港通知数据处理完成,处理数量: 2
最终修复文件
修改的文件:
/qaup-admin/src/main/resources/application.yml- 配置键名修改/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/DataCollectorService.java- 配置引用修改
测试文件:
- 集成测试验证功能正常工作
- 所有相关单元测试通过
经验教训
1. Spring Boot配置最佳实践
- 避免在嵌套配置中使用连字符:虽然YAML支持连字符,但Spring Boot的属性绑定机制在处理嵌套连字符属性时可能存在问题
- 优先使用驼峰命名或无连字符命名:如
flightNotification或flightnotification - 为配置属性提供默认值:使用
${key:defaultValue}模式避免配置缺失导致的启动失败
2. 连字符配置的使用场景
推荐使用连字符的场景:
- @ConfigurationProperties的类属性绑定
- 简单的顶级配置属性
避免使用连字符的场景:
- @Value注解中的嵌套属性路径
- @Scheduled注解中的配置引用
- 复杂的多级嵌套属性路径
3. 问题排查方法论
- 对比分析法:对比正常工作和异常功能的实现差异
- 逐层排查:从代码逻辑 → 配置文件 → Spring机制
- 添加调试日志:在关键位置添加日志验证执行流程
- 配置验证:重点检查配置键名的命名规范
4. 测试策略
- 不要仅依赖手动调用测试:集成测试手动调用方法无法发现Spring调度问题
- 需要真实的Spring Boot环境测试:只有在完整的Spring上下文中才能发现配置绑定问题
- 添加专门的调试日志:临时添加明显的调试标识快速验证修复效果
相关技术点
Spring Boot属性绑定机制
Spring Boot使用@ConfigurationProperties和Environment抽象来绑定配置属性。对于嵌套属性:
- 支持的格式:
data.collector.interval、data.collector.route.interval - 可能有问题的格式:
data.collector.flight-notification.interval - 推荐格式:
data.collector.flightnotification.interval
@Scheduled注解机制
@Scheduled注解在Spring容器启动时进行解析:
- Spring扫描所有带有
@Scheduled的方法 - 解析
fixedRateString中的配置属性引用 - 如果配置属性解析失败,该定时任务不会被注册
- 不会抛出异常,只是静默跳过
这就是为什么其他接口正常工作,而配置有问题的接口完全没有日志输出的原因。
状态
- ✅ 问题已解决
- ✅ 根本原因已确认
- ✅ 修复方案已验证
- ✅ 相关测试已通过
最终结果:航班进出港通知定时任务现在每1秒正常执行,日志输出正常,WebSocket事件发布正常。