- 2023-07-24 13:13:26
- 3685 热度
- 0 评论
上一篇我们介绍了在Spring Boot中整合EhCache的方法。既然用了ehcache,我们自然要说说它的一些高级功能,不然我们用默认的ConcurrentHashMap
就好了。本篇不具体介绍EhCache缓存如何落文件、如何配置各种过期参数等常规细节配置,这部分内容留给读者自己学习,如果您不知道如何搞,可以看看这里的官方文档。
那么我们今天具体讲什么呢?先思考一个场景,当我们使用了EhCache,在缓存过期之前可以有效的减少对数据库的访问,但是通常我们将应用部署在生产环境的时候,为了实现应用的高可用(有一台机器挂了,应用还需要可用),肯定是会部署多个不同的进程去运行的,那么这种情况下,当有数据更新的时候,每个进程中的缓存都是独立维护的,如果这些进程缓存同步机制,那么就存在因缓存没有更新,而一直都用已经失效的缓存返回给用户,这样的逻辑显然是会有问题的。所以,本文就来说说当使用EhCache的时候,如果来组建进程内缓存EnCache的集群以及配置配置他们的同步策略。
由于下面是组建集群的过程,务必采用多机的方式调试,避免不必要的错误发生。
动手试试
本篇的实现将基于上一篇的基础工程来进行。先来回顾下上一篇中的程序要素:
User实体的定义
@Entity |
User实体的数据访问实现(涵盖了缓存注解)
@CacheConfig(cacheNames = "users") |
下面开始改造这个项目:
第一步:为需要同步的缓存对象实现Serializable
接口
@Entity |
注意:如果没有做这一步,后续缓存集群通过过程中,因为要传输User对象,会导致序列化与反序列化相关的异常
第二步:重新组织ehcache的配置文件。我们尝试手工组建集群的方式,不同实例在网络相关配置上会产生不同的配置信息,所以我们建立不同的配置文件给不同的实例使用。比如下面这样:
实例1,使用ehcache-1.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
实例2,使用ehcache-2.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
配置说明:
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 |
第四步:实现几个接口用来验证缓存的同步效果
@RestController |
验证逻辑:
- 启动通过第三步说的命令参数,启动两个实例
- 调用实例1的
/create
接口,创建一条数据 - 调用实例1的
/find
接口,实例1缓存User,同时同步缓存信息给实例2,在实例1中会存在SQL查询语句 - 调用实例2的
/find
接口,由于缓存集群同步了User的信息,所以在实例2中的这次查询也不会出现SQL语句
进一步思考
上一篇发布的时候,公众号上有网友留言问,数据更新之后怎么办?
其实当构建了缓存集群之后,就比较好办了。比如这里的例子,需要做两件事:
save
操作增加@CachePut
注解,让更新操作完成之后将结果再put到缓存中- 保证缓存事件监听的replicateUpdates=true,这样数据在更新之后可以保证复制到其他节点
这样就可以防止缓存的脏数据了,但是这种方法还并不是很好,因为缓存集群的同步依然需要时间,会存在短暂的不一致。同时进程内的缓存要在每个实例上都占用,如果大量存储的话始终不那么经济。所以,很多时候进程内缓存不会作为主要的缓存手段。下一篇将具体说说,另一个更重要的缓存使用!
欢迎关注本系列教程:《Spring Boot 2.x基础教程》
参考资料
代码示例
本文的相关例子可以查看下面仓库中的chapter5-3
目录:
- Github:https://github.com/dyc87112/SpringBoot-Learning/
- Gitee:https://gitee.com/didispace/SpringBoot-Learning/
如果您觉得本文不错,欢迎Star
支持,您的关注是我坚持的动力!
- Spring(403)
- Boot(208)
- Spring Boot(187)
- Java(82)
- Cloud(82)
- Spring Cloud(82)
- Security(60)
- Spring Security(54)
- Boot2(51)
- Spring Boot2(51)
- Redis(31)
- SQL(29)
- Mysql(25)
- IDE(24)
- Dalston(24)
- JDBC(22)
- IDEA(22)
- mongoDB(22)
- MVC(22)
- Web(21)
- CLI(20)
- Alibaba(19)
- SpringMVC(19)
- SpringBoot(17)
- Docker(17)
- Eclipse(16)
- Vue(16)
- Git(16)
- JPA(15)
- Apache(15)
- ORA(15)
- Oracle(14)
- jdk(14)
- Tomcat(14)
- Linux(14)
- HTTP(14)
- Mybatis(14)
- XML(13)
- JdbcTemplate(13)
- OAuth(13)
- Nacos(13)
- Pro(13)
- JSON(12)
- OAuth2(12)
- Data(12)
- stream(11)
- int(11)
- Myeclipse(11)
- not(10)
- Bug(10)
- maven(9)
- Map(9)
- Hystrix(9)
- ast(9)
- APP(8)
- Bit(8)
- API(8)
- session(8)
- Window(8)
- Swagger(8)
- JavaMail(7)
- Cache(7)
- File(7)
- IntelliJ(7)
- mail(7)
- windows(7)
- too(7)
- HTML(7)
- Github(7)
- Excel(6)
- Log4J(6)
- pushlet(6)
- apt(6)
- read(6)
- Freemarker(6)
- WebFlux(6)
- JSP(6)
- Bean(6)
- error(6)
- Server(6)
- nginx(6)
- ueditor(6)
- jar(6)
- ehcache(6)
- UDP(6)
- RabbitMQ(6)
- star(6)
- and(6)
- Struts(5)
- string(5)
- script(5)
- Syntaxhighlighter(5)
- Tool(5)
- Controller(5)
- swagger2(5)
- ldquo(5)
- input(5)
- Servlet(5)
- Config(5)
- discuz(5)