SpringBoot + EhCache实现本地缓存

(1)SpringBoot + EhCache实现本地缓存
(2)SpringBoot + Redis 实现分布式缓存

SprinBoot 系列文章:
Spring Boot入门之Hello Spring Boot
SpringBoot 配置多个JdbcTemplate
SpringBoot 整合Mybatis
CAS统一登录认证(3): CAS 客户端接入实践
SpringBoot 整合Mail实现邮件发送
数据库连接池优化配置(druid,dbcp,c3p0)
SpringBoot+SpringSecurity+mysql实现认证与授权
SpringBoot+Spring Security基于内存用户认证
SpringBoot+WebSocket在线聊天室、消息推送
SpringBoot+SpringData JPA操作Mysql数据库
SpringBoot热部署值devtools配置
Spring Boot 资源文件属性配置
Spring Boot Server等内容的配置
Spring Boot + FreeMarker模板
SpringBoot+thymeleaf模板
SpringBoot +JDBC连接Mysql数据库
Zipkin分布式任务追踪
SpringBoot应用部署到外置Tomcat
Spring Boot + Swagger2 自动生成api接口文档

SpringBoot整合Shiro安全框架
SpringBoot+CAS Client 实现单点登录

SpringBoot 整合MyBatis-Plus
SpringBoot + validation 接口参数校验
Springboot+Redis 实现API接口防刷限流

ShardingSphere-ShardingJdbc 数据分片(分库、分表)
ShardingSphere-ShardingJdbc 读写分离
ShardingSphere-ShardingJdbc 数据脱敏

springboot+sms 集成腾讯云短信平台
SpringBoot+RabbitMQ 实现消息队列
快速从零搭建一个SpringBoot Web项目
从零快速搭建一个SpringBoot Web项目

SpringBoot+ElasticSearch 实现全文检索
访问ElasticSearch的几种方式
SpringBoot + Activiti 工作流引擎(一、基本概念与环境搭建)
SpringBoot + Activiti 工作流引擎(二、流程&任务操作)
SpringBoot 定时任务 实现方式

SpringBoot + EhCache实现本地缓存
SpringBoot + Redis 实现分布式缓存

背景

缓存(Cache):指将程序或系统中常用的数据对象存储在像内存这样特定的介质中,以避免在每次程序调用时,重新创建或组织数据所带来的性能损耗,从而提高了系统的整体运行速度。

以目前的系统架构来说,用户的请求一般会先经过缓存系统,如果缓存中没有相关的数据,就会在其他系统中查询到相应的数据并保存在缓存中,最后返回给调用方。

本地缓存:指程序级别的缓存组件,它的特点是本地缓存和应用程序会运行在同一个进程中,所以本地缓存的操作会非常快,因为在同一个进程内也意味着不会有网络上的延迟和开销。

本地缓存适用于单节点非集群的应用场景,它的优点是快,缺点是多程序无法共享缓存,比如分布式用户 Session 会话信息保存,由于每次用户访问的服务器可能是不同的,如果不能共享缓存,那么就意味着每次的请求操作都有可能被系统阻止,因为会话信息只保存在某一个服务器上,当请求没有被转发到这台存储了用户信息的服务器时,就会被认为是非登录的违规操作。

除此之外,无法共享缓存可能会造成系统资源的浪费,这是因为每个系统都单独维护了一份属于自己的缓存,而同一份缓存有可能被多个系统单独进行存储,从而浪费了系统资源。

分布式缓存:指将应用系统和缓存组件进行分离的缓存机制,这样多个应用系统就可以共享一套缓存数据了,它的特点是共享缓存服务和可集群部署,为缓存系统提供了高可用的运行环境,以及缓存共享的程序运行机制。

本地缓存可以使用 EhCache 和 Google 的 Guava 来实现,而分布式缓存可以使用 Redis 或 Memcached 来实现。

由于 Redis 本身就是独立的缓存系统,因此可以作为第三方来提供共享的数据缓存,而 Redis 的分布式支持主从、哨兵和集群的模式,所以它就可以支持分布式的缓存,而 Memcached 的情况也是类似的。

一、EhCache 介绍

1.1 什么是EhCache ?

Ehcache是基于Java实现的一套简单、高效、线程安全的缓存管理类库。Ehcache提供了内存、磁盘文件及分布式存储方式等多种灵活的Cache管理方案,特点是快速、轻量、可伸缩、操作灵活、支持持久化等。
  
Ehcache的原理:
Ehcache是基于Java实现的高效缓存框架,其内部采用多线程实现,采用LinkedHashMap存储元素,同时支持将数据持久化到物理磁盘上。

Ehcache的特点:
(1)快速:Ehcache内部采用多线程机制实现,数据存取性能高。

(2)轻量:Ehcache的安装包大小只有1.6MB,可以被快速、方便地继承到系统中。

(3)可伸缩:Ehcache缓存在内存和硬盘的存储可以伸缩到数几十GB,可轻松应对大数据场景。

(4)操作灵活:Ehcache提供了丰富的API接口,可实现基于主键、条件进行数据读取等。同时,Ehcache支持在运行时修改缓存配置(存活时间、空闲时间、内存的最大数据、磁盘的最大数量),提高了系统维护的灵活性。

(5)支持多种淘汰算法:Ehcache支持最近最少被使用、最少被使用和先进先出缓存策略。

(6)支持持久化:Ehcache支持将缓存数据持久化到磁盘上,在机器重启后从磁盘上重新加载缓存数据。

Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决方案(如Ehcache),但本身不直接提供缓存功能的实现。它支持注解方式使用缓存,非常方便。

一些小微服务项目又要用到缓存,但是又不想搭建 Redis 等服务,那么可以快速的配置一个 Ehcache 即可。

Ehcache的存储方式:
Ehcache的存储方式包括堆存储、堆外存储和磁盘存储。
(1)堆存储:将缓存数据存储在Java堆内存中,其特点是存取速度快,但容量有限。
(2)堆外存储:基于NIO的DirectByteBuffers实现,将缓存数据存储在堆外内存上。其特点是比磁盘存取速度快,而且不受GC的影响,可以保证响应时间的稳定性,在内存分配上开销比堆内存大,而且要求必须以字节数组方式存储,因此对象必须在存储过程中进行序列化,对读取操作则进行反序列化,数据存取速度比堆内存慢一个数量级。
(3)磁盘存储:将数据存储在磁盘上,保障服务重启后内存数据能够重新从磁盘上加载,其读取效率最低,是内存数据持久化的一种方式。

1.2 使用方式

可以单独使用,一般在第三方库中被用到的比较多(如mybatis、shiro等)ehcache 对分布式支持不够好,多个节点不能同步,通常和redis一块使用

1.3 ehcache 和 redis 比较

ehcache直接在jvm虚拟机中缓存,速度快,效率高;但是缓存共享麻烦,集群分布式应用不方便。

redis是通过socket访问到缓存服务,效率比Ehcache低,比数据库要快很多,处理集群和分布式缓存方便,有成熟的方案。如果是单个应用或者对缓存访问要求很高的应用,用ehcache。如果是大型系统,存在缓存共享、分布式部署、缓存内容很大的,建议用redis。

ehcache也有缓存共享方案,不过是通过RMI或者Jgroup多播方式进行广播缓存通知更新,缓存共享复杂,维护不方便;简单的共享可以,但是涉及到缓存恢复,大数据缓存,则不合适。

二、与SpringBoot集成

在Spring Boot中使用Ehcache组件比较简单,分为引入jar包、配置ehcache.xml和使用Ehcache缓存,具体实现如下。

2.1 在pom中引入ehcache相关依赖

<!--开启Springboot cache 缓存 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- ehcache 缓存 -->
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.8.1</version>
</dependency>
<dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId>
    <version>1.1.1</version>
</dependency>

2.2 在启动Application类中加上缓存注解@EnableCaching

@SpringBootApplication
//开启缓存
@EnableCaching
public class EhcacheApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(EhcacheApplication.class, args);
    }

}

2.3 在 resources 下 创建 ehcache.xml文件,文件内容如下:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.ehcache.org/v3"
        xmlns:jsr107="http://www.ehcache.org/v3/jsr107"
        xsi:schemaLocation="
            http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
            http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
    <service>
        <jsr107:defaults enable-statistics="true"/>
    </service>

    <!-- user 为该缓存名称 对应@Cacheable的属性cacheNames-->
    <cache alias="user_cache" >
        <!-- 指定缓存 key 类型,对应@Cacheable的属性key -->
        <key-type>java.lang.String</key-type>
        <!-- 配置value类型 -->
        <value-type>com.oycbest.ehcache.entity.OyUser</value-type>
        <expiry>
            <!-- 缓存 ttl,单位为分钟,现在设置的是1个小时 -->
            <ttl unit="minutes">60</ttl>
        </expiry>
        <resources>
            <!-- 分配资源大小 -->
            <heap unit="entries">2000</heap>
            <offheap unit="MB">100</offheap>
        </resources>
    </cache>
    <!--这里可以配置N个 。。。。 不同的cache 根据业务情况配置-->
</config>

单个cache标签可以配置多个,这里注意的一点是 ttl 设置。这个是很有用的功能。
在 application.properties配置文件中配置:

spring.cache.jcache.config=classpath:ehcache.xml

指定jcache config配置文件路径。

2.4 Cache 使用

缓存的对象必须支持序列化,因为 Ehcache 会在一定的规则下会序列化后存储到硬盘上,所以要implements Serializable。 另外这里这里就2点简单讲解使用的方法。

2.4.1 手动使用Ehcache

首先Spring本身就定义了缓存接口Cache和管理缓存控制器 CacheManager (有点像java中的Map)。

直接在在需要使用的类中注入即可,再提醒一次注意引入的包路径,用 @Resource 不要用 @Autowired。

手动存储和获取对象。

@Resource
    CacheManager cacheManager;

    @GetMapping("cacheTest/{userId}")
    public OyUser cacheTest(@PathVariable Integer userId) {
    
    
        //获取所有cache 配置
        Iterable<String> cacheNames = cacheManager.getCacheNames();
        cacheNames.forEach(name -> System.out.println("name = " + name));

        //获取缓存对象,"user_cache" 就是 ehcache.xml 中 <cache> 标签的 alias
        Cache cache = cacheManager.getCache("user_cache");
        Cache.ValueWrapper valueWrapper = cache.get("userId-" + userId);
        if (valueWrapper != null && valueWrapper instanceof OyUser) {
    
    
            return (OyUser) valueWrapper.get();
        }
        //创建一个对象
        OyUser user = oyUserService.getUserById(userId);
        System.out.println(user.toString());

        //存入缓存
        cache.put("userId-" + userId, user);

        //获取刚刚存入的值
        valueWrapper = cache.get("userId-" + userId);
        if (null != valueWrapper) {
    
    
            //这里获取  ehcache.xml 中 <cache>  value-type 定义的类型,可以直接强转。
            OyUser obj = (OyUser) valueWrapper.get();
            //输出
            System.out.println(obj.toString());
        }
        return user;
    }

2.4.2 通过@Cacheable注解

可以直接注解到我们的方法上,再次查询如果缓存没过期,就直接返回缓存内容,这样 Ehcache 和数据库配合起来使用就非常方便。

@GetMapping("{id}")
    @Cacheable(cacheNames = "user_cache", key = "'userId'+#id")// 从缓存获取,key为ID,缓存具体看 ehcache.xml 配置文件
    public OyUser selectOne(@PathVariable("id") Integer id) {
    
    
        // 从数据库查询并记录到缓存
        OyUser user = oyUserService.getUserById(id);
        return user;
    }

猜你喜欢

转载自blog.csdn.net/u014553029/article/details/113944676