程序员首页投稿程序员首页投稿(暂停使用,暂停投稿)

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

2018-02-12  本文已影响166人  虾游于海

在上一集中,我们使用了默认的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进行配置,以实现缓存过期等策略。具体的包括

spring:
  cache:
    cache-names:
    - test

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

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官方网站,不在这里展开。

@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枚举中。

点击这里下载相关代码

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

过年了,新年快乐。

上一篇 下一篇

猜你喜欢

热点阅读