SpringBoot中引入Ehcache3.x

1.Ehcache介绍

Ehcache是一个用Java实现的简单、高速、线程安全的缓存管理类库。具体快速、简单、低消耗、依赖性小、扩展性强、支持对象或序列化缓存、支持缓存或元素的失效、提供LRU/LFU/FIFO缓存策略、支持内存缓存及磁盘缓存、采用分布式缓存机制等特点.

2.引入依赖

在项目的pom.xml中添加下面这三个依赖:

<dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId>
</dependency>
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.7.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
  • cache-api是JSR-107 Cache的规范,定义了一列接口规范(但是这只是一种规范,需要使用它的实现,例如ehcache3.x、Hazelcast等)。
  • ehcache是ehcache的功能包。
  • springBoot要支持第三方缓存的话,还需要引入spring-boot-starter-cache

3.配置属性

3.1 配置application.properties

在application.properties添加如下配置:

# 可选,配置了spring.cache.jcache.config属性会自动装配JCacheCacheManager
spring.cache.type=jcache
# 指定ehcache的配置文件所在的位置
spring.cache.jcache.config=classpath:ehcache-3.x.xml

3.2配置ehcache-3.x.xml

在resources文件夹下新建ehcache-3.x.xml,添加如下内容:

<?xml version="1.0" encoding="UTF-8" ?>
<eh:config
        xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
        xmlns:eh='http://www.ehcache.org/v3'
        xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.3.xsd">
    <!--指定缓存目录-->
    <eh:persistence directory="${java.io.tmpdir}/cache-data"/>

    <!--缓存模板-->
    <eh:cache-template name="default">
        <eh:expiry>
            <eh:ttl unit="seconds">600</eh:ttl>
        </eh:expiry>
        <eh:resources>
            <!--堆内内存可以放2000个条目,超出部分堆外100MB-->
            <eh:heap unit="entries">2000</eh:heap>
            <eh:offheap unit="MB">100</eh:offheap>
        </eh:resources>
    </eh:cache-template>

    <!--实际的缓存区间,继承了default缓存模板,sample完全使用模板默认-->
    <eh:cache alias="sample" uses-template="default"></eh:cache>

    <!--下面两个继承了default缓存模板,但覆盖了缓存的过期时间-->
    <eh:cache alias="authority_service" uses-template="default">
        <eh:expiry>
            <eh:ttl unit="hours">1</eh:ttl>
        </eh:expiry>
    </eh:cache>

    <eh:cache alias="shop_service" uses-template="default">
        <eh:expiry>
            <eh:ttl unit="hours">24</eh:ttl>
        </eh:expiry>
    </eh:cache>
</eh:config>

更多配置知识,请参考ehcache官网:ehcache XML配置

4.使用示例

4.1 准备一个controller

该controller只有一个方法,获得某类商品的列表:

@RestController
@Validated
@RequestMapping("/v1.0/api/shop")
public class ShopController {
  @Autowired
  ShopService shopService;

  @RequestMapping(value = {"/commodity"}, method = RequestMethod.GET)
  public List<String> listCommodity (@RequestParam String type) {
    System.out.println("ShopController: type is " + type);
    return shopService.listCommodity(type);
  }

}

4.2 准备一个service

  1. 在需要使用缓存的Bean上面添加@EnableCaching注解,那该bean具有缓存功能。
  2. 在需要使用缓存的方法上添加@Cacheable注解,那该方法具有缓存功能(前提是该bean具有缓存的功能)。

注意: 1和2配合起来才能使某个bean的某个方法具有缓存的功能。

package com.example.demo.service;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
@EnableCaching
public class ShopService {
  // value:使用叫做'shop_service'的缓存器
  // key: 缓存的key等于#type,即传入的key值
  // condition:缓存的条件,当#type等于phone时,才进行缓存
  @Cacheable(cacheNames = "shop_service", key = "#type", condition = "#type == 'phone'")
  public List<String> listCommodity(String type) {
    System.out.println("ShopService: 调用了listCommodity");
    List<String> commodities = new ArrayList<>();
    if (type.equals("phone")) {
      commodities.add("Apple");
      commodities.add("HuaWei");
    } else {
      commodities.add("others");
    }
    return commodities;
  }
}

4.3 启动springBoot,进行测试

使用浏览器,发送4个请求:

  1. http://localhost:8080/v1.0/api/shop/commodity?type=phone
  2. http://localhost:8080/v1.0/api/shop/commodity?type=phone
  3. http://localhost:8080/v1.0/api/shop/commodity?type=computer
  4. http://localhost:8080/v1.0/api/shop/commodity?type=computer

后台打印的日志如下:

ShopController: type is phone
ShopService: 调用了listCommodity
ShopController: type is phone

ShopController: type is computer
ShopService: 调用了listCommodity
ShopController: type is computer
ShopService: 调用了listCommodity

4.4 结果分析

第一次发送请求,符合缓存的条件,由于没有缓存,于是执行了service的逻辑,并将结果缓存到了ehcache中。
第二次发送请求,符合缓存的条件,由于已经缓存了结果,直接从ehcache中拿取缓存的结果返回,没有进入到service的逻辑。
第三次和第四次都不符合缓存的条件,需要进入到service的逻辑计算结果。

5.Ehcache使用场景

使用的过程中,根据优点和缺点进行权衡后再应用到项目中去,Ehcache缓存也是如此,在实际工作有很多使用场景,通常将Ehcache作为Redis的二次缓存使用。

5.1 Ehcache的适用场景

(1) 比较少的更新数据表的情况下
Ehcache作为Hibernate的缓存时,在进行修改表数据(save、update、delete等)的时候,Ehcache会自动把缓存中关于此表的所有缓存全部删除掉,这样做只是能达到同步,但对于数据经常修改的表来说,可能就失去了缓存的意义了。
(2)对一致性要求不高的情况下
因为Ehcache本地缓存的特性,目前无法很好的解决不同服务器缓存同步的问题,所以在一致性要求高的场合下,建议使用Redis、Memcached等集中式缓存。

5.2Ehcache的缺陷

(1) 缓存漂移
每个应用节点只管理自己的缓存,在更新某个节点的时候,不会影响到其他的节点,这样数据之间可能就不同步了。
(2) 数据库瓶颈
对于单实例的应用来说,缓存可以保护数据库的读风暴;但是在集群的环境下,每一个应用节点都要定期保存数据更新,节点越多,要维持这样的情况对数据库的开销也越大。

5.3 Ehcache的正确打开方式

我们在项目中使用集中式缓存(Redis或Memcached等)通常都是检查缓存中是否存在期望的数据,如果存在直接将数据返回,如果不存在就查询数据库然后再将数据缓存,而后将结果返回。这时候如果缓存系统因为某些原因宕机,造成服务无法访问,那么大量的请求将直接穿透到数据库,对数据库造成巨大的压力。

针对上述情况,我们有多种可行的解决方法,其中一种方案是将Ehcache作为集中式缓存的二级本地缓存,这样当缓存系统宕机后,服务器应用的本地缓存还能继续抗住大量请求。

使用了Ehcache作为本地缓存后,可能会出现本地缓存与缓存系统之间出现数据不一致的情况,因为本地缓存是在服务器应用中存在,在实际生产环境中必定是多台服务器分别部署,如何能够在更新缓存系统数据的同时,也能够更新Ehcache的缓存数据,以及保证不同服务器间Ehcache本地缓存数据的同步问题。

一般有两种解决方案可供参考:
第一种:定时轮询
每台应用服务器定时轮询Redis缓存,更新本地的Ehcache缓存。
第二种:主动通知
每台应用服务器的Ehcache同步侦听MQ消息,通过MQ推送的方式,将redis中更新的缓存数据推送到每台应用服务器中。

针对上述的分析,可形成如下的缓存方案:


缓存方案.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 原文连接:https://my.oschina.net/coolfire368/blog/123377 ehcac...
    晴天哥_王志阅读 1,289评论 0 1
  • Ehcache是现在最流行的纯Java开源缓存框架,配置简单、结构清晰、功能强大,最初知道它,是从Hibernat...
    安易学车阅读 1,998评论 0 11
  • 一、简介 Ehcache是一个用Java实现的使用简单,高速,实现线程安全的缓存管理类库,ehcache提供了用内...
    小程故事多阅读 43,467评论 9 59
  • 理论总结 它要解决什么样的问题? 数据的访问、存取、计算太慢、太不稳定、太消耗资源,同时,这样的操作存在重复性。因...
    jiangmo阅读 2,774评论 0 11
  • 湿地公园有熊猫可看,一对姐妹花。 发了张照片给丫头,半月留校未归,让我得空陪爸妈闲游。公园靠近学校,没有去看她,零...
    南遇北辞阅读 597评论 0 5