Ignite监听EVT_NODE_LEFT无缓存数据时服务端挂起如何修复
问题根本原因
你在Ignite的**系统关键线程(disco-event-worker发现事件工作线程)**中执行了阻塞操作ignite.getOrCreateCache()。该线程是Ignite用于处理集群拓扑变更、节点发现等核心逻辑的专属线程,不允许执行任何阻塞等待操作,否则就会触发你看到的SYSTEM_WORKER_BLOCKED错误,严重时会直接导致整个集群脑裂。
为什么有数据时运行正常、无数据时挂起:
- 当test缓存已有数据时,缓存元数据已经在本地节点完成同步,
getOrCreateCache不需要跨节点协商,直接返回本地缓存实例,不会触发阻塞。 - 当test缓存无数据时,缓存的完整元数据未在当前存活节点落地,
getOrCreateCache需要发起集群请求确认所有节点的缓存状态,此时另一节点已经下线,请求永远无法收到返回,就会将系统线程永久挂起。
修复方案
1. 禁止在发现事件回调中执行阻塞操作
所有缓存清理、业务逻辑必须提交到自定义业务线程池异步执行,绝对不能占用disco-event-worker系统线程。
2. 提前初始化需要用到的缓存
不要在节点下线事件回调中才动态创建缓存,在服务节点启动阶段就完成目标缓存的初始化,直接复用实例即可。
优化后代码示例
// 服务启动阶段提前初始化缓存,存入全局变量复用 IgniteCache<String, String> testCache = ignite.getOrCreateCache("test"); // 事件监听逻辑 IgniteEvents events = ignite.events(); IgnitePredicate<DiscoveryEvent> filter = evt -> { if (evt.eventNode().isClient()) { return true; } System.out.println("remote event: " + evt.name()); System.out.println("remote event: " + evt.eventNode().consistentId()); return true; }; UUID uuid = events.remoteListen(new IgniteBiPredicate<UUID, DiscoveryEvent>() { // 自定义业务线程池处理清理逻辑,不要占用Ignite系统线程 private final ExecutorService bizExecutor = Executors.newFixedThreadPool(2); @Override public boolean apply(UUID uuid, DiscoveryEvent e) { ClusterNode node = e.eventNode(); if(node.isClient()) { return true; } String consistentId= node.consistentId().toString(); // 异步提交清理逻辑,直接返回不阻塞系统线程 bizExecutor.submit(() -> { // 直接使用提前初始化好的testCache执行操作即可 // testCache.xxx() }); return true; } }, filter, EventType.EVT_NODE_LEFT);
额外可选优化
如果业务确实需要动态获取缓存,不要用无超时的同步方法,改用带超时的异步调用避免永久阻塞:
// 最多等待3秒,超时后直接抛出异常处理,不会永久挂住线程 IgniteCache<String, String> cache = ignite.getOrCreateCacheAsync("test").get(3, TimeUnit.SECONDS);
内容的提问来源于stack exchange,提问作者Henrik
相关产品推荐
相关产品推荐

