乐趣区

Spring-Boot-2x基础教程使用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 的配置文件。咱们尝试手工组建集群的形式,不同实例在网络相干配置上会产生不同的配置信息,所以咱们建设不同的配置文件给不同的实例应用。比方上面这样:

实例 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 基础教程》

参考资料

  • EhCache 分布式缓存 / 缓存集群
  • Java RMI:rmi Connection refused to host: 127.0.0.1 异样解决

本文首发:Spring Boot 2.x 基础教程:应用 EhCache 缓存集群,转载请注明出处。
欢送关注我的公众号:程序猿 DD,取得独家整顿的学习资源和日常干货推送。点击中转本系列教程目录。

退出移动版