拿什么拯救你的性能(2) - 更换Cache实现

在上一集中,我们使用了默认的ConcurrentMapCacheManager作为Spring Cache的默认实现。在小型的应用中,这已经足够了,但在实际的项目中,我们可能需要引入JCache,Redis,MemCache等更加成熟的缓存技术.下面我们就来看看吧.

JCache

JCache通常也叫JSR-107,JCache是一套规范.
JCACHE规范承诺为Java提供一套标准API,通过这套API,编程人员可以透明地操作数据,不用关心数据放在哪里.
我们来看看,它的初衷和Spring Cache的目的是一样的,都是一套规范.
同时,JCache提供了一套类似于Spring Cache的annotation来标注方法和类.包括@CacheResult,@CachePut,@CacheRemove等,这些注解都位于javax.cache.annotation包下.
在使用Spring Cache的时候,我们也可以使用JCache的注解,Spring 能正确的根据注解实现相应的缓存逻辑.
JCache有各种实现,比较典型的是EhCache3,Hazelcast等,Spring Cache可以直接使用JCache的各种实现来作为自己的Cache实现. 下面我们以Spring Boot使用EhCache为例。改造上一次的代码

首先,导入必要依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-cache</artifactId>
 </dependency>
<dependency>
  <groupId>javax.cache</groupId>
  <artifactId>cache-api</artifactId>
</dependency>
<dependency>
  <groupId>org.ehcache</groupId>
  <artifactId>ehcache</artifactId>
</dependency>

这里的cache-api就是JSR-107所定义的相关的Cache的接口.
ehcache就是ehcache3.

启用Spring Cache

@SpringBootApplication
@EnableCaching
public class EhCacheApplication {
  public static void main(String[] args) throws Exception {
    SpringApplication.run(EhCacheApplication.class, args);
  }
}

我们所有的代码没有变化,在controller中加入一个接口,用来获取系统中实际使用的cacheManager

/**
 * 注入Spring Boot生成的CacheManager
 */
@Autowired
private CacheManager cacheManager;

/**
 * 获取并显示实际使用的CacheManager
 */
@GetMapping("manager")
public String getManager() {
  return cacheManager.toString();
}

在浏览器中输入相关URL,可以获得结果
org.springframework.cache.jcache.JCacheCacheManager@xxxx
我们可以注释掉相关的依赖,即pom.xml中的cache-api和ehcache两项,再运行相关的接口,接口会返回相应的数据
org.springframework.cache.concurrent.ConcurrentMapCacheManager@xxxx.

Spring Boot会按照如下优先级来自动装配一个CacheManager
Generic
JCache (JSR-107) (EhCache 3, Hazelcast, Infinispan, etc)
EhCache 2.x
Hazelcast
Infinispan
Couchbase
Redis
Caffeine
Guava (deprecated)
Simple
参见Spring Boot文档.
当Spring Boot按照以上顺序检测到某一个缓存实现存在的话,会自动构建一个使用相应实现的CacheManager.并跳过后续检测。

配置JCache的配置文件

我们使用ehcache3作为缓存实现的时候,可以对ehcache进行配置,以实现缓存过期等策略。具体的包括

  • 简单配置
    我们的代码,到目前为止,当我们启用了ehcache3,并进行了相关的接口调用的时候,会抛出异常java.lang.IllegalArgumentException,Cannot find cache named 'test' for Builder.这是因为ehcahce需要我们显示的声明Cache.
    我们可以在Spring Boot的配置文件 src\main\resources\application.yml中声明Cache
spring:
  cache:
    cache-names:
    - test

cache-names是一个列表,声明了在项目中用到的cache的名称。

  • 完整的Ehcache配置
    在yml/properties文件中,我们只能进行一些简单的配置。如果要进行复杂的配置,我们需要指定一个配置文件所在的路径。
    修改后的yml如下
spring:
  cache:
    jcache:
      config: classpath:cache.xml

并且,我们在resources目录下面建立cache.xml文件。进行cache的配置

<?xml version="1.0" encoding="UTF-8"?>
<config xmlns='http://www.ehcache.org/v3' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
    xsi:schemaLocation="http://www.ehcache.org/v3
        http://www.ehcache.org/schema/ehcache-core-3.0.xsd">

    <cache-template name="default">
        <heap unit="MB">1</heap>
    </cache-template>

    <cache alias="test" uses-template="default">
        <expiry>
            <ttl unit="seconds">10</ttl>
        </expiry>
    </cache>
</config>

在这里,我们通过alias值定了cache的名称,通过ttl指定了cache过期时间,这里指定为10秒。我们重新访问接口时,当距离上次缓存的时间大于10秒钟的时候,都会重新执行一次方法。
更多ehcache的配置文件,可以参考Ehcache官方网站,不在这里展开。

  • 缓存并发穿透
    并发穿透,指的是当缓存过期失效后。如果瞬间有大量的请求进来,这些请求在执行的时候,均会查询缓存,这些查询都会导致缓存没有命中,进而执行实际的代码。如果这些代码是持久层的操作,或者是比较耗时的操作的,会导致计算压力瞬间倍增。
    通常要解决这些场景的问题,需要一些线程同步的能力,但在Spring Cache 4.3以后的版本中,这种情况大为改善。我们只需要在Cacheable的注解中,指定sync=true即可。示例代码如下。
@Override
@Cacheable(cacheNames = "test", sync = true)
 public String get(String id) {
    // 记录数据产生的时间,用于测试对比
    long time = new Date().getTime();
    // 打印使用到的cacheManager
    logger.info("The cacheManager is" + cacheManager);
    // 当数据不是从cache里面获取时,打印日志
    logger.info("Get value by id=" + id + ", The time is " + time);
    return "Get value by id=" + id + ",the value is " + enties.get(id);
}

当缓存失效之后,有请求并发访问到这里的时,只会有一个线程实际执行方法体,其它的请求等待之前的线程执行并缓存结果。这大大简化了并发的处理逻辑。

Redis

redis是一个常用的集中式缓存服务。Spring对Redis也进行了集成,我们可以方法的使用RedisTemplate进行Redis的读写操作。当然,我们也可以非常方便的将Spring的缓存实现更改为Redis实现,你只需要加入Redis相关的依赖。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

然后在配置文件中配置相关的redis连接信息。以上的代码不用更改,即可实现使用Redis作为缓存的实现。

指定缓存类型

当项目中同时存在多种技术时,Spring会按照一定的顺序去寻找缓存的实现。但有时候我们需要显示的指定缓存的实现,比如当比如Jcache和redis共存时,Spring会使用JCache作为缓存的实现,而事实上我们可能需要的是Redis.这时,就需要我们手工显示的指定实现。在yml/properties文件中指定即可

spring:
  cache:
    type: redis

可供选择的类型在org.springframework.boot.autoconfigure.cache.CacheType枚举中。

点击这里下载相关代码

小结:缓存在互联网时代是非常重要的技术,也不是一两篇文章就能讲完的,大家一起研究,一起学习。
距离上一篇已经过去了一个月了,码字的速度好慢。

过年了,新年快乐。

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

推荐阅读更多精彩内容