Tomcat集群部署情况说明
两台Web服务器,每台服务器部署两个tomcat
server1 192.168.178.101
tomcat1 tomcat2
server2 192.168.178.102
tomcat3 tomcat4
采用Ehcache的RMI方式来实现缓存同步复制
方式一:自动发现集群成员
ehcache.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="true" monitoring="autodetect"
dynamicConfig="true">
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic,
multicastGroupAddress=230.0.0.1,
multicastGroupPort=4446
timeToLive=1"/>
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/>
<cache name="Cache1"
maxElementsInMemory="100"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LFU">
<cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
</cache>
</ehcache>
PS:
a、配置简单,每个tomcat使用完全相同的ehcache配置;
b、通过多播( multicast )来维护集群中的所有有效节点。这也是最为简单而且灵活的方式,与手工模式不同的是,每个节点上的配置信息都相同,大大方便了节点的部署,避免人为的错漏出现。
c、timeToLive的值指的是数据包可以传递的域或是范围。约定如下:
0是限制在同一个服务器
1是限制在同一个子网
32是限制在同一个网站
64是限制在同一个region
128是限制在同一个大洲
255是不限制
在Java实现中默认值是1,也就是在同一个子网中传播。改变timeToLive属性可以限制或是扩展传播的范围。
d、自动的peer discovery与广播息息相关。广播可能被路由阻拦,像Xen和VMWare这种虚拟化的技术也可以阻拦广播(阿里云主机好像也不提供广播,阿里云服务器上部署时可采用手动配置成员发现)。
如果这些都打开了,你可能还在要将你的网卡的相关配置打开。一个简单的办法可以告诉广播是否有效,那就是使用ehcache remote debugger来看“心跳”是否可用。
方式二:手动配置发现集群成员
ehcache.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="true" monitoring="autodetect"
dynamicConfig="true">
<!--RMI方式二:手动成员发现配置: ip + 端口号,手动指定需要同步的server和cache name -->
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,
rmiUrls=
//192.168.178.101:50001/Cache1|
//192.168.178.101:50001/Cache2|
//192.168.178.102:40001/Cache1|
//192.168.178.102:40001/Cache2|
//192.168.178.102:50001/Cache1|
//192.168.178.102:50001/Cache2"/>
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=192.168.178.101, port=40001, socketTimeoutMillis=2000" />
<cache name="Cache1"
maxElementsInMemory="1000"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LFU">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
properties="
replicateAsynchronously=true,
replicatePuts=true,
replicateUpdates=true,
replicateUpdatesViaCopy=false,
replicateRemovals=true"/>
<!-- 用于在初始化缓存,以及自动设置 -->
<bootstrapCacheLoaderFactory class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>
</cache>
<cache name="Cache2"
maxElementsInMemory="2000"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LFU">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
properties="
replicateAsynchronously=true,
replicatePuts=true,
replicateUpdates=true,
replicateUpdatesViaCopy=false,
replicateRemovals=true"/>
<!-- 用于在初始化缓存,以及自动设置 -->
<bootstrapCacheLoaderFactory class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"/>
</cache>
</ehcache>
PS:
a、每个tomcat的配置文件都不一样,部署集群时有点繁琐;
b、配置cacheManagerPeerProviderFactory,rmiUrls配置需要同步的各个集群节点列表(不包括本服务器节点);
上面示例配置文件是tomcat1的配置,rmiUrls列表配置tomcat2,tomcat3,tomcat4需要同步的缓存项,每个缓存项的配置格式://server:port/cacheName
同理,tomcat2的配置中,rmiUrls列表就需要配置tomcat1,tomcat3,tomcat4的缓存项;tomcat3,tomcat4以此类推;
c、配置cacheManagerPeerListenerFactory,本节点的缓存监听配置,属性中需指定本节点IP或域名、监听端口号、socket通信超时时间
hostName=192.168.178.101, port=40001, socketTimeoutMillis=2000
同理:tomcat2配置:hostName=192.168.178.101, port=50001, socketTimeoutMillis=2000
tomcat3配置:hostName=192.168.178.102, port=40001, socketTimeoutMillis=2000
tomcat4配置:hostName=192.168.178.102, port=50001, socketTimeoutMillis=2000
d、配置具体的cache,需要配置cacheEventListenerFactory,指定哪些操作时需要replicate cache(同步复制缓存)
replicatePuts=true | false – 当一个新元素增加到缓存中的时候是否要同步复制到其他的peers. 默认是true。
replicateUpdates=true | false – 当一个已经在缓存中存在的元素被覆盖更新时是否要进行复制。默认是true。
replicateRemovals= true | false – 当元素移除的时候是否进行复制。默认是true。
replicateAsynchronously=true | false – 复制方式是异步的(指定为true时)还是同步的(指定为false时)。默认是true。
replicateUpdatesViaCopy=true | false – 当一个元素被拷贝到其他的cache中时是否进行复制(指定为true时为复制),默认是true。
Ehcahce官方文档中的描述:
The factory recognises the following properties:
replicatePuts=true | false - whether new elements placed in a cache are replicated to others. Defaults to true.
replicateUpdates=true | false - whether new elements which override an element already existing with the same key are replicated. Defaults to true.
replicateRemovals=true - whether element removals are replicated. Defaults to true.
replicateAsynchronously=true | false - whether replications are asyncrhonous (true) or synchronous (false). Defaults to true.
replicateUpdatesViaCopy=true | false - whether the new elements are copied to other caches (true), or whether a remove message is sent. Defaults to true.
调用Ehcahe提供的API,编码缓存的加载及获取,更新
引入jar包:ehcache.jar
放入缓存的对象必须是可序列化的,即必须实现接口Serializable
示例代码:
public class ImeiWhiteListCacheHelper {
private static final String IMEI_CACHE = "IMEICache";
private static ImeiWhiteListDAO imeiDao = new ImeiWhiteListDAO();
private static Cache cache;
static {
cache = CacheManager.getInstance().getCache(IMEI_CACHE);
}
/**
* 重新加载IMEI白名单到缓存中
*
* @throws SQLException
*/
public static void reloadImeiWhiteListToCache() throws SQLException{
synchronized (cache) {
cache.removeAll();
List<ImeiWhiteListDTO> list = imeiDao.getAllImeiWhiteList();
for ( ImeiWhiteListDTO imeiWhiteListDTO : list ) {
Element e = new Element(imeiWhiteListDTO.getImei(),imeiWhiteListDTO);
cache.put(e);
}
}
}
/**
* 从缓存中获取某个IMEI的信息。如缓存获取失败,从DB中读取,再放入缓存
*
* @param imei
* @return
* @throws SQLException
*/
public static ImeiWhiteListDTO getImeiInfo(String imei) throws SQLException{
ImeiWhiteListDTO imeiInfo = null;
synchronized (cache) {
Element element = cache.get(imei);
if (element != null) {
// System.out.println("hit count:" + element.getHitCount());
imeiInfo = (ImeiWhiteListDTO) element.getObjectValue();
} else {
imeiInfo = imeiDao.getModelByIMEI(imei);
if (imeiInfo != null) {
cache.put(new Element(imeiInfo.getImei(),imeiInfo));
}
}
}
return imeiInfo;
}
}