本地缓存Ehcache

房项禹
2023-12-01

缓存框架分类

  • 单点缓存框架(只能针对于单个 jvm 中,缓存容器存放在 jvm 中,每个 jvm 互不影响)有 ehcache、guava cache、oscache
  • 分布式缓存框架(共享缓存数据)有 Redis、memchache

什么是Ehcache

Ehcache是纯java的开源缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。它主要面向通用缓存、Java EE和轻量级容器,具有内存和磁盘存储、缓存加载器、缓存扩展、缓存异常处理程序。
Ehcache最初由Greg Luck于2003年开始开发。2009年,该项目被Terracotta购买。软件仍然开源,但一些新的主要功能(例如,快速可重启性之间的一致性的)只能在商业产品中使用。
Ehcache 被广泛用于在Hibernate、Spring、Cocoon等其他开源系统。

Ehcache的主要特性

  1. 快速;
  2. 简单;
  3. 多种缓存策略;
  4. 缓存数据有两级:内存和磁盘,因此无需担心容量问题;
  5. 缓存数据会在虚拟机重启的过程中写入磁盘;
  6. 可以通过 RMI、可插入 API 等方式进行分布式缓存;
  7. 具有缓存和缓存管理器的侦听接口;
  8. 支持多缓存管理器实例,以及一个实例的多个缓存区域;
  9. 提供 Hibernate 的缓存实现;

Ehcache使用介绍

Ehcache是用来管理缓存的一个工具,其缓存的数据可以是存放在内存里面的,也可以是存放在硬盘上的。其核心是CacheManager,一切Ehcache的应用都是从CacheManager开始的。它是用来管理Cache(缓存)的,一个应用可以有多个CacheManager,而一个CacheManager下又可以有多个Cache。Cache内部保存的是一个个的Element,而一个Element中保存的是一个key和value的配对,相当于Map里面的一个Entry。

Ehcache缓存过期策略

当缓存需要被清理时(比如空间占用已经接近临界值了),需要使用某种淘汰算法来决定清理掉哪些数据。常用的淘汰算法有下面几种:

  • FIFO:First In First Out,先进先出。判断被存储的时间,离目前最远的数据优先被淘汰。
  • LRU:Least Recently Used,最近最少使用。判断最近被使用的时间,目前最远的数据优先被淘汰。
  • LFU:Least Frequently Used,最不经常使用。在一段时间内,数据被使用次数最少的,优先被淘汰。

SpringBoot2.0整合Ehcache框架

Maven环境依赖
<!--开启 cache 缓存 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<!-- ehcache缓存 -->
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.4</version>
</dependency>
YML配置文件信息
###端口号配置
server:
  port: 8081
###数据库配置  
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    test-while-idle: true
    test-on-borrow: true
    validation-query: SELECT 1 FROM DUAL
    time-between-eviction-runs-millis: 300000
    min-evictable-idle-time-millis: 1800000
  # 缓存配置读取
  cache:
    type: ehcache
    ehcache:
      config: classpath:app1_ehcache.xml
App启动方式:

加上@EnableCaching 开启ehcache缓存模式

@MapperScan(basePackages = { "com.xiaoming.mapper" })
@SpringBootApplication
@EnableCaching
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}
项目使用:

@CacheConfig:配置缓存基本信息 、cacheNames :缓存名称、@Cacheable 该方法查询数据库完毕之后,存入到缓存中

@CacheConfig(cacheNames = "userCache") //缓存名称
public interface UserMapper {
	@Select("SELECT ID ,NAME,AGE FROM users where id=#{id}")
	@Cacheable //开启缓存
	List<Users> getUser(@Param("id") Long id);
}
EhCache配置

app1_ehcache

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">

    <diskStore path="java.io.tmpdir/ehcache-rmi-4000" />

    <!-- 多台机器配置 -->
    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
            properties="peerDiscovery=manual,rmiUrls=//127.0.0.1:5000/userCache" />
    <!-- 配置rmi集群模式,集群监听地址和端口号 -->
    <cacheManagerPeerListenerFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
            properties="hostName=127.0.0.1,port=4000,socketTimeoutMillis=120000" />
    <!-- 多播方式配置 搜索某个网段上的缓存 timeToLive 0是限制在同一个服务器 1是限制在同一个子网 32是限制在同一个网站 64是限制在同一个region
      128是限制在同一个大洲 255是不限制 <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
      properties="peerDiscovery=automatic, multicastGroupAddress=224.1.1.1, multicastGroupPort=40000,
      timeToLive=32" /> -->

    <!-- 默认缓存 -->
    <defaultCache maxElementsInMemory="1000" eternal="true"
                  timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
                  diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
                  diskPersistent="true" diskExpiryThreadIntervalSeconds="120"
                  memoryStoreEvictionPolicy="LRU">
    </defaultCache>

    <!-- demo缓存 userCache -->
    <cache name="userCache" maxElementsInMemory="1000" eternal="false"
           timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
           diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
           diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
           memoryStoreEvictionPolicy="LRU">
        <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" />
        <!-- 用于在初始化缓存,以及自动设置 -->
        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" />
    </cache>
</ehcache>

app2_ehcache

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">

    <diskStore path="java.io.tmpdir/ehcache-rmi-4000" />

    <!-- 多台机器配置 -->
    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
            properties="peerDiscovery=manual,rmiUrls=//127.0.0.1:4000/userCache" />
    <!-- 配置rmi集群模式,集群监听地址和端口号 -->
    <cacheManagerPeerListenerFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
            properties="hostName=127.0.0.1,port=5000,socketTimeoutMillis=120000" />

    <!-- 多播方式配置 搜索某个网段上的缓存 timeToLive 0是限制在同一个服务器 1是限制在同一个子网 32是限制在同一个网站 64是限制在同一个region
      128是限制在同一个大洲 255是不限制 <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
      properties="peerDiscovery=automatic, multicastGroupAddress=224.1.1.1, multicastGroupPort=40000,
      timeToLive=32" /> -->

    <!-- 默认缓存 -->
    <defaultCache maxElementsInMemory="1000" eternal="true"
                  timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
                  diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
                  diskPersistent="true" diskExpiryThreadIntervalSeconds="120"
                  memoryStoreEvictionPolicy="LRU">
    </defaultCache>

    <!-- demo缓存 userCache -->
    <cache name="userCache" maxElementsInMemory="1000" eternal="false"
           timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
           diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
           diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
           memoryStoreEvictionPolicy="LRU">
        <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" />
        <!-- 用于在初始化缓存,以及自动设置 -->
        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" />
    </cache>
</ehcache>
清除缓存
@Autowired
	private CacheManager cacheManager;

	@RequestMapping("/remvoKey")
	public void remoKey() {
		cacheManager.getCache("userCache").clear();
	}

如果出现错误

Cache configuration does not exist 'class path resource [app2_ehcache.xml]'

加上依赖

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache-core</artifactId>
    <version>2.6.10</version>
</dependency>

缓存与数据库不同步的问题

update(修改)或者del(删除),生产环境下是不能随意改变数据库值。
update 语句后面加上主动通知。先修改,修改成功之后,再主动清理缓存。这是在同一个事务中。
清除缓存,就能解决缓存与数据库不同步的问题

Ehcache集群模式

由于 EhCache 是进程中的缓存系统,一旦将应用部署在集群环境中,每一个节点维护各自的缓存数据,当某个节点对缓存数据进行更新,这些更新的数据无法在其它节点中共享,这不仅会降低节点运行的效率,而且会导致数据不同步的情况发生。例如某个网站采用 A、B 两个节点作为集群部署,当 A 节点的缓存更新后,而 B 节点缓存尚未更新就可能出现用户在浏览页面的时候,一会是更新后的数据,一会是尚未更新的数据,尽管我们也可以通过 Session Sticky 技术来将用户锁定在某个节点上,但对于一些交互性比较强或者是非 Web 方式的系统来说,Session Sticky 显然不太适合。

常用集群模式

EhCache从1.7版本开始,支持五种集群方案,分别是:Terracotta、RMI、JMS、JGroups、EhCache Server

RMi集群模式
  1. 正确的元素类型:只有可序列化的元素可以进行复制。一些操作,比如移除,只需要元素的键值而不用整个元素;在这样的操作中即使元素不是可序列化的但键值是可序列化的也可以被复制。
  2. 成员发现(Peer Discovery):Ehcache进行集群的时候有一个cache组的概念。每个cache都是其他cache的一个peer,没有主cache的存在。成员发现(Peer Discovery)正是用来解决 “你如何知道集群环境中的其他缓存?” 这个问题的。Ehcache提供了两种机制用来进行成员发现,即:自动成员发现和手动成员发现。要使用一个内置的成员发现机制要在ehcache的配置文件中指定cacheManagerPeerProviderFactory元素的class属性为
    net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory。

RMI 和 RPC 理念是相同的。RMI 是 Java 语言中 JVM 与 JVM 实现远程调用方式(不能跨平台)。RPC 也属于远程调用技术,rpc 底层使用 socket,因为 http 协议底层使用 socket,绝大多数语言都支持 socket(二进制)能够跨平台

 类似资料: