最近研究ehcache同步時發現一個問題:現有A、B兩個伺服器,由A伺服器向B伺服器同步信息,採用RMI方式手動方式進行同步配置信息如下: 同步的核心代碼:String key = StringUtils.leftPa...
最近研究ehcache同步時發現一個問題:
現有A、B兩個伺服器,由A伺服器向B伺服器同步信息,採用RMI方式手動方式進行同步
配置信息如下:
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false" monitoring="autodetect" dynamicConfig="true"> <cacheManagerPeerListenerFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" properties="hostName = 127.0.0.1, port = 50001, socketTimeoutMillis=10000" /> <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="peerDiscovery=manual, rmiUrls=//127.0.0.1:40001/clusterCache"/> <cache name="clusterCache" maxEntriesLocalHeap="999999999" eternal="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" overflowToDisk="false"> <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/> </cache> </ehcache>
同步的核心代碼:
String key = StringUtils.leftPad(Thread.currentThread().getName() + a, 20, "a"); Element element = new Element(key, value); CacheInstance.cache.put(element);
其中,value全部引用的是同一個靜態變數 static final byte[] bytes = new byte[1024*100];
測試發現問題:假設現有單個byte[1024*100],同步16000個,那麼同步的數據大小為1.5G左右,但是同步過程中,通過監控B伺服器的網卡,發現網卡上實際並沒有如此大的數據
後續經過分析得知:
RMI協議,是有java序列化和HTTP協議構成,同步時會將同步的數據全部序列化,而放入Element的value,實際上都是對靜態變數value的引用,而ehcache同步時預設是同步1000個Element,所以這1000個Element實際上經過網卡的數據只有一個byte[1024*100]大小。
Ehcache同步源碼:
private void writeReplicationQueue() {
List<EventMessage> eventMessages = extractEventMessages(maximumBatchSize);
if (!eventMessages.isEmpty()) {
for (CachePeer cachePeer : listRemoteCachePeers(eventMessages.get(0).getEhcache())) {
try {
cachePeer.send(eventMessages);
} catch (UnmarshalException e) {
String message = e.getMessage();
if (message.contains("Read time out") || message.contains("Read timed out")) {
LOG.warn("Unable to send message to remote peer due to socket read timeout. Consider increasing" +
" the socketTimeoutMillis setting in the cacheManagerPeerListenerFactory. " +
"Message was: " + message);
} else {
LOG.debug("Unable to send message to remote peer. Message was: " + message);
}
} catch (Throwable t) {
LOG.warn("Unable to send message to remote peer. Message was: " + t.getMessage(), t);
}
}
}
}
其中,maximumBatchSize是本次同步Element的數量,該值可以在如下配置中進行自定義:
<cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" properties = "asynchronousReplicationMaximumBatchSize=1"/>
其中,asynchronousReplicationMaximumBatchSize=1表示每次同步一個Element,那麼此時經過網卡的流量就跟實際正常。