EhCache RMI 分布式缓存/缓存集群
EhCache 系统简介
EhCache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点。
EhCache 的主要特性有:
- 快速、精干
- 简单;
- 多种缓存策略;
- 缓存数据有两级:内存和磁盘,因此无需担心容量问题;
- 缓存数据会在虚拟机重启的过程中写入磁盘;
- 可以通过 RMI、可插入 API 等方式进行分布式缓存;
- 具有缓存和缓存管理器的侦听接口;
- 支持多缓存管理器实例,以及一个实例的多个缓存区域;
- 提供 Hibernate 的缓存实现;
EhCache集群解决的问题:
由 于 EhCache 是进程中的缓存系统,一旦将应用部署在集群环境中,每一个节点维护各自的缓存数据,当某个节点对缓存数据进行更新,这些更新的数据无法在其它节点中共享, 这不仅会降低节点运行的效率,而且会导致数据不同步的情况发生。例如某个网站采用 A、B 两个节点作为集群部署,当 A 节点的缓存更新后,而 B 节点缓存尚未更新就可能出现用户在浏览页面的时候,一会是更新后的数据,一会是尚未更新的数据。
所以就需要用到 EhCache 的集群解决方案。
EhCache集群方案:
• Terracotta
• RMI
• JMS : 依赖 ehcache-jmsreplication.jar
• JGroups : 依赖ehcache-jgroupsreplication.jar
• EhCache Server
其中的三种最为常用集群方式,分别是 RMI、JGroups 以及 EhCache Server 。
EhCache集群疑问
• 你如何知道集群环境中的其他缓存?
• 分布式传送的消息是什么形式?
• 什么情况需要进行复制?增加(Puts),更新(Updates)或是失效(Expiries)?
• 采用什么方式进行复制?同步还是异步方式?
EhCache集群基本概念
1、正确的元素类型:只有可序列化的元素可以进行复制。一些操作,比如移除,只需要元素的键值而不用整个元素;在这样的操作中即使元素不是可序列化的但键值是可序列化的也可以被复制。
2、成员发现(Peer Discovery):Ehcache进行集群的时候有一个cache组的概念。每个cache都是其他cache的一个peer,没有主cache的存在。成员发现(Peer Discovery)正是用来解决 “你如何知道集群环境中的其他缓存?” 这个问题的。Ehcache提供了两种机制用来进行成员发现,即:自动成员发现和手动成员发现。要使用一个内置的成员发现机制要在ehcache的配置文件中指定cacheManagerPeerProviderFactory元素的class属性为
net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory。
自动的发现方式用TCP广播机制来确定和维持一个广播组。它只需要一个简单的配置可以自动的在组中添加和移除成员。在集群中也不需要什么优化服务器的知识,这是默认推荐的。ehcache.xml配置示例代码如下:
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1,
multicastGroupPort=4446, timeToLive=32"/>
<!-- timeToLive
0是限制在同一个服务器
1是限制在同一个子网
32是限制在同一个网站
64是限制在同一个region
128是限制在同一个大洲
255是不限制-->
手动的发现方式需要知道每个监听器的IP地址和端口。集群成员(也就是服务器)不能在运行时动态地添加和移除。ehcache.xml配置示例代码如下:
<!-- server1 -->
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,
rmiUrls=//server2:40001/sampleCache11|//server2:40001/sampleCache12"/> <!-- server2 -->
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,
rmiUrls=//server1:40001/sampleCache11|//server1:40001/sampleCache12"/>
CacheManagerPeerListener
每个CacheManagerPeerListener监听从成员们发向当前CacheManager的消息。配置 CacheManagerPeerListener需要指定一个CacheManagerPeerListenerFactory,它以插件的机制实现, 用来创建CacheManagerPeerListener。
Ehcache有一个内置的基于RMI的分布系统。它的监听器是RMICacheManagerPeerListener,这个监听器可以用RMICacheManagerPeerListenerFactory来配置。
示例代码:
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=localhost, port=40001,
socketTimeoutMillis=2000"/>
属性说明:
hostname (可选) – 运行监听器的服务器名称。标明了做为集群群组的成员的地址,同时也是你想要控制的从集群中接收消息的接口。
在CacheManager初始化的时候会检查hostname是否可用。
如果hostName不可用,CacheManager将拒绝启动并抛出一个连接被拒绝的异常。
如果指定,hostname将用InetAddress.getLocalHost().getHostAddress()来得到。
port – 监听器监听的端口。
socketTimeoutMillis (可选) – Socket超时的时间。默认是2000ms。当你socket同步缓存请求地址比较远,不是本地局域网。你可能需要把这个时间配置大些,不然很可能延时导致同步缓存失败。
CacheReplicators
每个要进行同步的cache都需要设置一个用来向CacheManager的成员复制消息的缓存事件监听器。这个工作要通过为每个cache的配置增加一个cacheEventListenerFactory元素来完成。
代码:
<cache name="sampleCache2" maxElementsInMemory="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>
cacheEventListenerFactory 支持以下属性
replicatePuts=true | false – 当一个新元素增加到缓存中的时候是否要复制到其他的peers. 默认是true。
replicateUpdates=true | false – 当一个已经在缓存中存在的元素被覆盖时是否要进行复制。默认是true。
replicateRemovals= true | false – 当元素移除的时候是否进行复制。默认是true。
replicateAsynchronously=true | false – 复制方式是异步的(指定为true时)还是同步的(指定为false时)。默认是true。
replicatePutsViaCopy=true | false – 当一个新增元素被拷贝到其他的cache中时是否进行复制指定为true时为复制,默认是true。
Cache属性说明:
<cache
name="userCache"
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
properties="replicateAsynchronously=true,replicatePuts=true,replicateUpdates=true,replicateUpdatesViaCopy=false,replicateRemovals=true">
</cacheEventListenerFactory>
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
properties="bootstrapAsynchronously=true">
</bootstrapCacheLoaderFactory>
</cache>
必须属性:
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:当内存中对象数量达
maxElementsInMemory时,Ehcache将会对象写到磁盘中。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
可选的属性:
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否disk store在虚拟机启动时持久化. The default value is false.
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
clearOnFlush:内存数量最大时是否清除。
缓存子元素:
cacheEventListenerFactory:注册相应的的缓存监听类,用于处理缓存事件,如put,remove,update,和expire
bootstrapCacheLoaderFactory:指定相应的BootstrapCacheLoader,用于在初始化缓存,以及自动设置。
EhCache RMI 手动方式配置缓存
Tomcat1: 127.0.0.1:8080, Cache Server1: 127.0.0.1:40001
Tomcat2: 127.0.0.1:8088, Cache Server2: 127.0.0.1:40002
用到的架包:
ehcache-core-2.6.8.jar
log4j-1.2.17.jar
servlet-api.jar (示例中使用了@WebServlet,请确保servler-api的架包版本大于3.0)
slf4j-api-1.6.1.jar
slf4j-log4j12-1.6.1.jar
创建Web工程TestEhcache, 工程目录如下:
CacheManagerFactory.java
private CacheManager manager;
private static CacheManagerFactory factory = new CacheManagerFactory();
private final static String EHCACHEFILE = "/ehcache.xml"; private CacheManagerFactory() {
} public static CacheManagerFactory getInstance() {
return factory;
} public CacheManager getCacheManager() {
if (manager == null) {
InputStream is = this.getClass().getResourceAsStream(EHCACHEFILE);
manager = CacheManager.create(is);
}
return manager;
} public Cache getCache(String cache) {
return getCacheManager().getCache(cache);
} public void setCache(Cache cache) {
getCacheManager().addCache(cache);
} public void setCache(String cache) {
getCacheManager().addCache(cache);
} public Element getElement(String cacheName, String key) {
if (getCache(cacheName) == null)
setCache(cacheName);
return getCache(cacheName).get(key);
} public void setElement(String cache, Element element) {
if (getCache(cache) == null)
setCache(cache);
getCache(cache).put(element);
} public Boolean continaElementKey(String cacheName, String key) {
if (getCache(cacheName) == null)
setCache(cacheName);
return getCache(cacheName).isKeyInCache(key);
}
TesAction.java
@WebServlet("/test")
public class TesAction extends HttpServlet { private static final long serialVersionUID = 1L; CacheManagerFactory cmf = CacheManagerFactory.getInstance(); protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
doPost(request,response);
} protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
String res = "";
String key = request.getParameter("key"); Element element = cmf.getElement("userCache", "map");
if(element == null){
Map<String, String> map = new HashMap<String, String>();
map.put(key, key);
cmf.setElement("userCache", new Element("map", map));
}else{
Map<String, String> map = (Map<String, String>) element.getValue();
res = map.get(key);
if(res == null){
map.put(key, key);
// 多次测试发现,存在同名Element是,重复put的是无法复制的,因此当遇到两个节点同步不上的时候,先remove后put。
cmf.getCache("userCache").remove("map");
cmf.setElement("userCache", new Element("map", map));
res = "0;null";
}
} response.setContentType("text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.write(res);
out.close();
} @Override
public void init() throws ServletException {
super.init();
} }
ehcache.xml:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.sf.net/ehcache.xsd"> <diskStore path="java.io.tmpdir" /> <cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,rmiUrls=//127.0.0.1:40002/userCache|//127.0.0.1:40002/resourceCache">
</cacheManagerPeerProviderFactory> <cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=127.0.0.1, port=40001, socketTimeoutMillis=2000">
</cacheManagerPeerListenerFactory> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false"/> <cache
name="userCache"
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
properties="replicateAsynchronously=true,replicatePuts=true,replicateUpdates=true,replicateUpdatesViaCopy=false,replicateRemovals=true">
</cacheEventListenerFactory>
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
properties="bootstrapAsynchronously=true">
</bootstrapCacheLoaderFactory>
</cache> <cache
name="resourceCache"
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
properties="replicateAsynchronously=true,replicatePuts=true,replicateUpdates=true,replicateUpdatesViaCopy=false,replicateRemovals=true">
</cacheEventListenerFactory>
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
properties="bootstrapAsynchronously=true">
</bootstrapCacheLoaderFactory>
</cache> </ehcache>
复制TestEhcache到 TestEhcache1, 修改TestEhcache1下的ehcache.xml ,只需要修改cacheManagerPeerProviderFactory和cacheManagerPeerListenerFactory修改为如下代码,其他不变
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,rmiUrls=//127.0.0.1:40001/userCache|//127.0.0.1:40001/resourceCache">
</cacheManagerPeerProviderFactory> <cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=127.0.0.1, port=40002, socketTimeoutMillis=2000">
</cacheManagerPeerListenerFactory>
为了区别,将TestEhcache1的TesAction.doPost()方法修改为如下代码,只get的缓存不put缓存,用于观察TestEhcache1是否能同步TestEhcache的数据:
String res = "0;null";
Element element = cmf.getElement("userCache", "map");
if(element != null){
Map<String, String> map = (Map<String, String>) element.getValue();
res = map.get(request.getParameter("key"));
if(res == null){
res = "0;null";
}
}
response.setContentType("text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.write(res);
out.close();
将TestEhcache部署到tomcat1, TestEhcache1部署到Tomcat2。依次执行如下代码:
localhost:8080/TestEhcache/test?key=125
localhost:8080/TestEhcache/test?key=125
localhost:8088/TestEhcache1/test?key=125
如果输出内容分别如下,说明集群ok, 两节点数据同步没问题, 否者请仔细检查配置文件和TestAction代码:
0;null
125
125
EhCache RMI 自动方式配置缓存
将ehcache.xml的cacheManagerPeerProviderFactory代码改为:
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic, multicastGroupAddress=192.168.0.1,
multicastGroupPort=40004, timeToLive=32"
/>
EhCache Jgroups 方式配置缓存
在TestEhcache和TestEhcache1工程中添加ehcache-jgroupsreplication-1.7.jar和jgroups-3.6.9.Final.jar。 使用该方式比RMI方式配置简单。
TestEhcache 的 ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.sf.net/ehcache.xsd"> <diskStore path="java.io.tmpdir" /> <cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsCacheManagerPeerProviderFactory"
properties="connect=TCP(bind_port=4001):
TCPPING(initial_hosts=192.168.8.150[4001],192.168.8.150[4002];port_range=10;timeout=3000;num_initial_members=3):
MERGE2(min_interval=3000;max_interval=5000):
FD_ALL(interval=5000;timeout=20000):
FD(timeout=5000;max_tries=48;):
VERIFY_SUSPECT(timeout=1500):
pbcast.NAKACK(retransmit_timeout=100,200,300,600,1200,2400,4800;discard_delivered_msgs=true):
pbcast.STABLE(stability_delay=1000;desired_avg_gossip=20000;max_bytes=0):
pbcast.GMS(print_local_addr=true;join_timeout=5000)"
propertySeparator="::">
</cacheManagerPeerProviderFactory> <defaultCache
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="true"
timeToIdleSeconds="0"
timeToLiveSeconds="0"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory"
properties="replicateAsynchronously=true,replicatePuts=true,replicateUpdates=true,replicateUpdatesViaCopy=false,replicateRemovals=true"/>
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsBootstrapCacheLoaderFactory"
properties="bootstrapAsynchronously=true">
</bootstrapCacheLoaderFactory>
</defaultCache>
</ehcache>
复制TestEhcache的ehcache.xml到TestEhcache1,并且修改下列代码:
connect=TCP(bind_port=4002)
推荐文章:http://raychase.iteye.com/blog/1545906
http://blog.csdn.net/tang06211015/article/details/52281551
http://www.ehcache.org/apidocs/2.10.3/index.html
来源:http://blog.csdn.net/xlxxcc/article/details/52350264
EhCache RMI 分布式缓存/缓存集群的更多相关文章
- ZooKeeper一二事 - 搭建ZooKeeper伪分布式及正式集群 提供集群服务
集群真是好好玩,最近一段时间天天搞集群,redis缓存服务集群啦,solr搜索服务集群啦,,,巴拉巴拉 今天说说zookeeper,之前搭建了一个redis集群,用了6台机子,有些朋友电脑跑步起来,有 ...
- JAVA架构师眼中的高并发架构,分布式架构 应用服务器集群
前言 高并发经常会发生在有大活跃用户量,用户高聚集的业务场景中,如:秒杀活动,定时领取红包等. 为了让业务可以流畅的运行并且给用户一个好的交互体验,我们需要根据业务场景预估达到的并发量等因素,来设计适 ...
- Ubuntu16.04.1上搭建分布式的Redis集群
为什么要集群: 通常为了,提高网站的响应速度,总是把一些经常用到的数据放到内存中,而不是放到数据库中,Redis是一个很好的Cache工具,当然了还有Memcached,这里只讲Redis.在我们的电 ...
- 在Centos 7上安装配置 Apche Kafka 分布式消息系统集群
Apache Kafka是一种颇受欢迎的分布式消息代理系统,旨在有效地处理大量的实时数据.Kafka集群不仅具有高度可扩展性和容错性,而且与其他消息代理(如ActiveMQ和RabbitMQ)相比,还 ...
- Hbase 完全分布式 高可用 集群搭建
1.准备 Hadoop 版本:2.7.7 ZooKeeper 版本:3.4.14 Hbase 版本:2.0.5 四台主机: s0, s1, s2, s3 搭建目标如下: HMaster:s0,s1(备 ...
- Ubuntu16.04.1上搭建分布式的Redis集群,并使用C#操作
为什么要集群: 通常为了,提高网站的响应速度,总是把一些经常用到的数据放到内存中,而不是放到数据库中,Redis是一个很好的Cache工具,当然了还有Memcached,这里只讲Redis.在我们的电 ...
- ClickHouse 分布式高可用集群搭建(转载)
一.ClickHouse安装方式: 源码编译安装 Docker安装 RPM包安装 为了方便使用,一般采用RPM包方式安装,其他两种方式这里不做说明. 二.下载安装包 官方没有提供rpm包,但是Alti ...
- 分布式缓存 Redis 集群搭建
Redis 集群简介 Redis Cluster 即 Redis 集群,是 Redis 官方在 3.0 版本推出的一套分布式存储方案.完全去中心化,由多个节点组成,所有节点彼此互联.Redis 客户端 ...
- Linux(Centos7)下redis5缓存服务集群分布式搭建
注意:可以查看Redis官网查看集群搭建方式,连接如下 https://redis.io/topics/cluster-tutorial 集群中应该至少有三个节点,每个节点有一备份节点.需要6台服务器 ...
随机推荐
- AE开发示例之RunGPAsync
using System; using System.Collections.Generic;using System.ComponentModel;using System.Data;using S ...
- tiny4412学习一:编译uboot,体验裸机
首先,我们在ubuntu建立一个自己的文件夹,我的是: /home/wang/tiny_4412下有 datasheet shc(原理图PCB文件夹) src tools src下有 codes ...
- 6410实现网卡(DM9000A)收发功能及ARP协议实现
1. 网卡硬件结构(DM9000A) 网卡的实质就是MAC通过MII接口控制PHY的过程. MAC主要负责数据帧的构建.数据差错检查.传送控制等. PHY是物理接口收发器,属于物理层,当它收到MAC过 ...
- leetcode72. Edit Distance(编辑距离)
以下为个人翻译方便理解 编辑距离问题是一个经典的动态规划问题.首先定义dp[i][j表示word1[0..i-1]到word2[0..j-1]的最小操作数(即编辑距离). 状态转换方程有两种情况:边界 ...
- MacOS changed System Integrity Protection status
禁止 System Integrity Protection 按住 command + R 重启系统 进入单用户模式 启动bash工具: 输入: csrutil disable 输入:reboot 启 ...
- MongoDB(八)Mongodb——GridFS存储
mongoDB的文档以BSON格式存储,支持二进制的数据类型,当我们把二进制格式的数据直接保存到mongoDB的文档中.但是当文件太大时,例如图片和视频等文件,每个文档的长度是有限的,于是mongoD ...
- My SQL的内连接,外链接查询
1.内连接:只连接匹配的行. 2.左外连接:包含左边表的全部行,以及右边表中所有匹配的行,无论右边的表有没有和左边匹配的行,左边的所有行都必须要显示. 3.右外连接:包含右边表的全部行,以及左边表中所 ...
- 并发读写缓存实现机制(一):为什么ConcurrentHashMap可以这么快?
大家都知道ConcurrentHashMap的并发读写速度很快,但为什么它会这么快?这主要归功于其内部数据结构和独特的hash运算以及分离锁的机制.做游戏性能很重要,为了提高数据的读写速度,方法之一就 ...
- Hibernate温习(一)
//从学校出来几个月了,一直用maximo没有使用到Hibernate,趁着周末的空闲时间重新开始学习Hibernate. Hibernate概念: Hibernate是数据库访问层的框架,对JDBC ...
- STL中的set容器的一点总结
1.关于set C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构 ...