RMI方式Ehcache集群的源码分析
Ehcache不仅支持基本的内存缓存,还支持多种方式将本地内存中的缓存同步到其他使用Ehcache的服务器中,形成集群。如下图所示:
1服务Provider
1.1自动发现配置
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic,
multicastGroupAddress=230.0.0.1,
multicastGroupPort=4446, timeToLive=32"/>
1.2手动发现配置
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,rmiUrls=//server2:40001/sampleCache11|//server2:40001/sampleCache12"/>
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,rmiUrls=//server1:40001/sampleCache11|//server1:40001/sampleCache12"/>
1.3源码分析-RMICacheManagerPeerProviderFactory
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public CacheManagerPeerProvider createCachePeerProvider(CacheManager cacheManager, Properties properties) throws CacheException { String peerDiscovery = PropertyUtil.extractAndLogProperty(PEER_DISCOVERY, properties); if (peerDiscovery == null || peerDiscovery.equalsIgnoreCase(AUTOMATIC_PEER_DISCOVERY)) { try { return createAutomaticallyConfiguredCachePeerProvider(cacheManager, properties); } catch (IOException e) { throw new CacheException("Could not create CacheManagerPeerProvider. Initial cause was " + e.getMessage(), e); } } else if (peerDiscovery.equalsIgnoreCase(MANUALLY_CONFIGURED_PEER_DISCOVERY)) { return createManuallyConfiguredCachePeerProvider(properties); } else { return null; } } protected CacheManagerPeerProvider createManuallyConfiguredCachePeerProvider(Properties properties) { String rmiUrls = PropertyUtil.extractAndLogProperty(RMI_URLS, properties); if (rmiUrls == null || rmiUrls.length() == 0) { LOG.info("Starting manual peer provider with empty list of peers. " + "No replication will occur unless peers are added."); rmiUrls = new String(); } rmiUrls = rmiUrls.trim(); StringTokenizer stringTokenizer = new StringTokenizer(rmiUrls, PayloadUtil.URL_DELIMITER); RMICacheManagerPeerProvider rmiPeerProvider = new ManualRMICacheManagerPeerProvider(); while (stringTokenizer.hasMoreTokens()) { String rmiUrl = stringTokenizer.nextToken(); rmiUrl = rmiUrl.trim(); rmiPeerProvider.registerPeer(rmiUrl); LOG.debug("Registering peer {}", rmiUrl); } return rmiPeerProvider; } |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
public final synchronized void registerPeer(String rmiUrl) { peerUrls.put(rmiUrl, new Date()); } public final synchronized List listRemoteCachePeers(Ehcache cache) throws CacheException { List remoteCachePeers = new ArrayList(); List staleList = new ArrayList(); for (Iterator iterator = peerUrls.keySet().iterator(); iterator.hasNext();) { String rmiUrl = (String) iterator.next(); String rmiUrlCacheName = extractCacheName(rmiUrl); if (!rmiUrlCacheName.equals(cache.getName())) { continue; } Date date = (Date) peerUrls.get(rmiUrl); if (!stale(date)) { CachePeer cachePeer = null; try { cachePeer = lookupRemoteCachePeer(rmiUrl); remoteCachePeers.add(cachePeer); } catch (Exception e) { if (LOG.isDebugEnabled()) { LOG.debug("Looking up rmiUrl " + rmiUrl + " through exception " + e.getMessage() + ". This may be normal if a node has gone offline. Or it may indicate network connectivity" + " difficulties", e); } } } else { LOG.debug("rmiUrl {} should never be stale for a manually configured cluster.", rmiUrl); staleList.add(rmiUrl); } } //Remove any stale remote peers. Must be done here to avoid concurrent modification exception. for (int i = 0; i < staleList.size(); i++) { String rmiUrl = (String) staleList.get(i); peerUrls.remove(rmiUrl); } return remoteCachePeers; } public CachePeer lookupRemoteCachePeer(String url) throws MalformedURLException, NotBoundException, RemoteException { LOG.debug("Lookup URL {}", url); CachePeer cachePeer = (CachePeer) Naming.lookup(url); return cachePeer; } |
|
1
2
3
4
5
6
7
8
9
10
11
|
public MulticastRMICacheManagerPeerProvider(CacheManager cacheManager, InetAddress groupMulticastAddress, Integer groupMulticastPort, Integer timeToLive, InetAddress hostAddress) { super(cacheManager); heartBeatReceiver = new MulticastKeepaliveHeartbeatReceiver(this, groupMulticastAddress, groupMulticastPort, hostAddress); heartBeatSender = new MulticastKeepaliveHeartbeatSender(cacheManager, groupMulticastAddress, groupMulticastPort, timeToLive, hostAddress); } |
2服务Listener
2.1配置文件
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=localhost,
port=40001,
socketTimeoutMillis=2000"/>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
public final CacheManagerPeerListener createCachePeerListener(CacheManager cacheManager, Properties properties) throws CacheException { String hostName = PropertyUtil.extractAndLogProperty(HOSTNAME, properties); String portString = PropertyUtil.extractAndLogProperty(PORT, properties); Integer port = null; if (portString != null && portString.length() != 0) { port = Integer.valueOf(portString); } else { port = Integer.valueOf(0); } //0 means any port in UnicastRemoteObject, so it is ok if not specified to make it 0 String remoteObjectPortString = PropertyUtil.extractAndLogProperty(REMOTE_OBJECT_PORT, properties); Integer remoteObjectPort = null; if (remoteObjectPortString != null && remoteObjectPortString.length() != 0) { remoteObjectPort = Integer.valueOf(remoteObjectPortString); } else { remoteObjectPort = Integer.valueOf(0); } String socketTimeoutMillisString = PropertyUtil.extractAndLogProperty(SOCKET_TIMEOUT_MILLIS, properties); Integer socketTimeoutMillis; if (socketTimeoutMillisString == null || socketTimeoutMillisString.length() == 0) { socketTimeoutMillis = DEFAULT_SOCKET_TIMEOUT_MILLIS; } else { socketTimeoutMillis = Integer.valueOf(socketTimeoutMillisString); } return doCreateCachePeerListener(hostName, port, remoteObjectPort, cacheManager, socketTimeoutMillis); } protected CacheManagerPeerListener doCreateCachePeerListener(String hostName, Integer port, Integer remoteObjectPort, CacheManager cacheManager, Integer socketTimeoutMillis) { try { return new RMICacheManagerPeerListener(hostName, port, remoteObjectPort, cacheManager, socketTimeoutMillis); } catch (UnknownHostException e) { throw new CacheException("Unable to create CacheManagerPeerListener. Initial cause was " + e.getMessage(), e); } } |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
public void init() throws CacheException { if (!status.equals(Status.STATUS_UNINITIALISED)) { return; } RMICachePeer rmiCachePeer = null; try { startRegistry(); int counter = 0; populateListOfRemoteCachePeers(); synchronized (cachePeers) { for (Iterator iterator = cachePeers.values().iterator(); iterator.hasNext();) { rmiCachePeer = (RMICachePeer) iterator.next(); bind(rmiCachePeer.getUrl(), rmiCachePeer); counter++; } } LOG.debug(counter + " RMICachePeers bound in registry for RMI listener"); status = Status.STATUS_ALIVE; } catch (Exception e) { String url = null; if (rmiCachePeer != null) { url = rmiCachePeer.getUrl(); } throw new CacheException("Problem starting listener for RMICachePeer " + url + ". Initial cause was " + e.getMessage(), e); } } protected void startRegistry() throws RemoteException { try { registry = LocateRegistry.getRegistry(port.intValue()); try { registry.list(); } catch (RemoteException e) { //may not be created. Let's create it. registry = LocateRegistry.createRegistry(port.intValue()); registryCreated = true; } } catch (ExportException exception) { LOG.error("Exception starting RMI registry. Error was " + exception.getMessage(), exception); } } protected void populateListOfRemoteCachePeers() throws RemoteException { String[] names = cacheManager.getCacheNames(); for (int i = 0; i < names.length; i++) { String name = names[i]; Ehcache cache = cacheManager.getEhcache(name); synchronized (cachePeers) { if (cachePeers.get(name) == null) { if (isDistributed(cache)) { RMICachePeer peer = new RMICachePeer(cache, hostName, port, remoteObjectPort, socketTimeoutMillis); cachePeers.put(name, peer); } } } } } |
3 事件Listener
3.1配置文件
<!-- Sample cache named sampleCache2. -->
<cache name ="sampleCache2"
maxEntriesLocalHeap ="10"
eternal="false"
timeToIdleSeconds ="100"
timeToLiveSeconds ="100"
overflowToDisk="false" >
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
properties="replicateAsynchronously=true,
replicatePuts=true, replicateUpdates=true,
replicateUpdatesViaCopy=false, replicateRemovals=true "/>
</cache>
<!-- Sample cache named sampleCache4. All missing RMICacheReplicatorFactory properties
default to true -->
<cache name="sampleCache4"
maxEntriesLocalHeap="10"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LFU">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
</cache>
3.2源码分析
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public final CacheEventListener createCacheEventListener(Properties properties) { boolean replicatePuts = extractReplicatePuts(properties); boolean replicatePutsViaCopy = extractReplicatePutsViaCopy(properties); boolean replicateUpdates = extractReplicateUpdates(properties); boolean replicateUpdatesViaCopy = extractReplicateUpdatesViaCopy(properties); boolean replicateRemovals = extractReplicateRemovals(properties); boolean replicateAsynchronously = extractReplicateAsynchronously(properties); int asynchronousReplicationIntervalMillis = extractReplicationIntervalMilis(properties); if (replicateAsynchronously) { return new RMIAsynchronousCacheReplicator( replicatePuts, replicatePutsViaCopy, replicateUpdates, replicateUpdatesViaCopy, replicateRemovals, asynchronousReplicationIntervalMillis); } else { return new RMISynchronousCacheReplicator( replicatePuts, replicatePutsViaCopy, replicateUpdates, replicateUpdatesViaCopy, replicateRemovals); } } |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
/** * Whether a put should replicated by copy or by invalidation, (a remove). * <p/> * By copy is best when the entry is expensive to produce. By invalidation is best when * we are really trying to force other caches to sync back to a canonical source like a database. * An example of a latter usage would be a read/write cache being used in Hibernate. * <p/> * This setting only has effect if <code>#replicateUpdates</code> is true. */ protected boolean replicatePutsViaCopy; public void notifyElementPut(final Ehcache cache, final Element element) throws CacheException { if (notAlive()) { return; } if (!replicatePuts) { return; } if (!element.isSerializable()) { if (LOG.isWarnEnabled()) { LOG.warn("Object with key " + element.getObjectKey() + " is not Serializable and cannot be replicated"); } return; } if (replicatePutsViaCopy) { replicatePutNotification(cache, element); } else { replicateRemovalNotification(cache, (Serializable) element.getObjectKey()); } } protected static void replicatePutNotification(Ehcache cache, Element element) throws RemoteCacheException { List cachePeers = listRemoteCachePeers(cache); for (Object cachePeer1 : cachePeers) { CachePeer cachePeer = (CachePeer) cachePeer1; try { cachePeer.put(element); } catch (Throwable t) { LOG.error("Exception on replication of putNotification. " + t.getMessage() + ". Continuing...", t); } } } static List listRemoteCachePeers(Ehcache cache) { CacheManagerPeerProvider provider = cache.getCacheManager().getCacheManagerPeerProvider("RMI"); return provider.listRemoteCachePeers(cache); } |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
public final void notifyElementPut(final Ehcache cache, final Element element) throws CacheException { if (notAlive()) { return; } if (!replicatePuts) { return; } if (replicatePutsViaCopy) { if (!element.isSerializable()) { if (LOG.isWarnEnabled()) { LOG.warn("Object with key " + element.getObjectKey() + " is not Serializable and cannot be replicated."); } return; } addToReplicationQueue(new CacheEventMessage(EventMessage.PUT, cache, element, null)); } else { if (!element.isKeySerializable()) { if (LOG.isWarnEnabled()) { LOG.warn("Object with key " + element.getObjectKey() + " does not have a Serializable key and cannot be replicated via invalidate."); } return; } addToReplicationQueue(new CacheEventMessage(EventMessage.REMOVE, cache, null, element.getKey())); } } protected void addToReplicationQueue(CacheEventMessage cacheEventMessage) { if (!replicationThread.isAlive()) { LOG.error("CacheEventMessages cannot be added to the replication queue because the replication thread has died."); } else { synchronized (replicationQueue) { replicationQueue.add(cacheEventMessage); } } } private final class ReplicationThread extends Thread { public ReplicationThread() { super("Replication Thread"); setDaemon(true); setPriority(Thread.NORM_PRIORITY); } public final void run() { replicationThreadMain(); } } private void replicationThreadMain() { while (true) { // Wait for elements in the replicationQueue while (alive() && replicationQueue != null && replicationQueue.size() == 0) { try { Thread.sleep(asynchronousReplicationInterval); } catch (InterruptedException e) { LOG.debug("Spool Thread interrupted."); return; } } if (notAlive()) { return; } try { if (replicationQueue.size() != 0) { flushReplicationQueue(); } } catch (Throwable e) { LOG.error("Exception on flushing of replication queue: " + e.getMessage() + ". Continuing...", e); } } } private void flushReplicationQueue() { List replicationQueueCopy; synchronized (replicationQueue) { if (replicationQueue.size() == 0) { return; } replicationQueueCopy = new ArrayList(replicationQueue); replicationQueue.clear(); } Ehcache cache = ((CacheEventMessage) replicationQueueCopy.get(0)).cache; List cachePeers = listRemoteCachePeers(cache); List resolvedEventMessages = extractAndResolveEventMessages(replicationQueueCopy); for (int j = 0; j < cachePeers.size(); j++) { CachePeer cachePeer = (CachePeer) cachePeers.get(j); try { cachePeer.send(resolvedEventMessages); } catch (UnmarshalException e) { String message = e.getMessage(); if (message.indexOf("Read time out") != 0) { LOG.warn("Unable to send message to remote peer due to socket read timeout. Consider increasing" + " the socketTimeoutMillis setting in the cacheManagerPeerListenerFactory. " + "Message was: " + e.getMessage()); } else { LOG.debug("Unable to send message to remote peer. Message was: " + e.getMessage()); } } catch (Throwable t) { LOG.warn("Unable to send message to remote peer. Message was: " + t.getMessage(), t); } } if (LOG.isWarnEnabled()) { int eventMessagesNotResolved = replicationQueueCopy.size() - resolvedEventMessages.size(); if (eventMessagesNotResolved > 0) { LOG.warn(eventMessagesNotResolved + " messages were discarded on replicate due to reclamation of " + "SoftReferences by the VM. Consider increasing the maximum heap size and/or setting the " + "starting heap size to a higher value."); } } } |
RMI方式Ehcache集群的源码分析的更多相关文章
- [转]RMI方式Ehcache集群的源码分析
RMI方式Ehcache集群的源码分析 Ehcache不仅支持基本的内存缓存,还支持多种方式将本地内存中的缓存同步到其他使用Ehcache的服务器中,形成集群.如下图所示: Ehcache支持 ...
- 【一起学源码-微服务】Nexflix Eureka 源码十二:EurekaServer集群模式源码分析
前言 前情回顾 上一讲看了Eureka 注册中心的自我保护机制,以及里面提到的bug问题. 哈哈 转眼间都2020年了,这个系列的文章从12.17 一直写到现在,也是不容易哈,每天持续不断学习,输出博 ...
- tomcat集群实现源码级别剖析
随着互联网快速发展,各种各样供外部访问的系统越来越多且访问量越来越大,以前Web容器可以包揽接收-逻辑处理-响应整个请求生命周期的工作,现在为了构建让更多用户访问更强大的系统,人们通过不断地业务解耦. ...
- Jedis cluster集群初始化源码剖析
Jedis cluster集群初始化源码剖析 环境 jar版本: spring-data-redis-1.8.4-RELEASE.jar.jedis-2.9.0.jar 测试环境: Redis 3.2 ...
- Go合集,gRPC源码分析,算法合集
年初时,朋友圈见到的最多的就是新的一年新的FlAG,年末时朋友圈最多的也是xxxx就要过去了,你的FLAG实现了吗? 这个公众号2016就已经创建了,但截至今年之前从来没发表过文章,现在想想以前很忙, ...
- Quartz.net 定时任务之储存与持久化和集群(源码)
一.界面 1.这篇博客不上教程.直接看结果(包括把quartz任务转换成Windows服务) (1).主界面 (2).添加任务(默认执行) (3).编辑(默认开启) (4).关闭和开启 2.代码说明 ...
- 吾日三省吾身 java核心代码 高并发集群 spring源码&思想
阿里面试题 未解决https://my.oschina.net/wuweixiang/blog/1863322 java基础 有答案 https://www.cnblogs.com/xdp- ...
- EhCache 集群 配置(RMI方式)
这里先说明下环境:JDK1.6.ehcache-core-2.1.0.jar.Tomcat6.Spring3.0.2.使用的是RMI方式配置集群的,这里先吐槽下遇到的情况,在搜相关知识的时候发现到处都 ...
- lodash源码分析之缓存使用方式的进一步封装
在世界上所有的民族之中,支配着他们的喜怒选择的并不是天性,而是他们的观点. --卢梭<社会与契约论> 本文为读 lodash 源码的第九篇,后续文章会更新到这个仓库中,欢迎 star:po ...
随机推荐
- hdu 5533(几何水)
Input The first line contains a integer T indicating the total number of test cases. Each test case ...
- Codeforces Round#402(Div.1)掉分记+题解
哎,今天第一次打div1 感觉头脑很不清醒... 看到第一题就蒙了,想了好久,怎么乱dp,倒过来插之类的...突然发现不就是一道sb二分吗.....sb二分看了二十分钟........ 然后第二题看了 ...
- Python SMTP邮件发送
SMTP是发送邮件的协议,Python内置对SMTP的支持,可以发送纯文本邮件.HTML邮件以及带附件的邮件. Python对SMTP支持有smtplib和email两个模块: email负责构造邮件 ...
- switch 循环中的case理解
case后面的常量表达式是程序的入口,一旦找到入口后,后面的其他的case就没有用了,程序就会继续执行后面的所有代码
- Tensorflow 免费中文视频教程,开源代码,免费书籍.
Free-Tensorflow Tensorflow 免费中文视频教程,开源代码,免费书籍. 官方教程 官方介绍 https://tensorflow.google.cn/ 安装教程 https:// ...
- Timestamp转Calendar
Timestamp scheduleTime = r.getTimestamp("time_recv"); Calendar calendarScheduleTime = Cale ...
- Vue生命周期-手动挂载理解
改前端遇到个bug,console能够输出值,但是前端不能显示. 我简直一脸懵逼,vue的问题?网络的问题?浏览器的缓存问题? 公司网络,所以直接排除网络问题. 浏览器缓存,试了下确实一定概率可以显示 ...
- 补充Mysql5.7用法
下面简单介绍一下安装: [root@MySQL soft]# tar xf mysql-5.7.10-linux-glibc2.5-x86_64.tar.gz -C /data/service/ [r ...
- swiper display:none 后 在显示 滑动问题
一般这种问题 必须在本身元素 或者父元素 显示出来 然后调用swiper实例 或者只需加两行 observer:true, // 修改swiper自己或子元素时,自动初始化swiper obs ...
- python3.6 使用 pymysql 连接 Mysql 数据库及 简单的增删改查操作
1.通过 pip 安装 pymysql 进入 cmd 输入 pip install pymysql 回车等待安装完成: 安装完成后出现如图相关信息,表示安装成功. 2.测试连接 import ...