第三集:分布式Ehcache缓存改造 前言
好久没有写博客了,大有半途而废的趋势。忙不是借口,这个好习惯还是要继续坚持。前面我承诺的第一期的DIY分布式,是时候上终篇了---DIY分布式缓存。
探索之路 在前面的文章中,我给大家大致说过项目背景:项目中的缓存使用的是Ehcache。因为前面使用Ehcache的应用就一台,所以这种单机的Ehcache并不会有什么问题。现在分布式部署之后,如果各个应用之间的缓存不能共享,那么其实各自就是一个孤岛。可能在一个业务跑下来,请求了不同的应用,结果在缓存中取出来的值不一样,
造成数据不一致。所以需要重新设计缓存的实现。
因为尽量不要引入新的中间件,所以改造仍然是围绕Ehcache来进行的。搜集了各种资料之后,发现Ehcache实现分布式缓存基本有以下两种思路:
客户端实现分布式算法: 在使用Ehcache的客户端自己实现分布式算法。
算法的基本思路就是取模:即假设有三台应用(编号假设分别为0,1,2),对于一个要缓存的对象,首先计算其key的hash值,然后将hash值模3,得到的余数是几,就将数据缓存到哪台机器。
同步冗余数据: Ehcache是支持集群配置的,集群的各个节点之间支持按照一定的协议进行数据同步。这样每台应用其实缓存了一整份数据,不同节点之间的数据是一致的。
虽然冗余的办法显得有点浪费资源,但是我最终还是选择了冗余。具体原因有以下几点:
分布式算法的复杂性: 前面所讲的分布式算法只是最基本的实现。事实上实现要比这个复杂的多。需要考虑增加或者删除节点的情况,需要使用更加复杂的一致性hash算法
可能导致整个应用不可用: 当删除节点之后,如果算法不能够感知进行自动调整,仍然去请求那个已经被删除的节点,可能导致整个系统不可用。
Demo 最终我的实现采用RMI的方式进行同步
配置ehcache spring-ehcache-cache.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" name="businessCaches"> <diskStore path="java.io.tmpdir/ehcache"/> <cache name="business1Cache" maxElementsInMemory="10000000" eternal="true" overflowToDisk="false" memoryStoreEvictionPolicy="LRU"> <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/> </cache> <cache name="business2Cache" maxElementsInMemory="100" eternal="true" overflowToDisk="false" memoryStoreEvictionPolicy="LRU"> <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/> </cache> <!-- cache发布信息配置,人工发现peerDiscovery=manual,cacheNames可配置多个缓存名称,以|分割 ) --> <cacheManagerPeerProviderFactory class="com.rampage.cache.distribute.factory.DisRMICacheManagerPeerProviderFactory" properties="peerDiscovery=manual, cacheNames=business1Cache|business2Cache" /> <!-- 接收同步cache信息的地址 --> <cacheManagerPeerListenerFactory class="com.rampage.cache.distribute.factory.DisRMICacheManagerPeerListenerFactory" properties="socketTimeoutMillis=2000" /> </ehcache>