Nacos Issue 修复:并发导致的 NPE 异常
2023-12-05 18:57:32
导语
Nacos 是一个流行的微服务配置中心和注册中心。最近,我在一个 Spring Boot 应用程序中遇到了一个奇怪的并发问题,导致 Nacos 无法启动。经过一番深入调查,我发现问题出在 Nacos 中一个未处理的 NPE 异常,该异常是由并发访问造成的。本文将详细介绍问题的诊断过程、修复过程以及从中吸取的教训。
问题
应用程序在启动时突然终止,并在日志中显示了以下异常:
java.lang.NullPointerException: Cannot invoke "java.lang.Object.hashCode()" because the return value of "org.springframework.boot.context.event.SpringApplicationEvent.getSource()" is null
问题诊断
从异常信息中可以看出,应用程序在调用 hashCode()
方法时抛出了 NullPointerException
异常。经过进一步调查,我发现异常发生在 DeferredApplicationEventPublisher
类的 publishEvent
方法中。该方法负责发布应用程序事件。
在并发场景下,多个线程可能会同时调用 DeferredApplicationEventPublisher.publishEvent
方法。在这种情况下,如果两个线程同时访问 DeferredApplicationEventPublisher.pendingEvents
字段,可能会导致竞争条件。如果竞争条件发生在该字段为 null
时,就会抛出 NullPointerException
异常。
问题修复
为了修复这个问题,我修改了 DeferredApplicationEventPublisher
类,使其在并发场景下安全地发布事件。具体来说,我对 publishEvent
方法进行了如下修改:
@Override
public void publishEvent(ApplicationEvent event) {
if (pendingEvents == null) {
synchronized (this) {
if (pendingEvents == null) {
pendingEvents = new ArrayBlockingQueue<>(1024);
}
}
}
pendingEvents.add(event);
}
通过使用同步块,我确保了在 pendingEvents
字段为 null
时,它只会被初始化一次。这消除了并发访问 pendingEvents
字段导致的竞争条件。
教训
从这个事件中,我吸取了以下教训:
- 考虑并发问题: 在设计和实现并发系统时,考虑并发问题至关重要。即使是看似简单的操作,也可能会在并发场景下导致意外的行为。
- 使用同步原语: 在并发场景下,使用同步原语(如锁或同步块)可以防止竞争条件和数据损坏。
- 充分测试: 充分的测试是发现并发问题的关键。使用并发测试框架(如 JUnit 的
@Concurrent
注解)可以模拟并发场景并发现潜在的问题。
总结
我通过修改 DeferredApplicationEventPublisher
类,成功解决了 Nacos 中并发导致的 NPE 异常问题。通过考虑并发问题、使用同步原语和充分测试,我确保了应用程序在并发场景下的稳定性。我希望这个案例可以帮助其他开发人员避免类似的问题并提高其应用程序的健壮性。