ehcache同步原理
最近研究ehcache同步时发现一个问题:
现有A、B两个服务器,由A服务器向B服务器同步信息,采用RMI方式手动方式进行同步
配置信息如下:
<?xml version="1.0" encoding="UTF-8"?>
同步的核心代码:
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() { ListeventMessages = 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,那么此时经过网卡的流量就跟实际正常。