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 ...
随机推荐
- Python的序列类型——List
List 列表 List,本质是一个链表,从链表的实现角度来讲,链表的每一个结点都存放着值和指向下一个节点的指针. 因此链表在内存的存储可以是不连续的,它是一种高效的数据结构.因此列表与字符串的区别是 ...
- day4 liaoxuefeng---高级特性
掌握了Python的数据类型.语句和函数,基本上就可以编写出很多有用的程序了. 但是在Python中,代码不是越多越好,而是越少越好.代码不是越复杂越好,而是越简单越好. 基于这一思想,我们来介绍Py ...
- js生成四位随机数的简便方法
do out = Math.floor(Math.random()*10000); while( out < 1000 ) alert( out );
- OSX 鼠标和键盘事件
本文转自:http://www.macdev.io/ebook/event.html 事件分发过程 OSX 与用户交互的主要外设是鼠标,键盘.鼠标键盘的活动会产生底层系统事件.这个事件首先传递到IOK ...
- Mysql锁机制--乐观锁 & 悲观锁
Mysql 系列文章主页 =============== 从 这篇 文章中,我们知道 Mysql 并发事务会引起更新丢失问题,解决办法是锁.所以本文将对锁(乐观锁.悲观锁)进行分析. 第一部分 悲观锁 ...
- Java并发中的CopyOnWrite容器
Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改, ...
- solr服务器搭建
百度百科定义:Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口.用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引:也可以通过Ht ...
- 原生Js写轮播图代码
html css js 在知道jQuery如何实现轮播效果的基础上,用js写代码 如图:标记这里的地方 理解一下 用到的知识: 1.HTML DOM 的appendChild() 和 removeCh ...
- ACM Smallest Difference
给定一些不同的十进制数字(distinct decimal digits),您可以通过选择这些数字的非空子集(non-empty subset)并以某种顺序编写它们,从而形成一个整数. 剩下的数字可以 ...
- JVM初探- 内存分配、GC原理与垃圾收集器
JVM初探- 内存分配.GC原理与垃圾收集器 标签 : JVM JVM内存的分配与回收大致可分为如下4个步骤: 何时分配 -> 怎样分配 -> 何时回收 -> 怎样回收. 除了在概念 ...