上一篇咱们介绍了在Spring Boot中整合EhCache的方法。既然用了ehcache,咱们天然要说说它的一些高级功能,否则咱们用默认的ConcurrentHashMap
就行了。本篇不具体介绍EhCache缓存如何落文件、如何配置各类过时参数等常规细节配置,这部份内容留给读者本身学习,若是您不知道如何搞,能够看看这里的官方文档。html
那么咱们今天具体讲什么呢?先思考一个场景,当咱们使用了EhCache,在缓存过时以前能够有效的减小对数据库的访问,可是一般咱们将应用部署在生产环境的时候,为了实现应用的高可用(有一台机器挂了,应用还须要可用),确定是会部署多个不一样的进程去运行的,那么这种状况下,当有数据更新的时候,每一个进程中的缓存都是独立维护的,若是这些进程缓存同步机制,那么就存在因缓存没有更新,而一直都用已经失效的缓存返回给用户,这样的逻辑显然是会有问题的。因此,本文就来讲说当使用EhCache的时候,若是来组建进程内缓存EnCache的集群以及配置配置他们的同步策略。java
因为下面是组建集群的过程,务必采用多机的方式调试,避免没必要要的错误发生。spring
本篇的实现将基于上一篇的基础工程来进行。先来回顾下上一篇中的程序要素:数据库
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实体的数据访问实现(涵盖了缓存注解)bash
@CacheConfig(cacheNames = "users") public interface UserRepository extends JpaRepository<User, Long> { @Cacheable User findByName(String name); }
下面开始改造这个项目:网络
第一步:为须要同步的缓存对象实现Serializable
接口app
@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
socket
<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
,这个标签主要用来定义缓存事件监听的处理策略,它有如下这些参数用来设置缓存的同步策略:
新增了一个cacheManagerPeerProviderFactory
标签的配置,用来指定组建的集群信息和要同步的缓存信息,其中:
|
链接第三步:打包部署与启动。打包没啥大问题,主要缓存配置内容存在必定差别,因此在指定节点的模式下,须要单独拿出来,而后使用启动参数来控制读取不一样的配置文件。好比这样:
-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; } }
验证逻辑:
/create
接口,建立一条数据/find
接口,实例1缓存User,同时同步缓存信息给实例2,在实例1中会存在SQL查询语句/find
接口,因为缓存集群同步了User的信息,因此在实例2中的此次查询也不会出现SQL语句上一篇发布的时候,公众号上有网友留言问,数据更新以后怎么办?
其实当构建了缓存集群以后,就比较好办了。好比这里的例子,须要作两件事:
save
操做增长@CachePut
注解,让更新操做完成以后将结果再put到缓存中这样就能够防止缓存的脏数据了,可是这种方法还并非很好,由于缓存集群的同步依然须要时间,会存在短暂的不一致。同时进程内的缓存要在每一个实例上都占用,若是大量存储的话始终不那么经济。因此,不少时候进程内缓存不会做为主要的缓存手段。下一篇将具体说说,另外一个更重要的缓存使用!
欢迎关注本系列教程:《Spring Boot 2.x基础教程》
本文首发: Spring Boot 2.x基础教程:使用EhCache缓存集群,转载请注明出处。
欢迎关注个人公众号:程序猿DD,得到独家整理的学习资源和平常干货推送。 点击直达本系列教程目录。