我使用ehcache来控制用户会话,每隔一段时间我在用户登录时看到日志中的这个错误。
net.sf.ehcache.constructs.nonstop.NonStopCacheException: keySet timed out
at net.sf.ehcache.constructs.nonstop.concurrency.NonStopCacheKeySet$NonStopCacheKeySetIterator$1.performClusterOperationTimedOut(NonStopCacheKeySet.java:103)
at net.sf.ehcache.constructs.nonstop.concurrency.NonStopCacheKeySet$NonStopCacheKeySetIterator$1.performClusterOperationTimedOut(NonStopCacheKeySet.java:96)
at net.sf.ehcache.constructs.nonstop.store.ExecutorServiceStore.executeClusterOperation(ExecutorServiceStore.java:1187)
at net.sf.ehcache.constructs.nonstop.store.NonstopStoreImpl.executeClusterOperation(NonstopStoreImpl.java:704)
at net.sf.ehcache.constructs.nonstop.concurrency.NonStopCacheKeySet$NonStopCacheKeySetIterator.<init>(NonStopCacheKeySet.java:96)
at net.sf.ehcache.constructs.nonstop.concurrency.NonStopCacheKeySet.iterator(NonStopCacheKeySet.java:56)
at net.sf.ehcache.Cache.getKeysWithExpiryCheck(v.java:1906)
...
官方ehcache文档说:"考虑您的使用是否需要检查过期密钥。因为这个方法需要很长时间,取决于缓存设置,…"。
所以我不确定ecache.xml中的超时应该增加多少来阻止这个错误,尽管20000 ms对我来说似乎已经足够了,因为文档还提到每1000个条目所花费的时间大约是200ms。
这是使用的ehcache.xml。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="RelianceCache" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd">
<cache name="READ_USERS_CACHE" maxElementsInMemory="0" eternal="true" overflowToDisk="false">
<terracotta clustered="true" valueMode="serialization" consistency="strong">
<nonstop immediateTimeout="false" timeoutMillis="20000">
<timeoutBehavior type="exception" />
</nonstop>
</terracotta>
</cache>
<terracottaConfig url="TSA_SERVERS:TSA_PORT" rejoin="true" />
</ehcache>
编辑:查看NonStopCacheKeySet实现,错误正在方法NonStopCacheKeySetIterator
中抛出。
增加超时时间并不能解决问题。100个条目20秒的时间太长了。
如果从应用程序逻辑的角度来看是合适的,我会考虑将timeoutBehavior更改为timeoutBehavior="localReadsAndExceptionOnWrite"
。请注意,localCacheEnabled
属性必须为TRUE,这是默认值。
...
<terracotta clustered="true" localCacheEnabled="true" valueMode="serialization" consistency="strong">
<nonstop immediateTimeout="false" timeoutMillis="20000">
<timeoutBehavior type="localReadsAndExceptionOnWrite" />
</nonstop>
</terracotta>
...
另一个解决方案是将一致性更改为默认值consistency ="eventual"
。这很可能会解决问题,但可能会导致缓存返回的数据暂时过期。
正如文档所述"考虑您的使用是否需要检查过期密钥…"。是否有可能调整应用程序逻辑以使用Cache.getKey()而不是遍历键列表?