上一篇我们介绍了在Spring Boot中整合EhCache的方法。既然用了ehcache,我们自然要说说它的一些高级功能,不然我们用默认的ConcurrentHashMap就好了。本篇不具体介绍EhCache缓存如何落文件、如何配置各种过期参数等常规细节配置,这部分内容留给读者自己学习,如果您不知道如何搞,可以看看这里的官方文档

那么我们今天具体讲什么呢?先思考一个场景,当我们使用了EhCache,在缓存过期之前可以有效的减少对数据库的访问,但是通常我们将应用部署在生产环境的时候,为了实现应用的高可用(有一台机器挂了,应用还需要可用),肯定是会部署多个不同的进程去运行的,那么这种情况下,当有数据更新的时候,每个进程中的缓存都是独立维护的,如果这些进程缓存同步机制,那么就存在因缓存没有更新,而一直都用已经失效的缓存返回给用户,这样的逻辑显然是会有问题的。所以,本文就来说说当使用EhCache的时候,如果来组建进程内缓存EnCache的集群以及配置配置他们的同步策略。

由于下面是组建集群的过程,务必采用多机的方式调试,避免不必要的错误发生。

动手试试

本篇的实现将基于上一篇的基础工程来进行。先来回顾下上一篇中的程序要素:

User实体的定义

@Entity
@Data
@NoArgsConstructor
public class User { @Id
@GeneratedValue
private Long id; private String name;
private Integer age; public User(String name, Integer age) {
this.name = name;
this.age = age;
}
}

User实体的数据访问实现(涵盖了缓存注解)

@CacheConfig(cacheNames = "users")
public interface UserRepository extends JpaRepository<User, Long> { @Cacheable
User findByName(String name); }

下面开始改造这个项目:

第一步:为需要同步的缓存对象实现Serializable接口

@Entity
@Data
@NoArgsConstructor
public class User implements Serializable { @Id
@GeneratedValue
private Long id; private String name;
private Integer age; public User(String name, Integer age) {
this.name = name;
this.age = age;
}
}

注意:如果没有做这一步,后续缓存集群通过过程中,因为要传输User对象,会导致序列化与反序列化相关的异常

第二步:重新组织ehcache的配置文件。我们尝试手工组建集群的方式,不同实例在网络相关配置上会产生不同的配置信息,所以我们建立不同的配置文件给不同的实例使用。比如下面这样:

实例1,使用ehcache-1.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"> <cache name="users"
maxEntriesLocalHeap="200"
timeToLiveSeconds="600">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
properties="replicateAsynchronously=true,
replicatePuts=true,
replicateUpdates=true,
replicateUpdatesViaCopy=false,
replicateRemovals=true "/>
</cache> <cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="hostName=10.10.0.100,
port=40001,
socketTimeoutMillis=2000,
peerDiscovery=manual,
rmiUrls=//10.10.0.101:40001/users" /> </ehcache>

实例2,使用ehcache-2.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"> <cache name="users"
maxEntriesLocalHeap="200"
timeToLiveSeconds="600">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
properties="replicateAsynchronously=true,
replicatePuts=true,
replicateUpdates=true,
replicateUpdatesViaCopy=false,
replicateRemovals=true "/>
</cache> <cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="hostName=10.10.0.101,
port=40001,
socketTimeoutMillis=2000,
peerDiscovery=manual,
rmiUrls=//10.10.0.100:40001/users" /> </ehcache>

配置说明:

  • cache标签中定义名为users的缓存,这里我们增加了一个子标签定义cacheEventListenerFactory,这个标签主要用来定义缓存事件监听的处理策略,它有以下这些参数用来设置缓存的同步策略:

    • replicatePuts:当一个新元素增加到缓存中的时候是否要复制到其他的peers。默认是true。
    • replicateUpdates:当一个已经在缓存中存在的元素被覆盖时是否要进行复制。默认是true。
    • replicateRemovals:当元素移除的时候是否进行复制。默认是true。
    • replicateAsynchronously:复制方式是异步的指定为true时,还是同步的,指定为false时。默认是true。
    • replicatePutsViaCopy:当一个新增元素被拷贝到其他的cache中时是否进行复制指定为true时为复制,默认是true。
    • replicateUpdatesViaCopy:当一个元素被拷贝到其他的cache中时是否进行复制指定为true时为复制,默认是true。
  • 新增了一个cacheManagerPeerProviderFactory标签的配置,用来指定组建的集群信息和要同步的缓存信息,其中:
    • hostName:是当前实例的主机名
    • port:当前实例用来同步缓存的端口号
    • socketTimeoutMillis:同步缓存的Socket超时时间
    • peerDiscovery:集群节点的发现模式,有手工与自动两种,这里采用了手工指定的方式
    • rmiUrls:当peerDiscovery设置为manual的时候,用来指定需要同步的缓存节点,如果存在多个用|连接

第三步:打包部署与启动。打包没啥大问题,主要缓存配置内容存在一定差异,所以在指定节点的模式下,需要单独拿出来,然后使用启动参数来控制读取不同的配置文件。比如这样:

-Dspring.cache.ehcache.config=classpath:ehcache-1.xml
-Dspring.cache.ehcache.config=classpath:ehcache-2.xml

第四步:实现几个接口用来验证缓存的同步效果

@RestController
static class HelloController { @Autowired
private UserRepository userRepository; @GetMapping("/create")
public void create() {
userRepository.save(new User("AAA", 10));
} @GetMapping("/find")
public User find() {
User u1 = userRepository.findByName("AAA");
System.out.println("查询AAA用户:" + u1.getAge());
return u1;
} }

验证逻辑:

  1. 启动通过第三步说的命令参数,启动两个实例
  2. 调用实例1的/create接口,创建一条数据
  3. 调用实例1的/find接口,实例1缓存User,同时同步缓存信息给实例2,在实例1中会存在SQL查询语句
  4. 调用实例2的/find接口,由于缓存集群同步了User的信息,所以在实例2中的这次查询也不会出现SQL语句

进一步思考

上一篇发布的时候,公众号上有网友留言问,数据更新之后怎么办?

其实当构建了缓存集群之后,就比较好办了。比如这里的例子,需要做两件事:

  1. save操作增加@CachePut注解,让更新操作完成之后将结果再put到缓存中
  2. 保证缓存事件监听的replicateUpdates=true,这样数据在更新之后可以保证复制到其他节点

这样就可以防止缓存的脏数据了,但是这种方法还并不是很好,因为缓存集群的同步依然需要时间,会存在短暂的不一致。同时进程内的缓存要在每个实例上都占用,如果大量存储的话始终不那么经济。所以,很多时候进程内缓存不会作为主要的缓存手段。下一篇将具体说说,另一个更重要的缓存使用!

欢迎关注本系列教程:《Spring Boot 2.x基础教程》

参考资料

本文首发:Spring Boot 2.x基础教程:使用EhCache缓存集群,转载请注明出处。

欢迎关注我的公众号:程序猿DD,获得独家整理的学习资源和日常干货推送。点击直达本系列教程目录

Spring Boot 2.x基础教程:使用EhCache缓存集群的更多相关文章

  1. Spring Boot 2.x基础教程:EhCache缓存的使用

    上一篇我们学会了如何使用Spring Boot使用进程内缓存在加速数据访问.可能大家会问,那我们在Spring Boot中到底使用了什么缓存呢? 在Spring Boot中通过@EnableCachi ...

  2. Spring Boot 2.x基础教程:使用集中式缓存Redis

    之前我们介绍了两种进程内缓存的用法,包括Spring Boot默认使用的ConcurrentMap缓存以及缓存框架EhCache.虽然EhCache已经能够适用很多应用场景,但是由于EhCache是进 ...

  3. Spring Boot 2.x基础教程:使用Swagger2构建强大的API文档

    随着前后端分离架构和微服务架构的流行,我们使用Spring Boot来构建RESTful API项目的场景越来越多.通常我们的一个RESTful API就有可能要服务于多个不同的开发人员或开发团队:I ...

  4. Spring Boot 2.x基础教程:JSR-303实现请求参数校验

    请求参数的校验是很多新手开发非常容易犯错,或存在较多改进点的常见场景.比较常见的问题主要表现在以下几个方面: 仅依靠前端框架解决参数校验,缺失服务端的校验.这种情况常见于需要同时开发前后端的时候,虽然 ...

  5. Spring Boot 2.x基础教程:Swagger接口分类与各元素排序问题详解

    之前通过Spring Boot 2.x基础教程:使用Swagger2构建强大的API文档一文,我们学习了如何使用Swagger为Spring Boot项目自动生成API文档,有不少用户留言问了关于文档 ...

  6. Spring Boot 2.x基础教程:Swagger静态文档的生成

    前言 通过之前的两篇关于Swagger入门以及具体使用细节的介绍之后,我们已经能够轻松地为Spring MVC的Web项目自动构建出API文档了.如果您还不熟悉这块,可以先阅读: Spring Boo ...

  7. Spring Boot 2.x基础教程:使用国产数据库连接池Druid

    上一节,我们介绍了Spring Boot在JDBC模块中自动化配置使用的默认数据源HikariCP.接下来这一节,我们将介绍另外一个被广泛应用的开源数据源:Druid. Druid是由阿里巴巴数据库事 ...

  8. Spring Boot 2.x基础教程:找回启动日志中的请求路径列表

    如果您看过之前的Spring Boot 1.x教程,或者自己原本就对Spring Boot有一些经验,或者对Spring MVC很熟悉.那么对于Spring构建的Web应用在启动的时候,都会输出当前应 ...

  9. Spring Boot 2.x基础教程:使用MyBatis的XML配置方式

    上一篇我们介绍了如何在Spring Boot中整合我们国人最常用的MyBatis来实现对关系型数据库的访问.但是上一篇中使用了注解方式来实现,而对于很多MyBatis老用户还是习惯于XML的开发方式, ...

随机推荐

  1. Windows程序设计(1)

    1. Windows程序设计基础 1.1 代码风格 #include "stdafx.h" #include <windows.h> void Alert(int i) ...

  2. Linux 之Mycat搭建报错 java.net.MalformedURLException: Local host name unknown: java.net.UnknownHostException

    搭建MyCat环境时出现 错误: 代理抛出异常错误: java.net.MalformedURLException: Local host name unknown: java.net.Unknown ...

  3. 一分钟开始持续集成之旅系列之:Java + GWT

    作者:CODING - 朱增辉 前言 Google Web Toolkit(GWT)是一个开源.免费的 Web 开发框架,通过该框架,您可以使用 Java 构建复杂.高性能的 JavaScript 应 ...

  4. 人脸识别Demo解析C#

    概述 不管你注意到没有,人脸识别已经走进了生活的角角落落,钉钉已经支持人脸打卡,火车站实名认证已经增加了人脸自助验证通道,更别提各个城市建设的『智能城市』和智慧大脑了.在人脸识别业界,通常由人脸识别提 ...

  5. 深入理解Java闭包概念

    闭包又称词法闭包 闭包最早定义为一种包含<环境成分>和<控制成分>的实体. 解释一:闭包是引用了自由变量的函数,这个被引用的变量将和这个函数一同存在. 解释二:闭包是函数和相关 ...

  6. swagger ui demo

    前言 前几天一个朋友公司在用Springboot集合swagger时候总是从浏览器看不了接口,我两找了问题,但是他还是没有找到,于是我就自己从http://start.spring.io/上下载了一个 ...

  7. 使用 Nginx 部署静态页面

    Nginx 介绍 Nginx 是俄罗斯人编写的十分轻量级的 HTTP 服务器, Nginx,它的发音为「engine X」,是一个高性能的 HTTP 和反向代理服务器,同时也是一个 IMAP/ POP ...

  8. spring boot 整合Thymeleaf模板

    SpringBoot 是为了简化 Spring 应用的创建.运行.调试.部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖 ...

  9. 虚拟机 - NAT模式下设置静态 IP 地址

    背景 如果不给虚拟机设置静态 IP 地址的话,每次重启机器都会自动分配一个新的 IP 如果有多台虚拟机的话,也会动态获取 IP 动态IP的话,每次 设置静态 IP 的步骤 查看本机 IP 和网关 cm ...

  10. Mysql中Union和OR性能对比

    博客已搬家,更多内容查看https://liangyongrui.github.io/ Mysql中Union和OR性能对比 在leetcode上看到一篇文章,整理一下 参考:https://leet ...