Spring Boot 2.x基础教程:使用EhCache缓存集群

上一篇我们先容了在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的设置文件。我们实验手工组建集群的方式,差别实例在网络相关设置上会发生差别的设置信息,以是我们确立差别的设置文件给差别的实例使用。好比下面这样:

JVM调优工具Arthas的使用

实例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,获得独家整理的学习资源和一样平常干货推送。点击直达本系列教程目录

原创文章,作者:28qn新闻网,如若转载,请注明出处:https://www.28qn.com/archives/23574.html