Grails 2.4.5多节点Ehcache缓存复制失效问题求助
多节点缓存不一致与RMI复制失效排查方案
一、先处理RMI复制的SocketException(Broken pipe)
该错误直接指向RMI通信链路中断,优先从连通性和配置入手:
- 验证RMI端口连通性:
在应用内执行以下Groovy脚本,确认两台节点的RMI监听端口能互相访问:
若连通失败,说明防火墙/安全组阻断了端口,需联系运维开放;若连通成功,继续排查配置问题。def testRmiConnectivity(String targetIp, int port) { def socket = new Socket() try { socket.connect(new InetSocketAddress(targetIp, port), 5000) return "节点${targetIp}:${port}连通成功" } catch (Exception e) { return "节点${targetIp}:${port}连通失败: ${e.message}" } finally { socket.close() } } // 调用示例:testRmiConnectivity("192.168.1.100", 40001) - 调整RMI超时配置:
在ehcache配置中延长连接超时时间:
同时可尝试添加JVM启动参数:<cacheManagerPeerListenerFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" properties="port=40001, socketTimeoutMillis=30000"/>-Dsun.rmi.transport.tcp.responseTimeout=30000(若允许修改Tomcat启动参数)。 - 检查RMI端口绑定状态:
查看应用启动日志,确认RMI监听端口是否绑定成功,是否存在Address already in use这类端口占用报错。
二、校验ehcache分布式复制配置
- 确认缓存复制开关:
检查每个需要同步的缓存节点,是否配置了RMICacheReplicatorFactory并开启必要复制规则:<cache name="yourCacheName" maxEntriesLocalHeap="10000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200"> <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" properties="replicatePuts=true, replicateUpdates=true, replicateRemovals=true, replicateUpdatesViaCopy=true"/> </cache> - 检查Peer节点配置:
若为手动指定Peer节点,确认cacheManagerPeerProviderFactory的peerList包含两台节点的完整RMI地址:
若为自动发现模式(<cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="peerDiscovery=manual, peerList=rmi://node1-ip:40001/cacheManagerPeer,rmi://node2-ip:40001/cacheManagerPeer"/>peerDiscovery=automatic),确认两台节点在同一广播域,组播地址(默认230.0.0.1:4446)配置正确。 - 核对依赖版本兼容性:
本地执行grails dependency-report,确认grails-cache-ehcache1.0.101依赖的ehcore版本与Grails2.4.5的适配性,避免版本冲突导致RMI协议不兼容。
三、排查应用层缓存操作逻辑
- 校验缓存Key一致性:
在缓存读写逻辑处添加日志,打印生成的缓存Key,对比两台节点的日志,确认同一业务数据生成的Key完全一致(避免因本地变量、节点标识导致Key差异)。 - 检查缓存失效同步:
确认缓存失效操作(如cacheEvict)是否触发了RMI复制,若失效逻辑仅在本地执行,会导致节点间缓存状态不一致。
四、临时替代方案
若RMI问题无法快速解决,可采用以下方案规避:
- 切换集中式缓存:替换ehcache分布式实现为Redis,通过
grails-cache-redis插件实现全节点共享缓存,从根源解决一致性问题。 - 缩短缓存过期时间:给缓存设置较短的TTL(比如30分钟),缩小数据不一致的窗口;或在写操作后,通过HTTP接口主动触发其他节点的缓存失效。
内容的提问来源于stack exchange,提问作者Axel Kouassi
相关产品推荐
相关产品推荐

