分布式本地缓存的一种实现

  • 源代码

  • 背景

过去一年公司核心系统重构中,有些应用场景是需要使用到本地缓存的,当本地缓存的数据发生更新(删除/增加/修改/全量刷新),如何通知到同一个集群中其他进程更新本地副本,确保同一个集群中,每一个进程实例的地址空间的数据是一致,保证实效性。现在市面上可选用的开源的本地缓存也非常之多,比如Guava的LoadingCache等。这些数据的有效性一般是通过过期-回源的方式来实现的,并不是跨进程的数据增量复制来保证各个业务进程的本地缓存数据一致。在这里,我们实现了后一种方式的数据同步,也是一种去中心化的架构。

  • 设计思想

分为两大核心层:

  1. 第一层为数据缓存层,原则上可以选用任意第三方本地缓存组件,这一块不是我们的重点,在我们的实现中,选用了Guava的LoadingCache。
  2. 第二层为数据复制层,这一层是我们的重点,我们要解决跨进程的数据复制,确保同一集群中各个进程实例地址空间数据副本的一致性,网络节点监控以及服务发现等能力。
采用JAVA实现本地缓存的SDK,因为在实际应用中,我们的所有业务系统都是Java编写的。确保JAVA SDK尽可能少的和应用争抢资源,尽可能少的占用系统资源。JAVA SDK在缓存层对第三方缓存组件做了简单的封装,将二进制日志通过SDK写入本地共享内存,共享内存被建模为一个基于PROTOBUF 3协议的本地小型数据库。JAVA SDK只会负责向数据库中写入BINGLOG。
本地Agent进程,负责消费共享内存数据库中的数据,并对日志进行跨进程复制和重放。Agent同其他节点的业务进程(JAVA SDK)之间建立TCP长连接,用于数据复制,当业务进程(JAVA SDK)接收到请求之后,直接更新本地缓存。共享内存中的数据是持久化到磁盘的,应用可以从任何一个db文件以及该db文件的offset开始重放,能够确保数据不丢。
Agent进程之间,使用组播实现自适应网络,任何Agent或者第三方节点都可以join&leave这个网络而不需要额外的配置,任何第三方系统也可以加入并监听组播网络中的消息实现网络管理功能。当有新的Agent节点加入后,集群中的Agent节点将自动感知到,并且能自动获取即将加入节点的地址,其他节点的Agent可以自动增加到即将加入节点的业务进程TCP长连接(Agent到JAVA SDK的TCP长连接)。当有节点退出,组播网络也能广而告之,该节点将自动从集群中移除,从而实现自管理。
在数据复制过程中,如果一条数据通道(TCP长连接)发生异常,本地的Agent将通知到本地的JAVA SDK,通过回调告知应用(Agent通知JAVA SDK,带上无法复制的key&value),某台服务器的本地缓存可能没法同步到,数据不一致即将发生,应用可以采取一些措施。同时,Agent也会通过组播网络告知集群中的其他节点或者第三方监控节点,数据一致性可能存在问题并发出alarming。
在这套体系中的所有IPC统一采用PROTOBUF 3二进制编码(包括:共享内存,复制协议,服务发现协议等)。同时,Agent到各个业务进程(JAVA SDK)的TCP长连接有重连机制。当TCP因为网络问题闪断,能保证2秒内重连恢复。
  • 共享内存数据结构
包括:索引和数据两部分。
  • 下一步演进

数据复制只会在Agent之间进行,由本地的Agent通过UDP实现回调通知到本地的JAVA SDK。

### Java实现分布式本地缓存的方式或工具 在 Java 开发领域,分布式本地缓存一种常见的优化手段,用于提升系统的性能和可扩展性。以下是几种主流的实现方式及其特点: #### 1. **Ehcache** Ehcache 是一种广泛使用的缓存框架,最初设计为本地缓存,但在分布式场景下也提供了强大的支持。通过特定的配置,它可以将本地缓存升级为分布式集群缓存,从而解决多个节点间数据一致性的问题[^3]。 - Ehcache 支持多种分布模式,例如基于 RMI 的复制机制以及 Terracotta 集群方案。 - 数据存储于进程内存中,减少了网络交互带来的延迟。 代码示例: ```java import org.ehcache.Cache; import org.ehcache.CacheManager; import org.ehcache.config.builders.CacheConfigurationBuilder; import org.ehcache.config.builders.CacheManagerBuilder; public class EhcacheExample { public static void main(String[] args) { CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().build(); cacheManager.init(); Cache<String, String> cache = cacheManager.createCache("myCache", CacheConfigurationBuilder.newCacheConfigurationBuilder( String.class, String.class, ResourcePoolsBuilder.heap(10))); cache.put("key", "value"); System.out.println(cache.get("key")); } } ``` #### 2. **Caffeine** 虽然 Caffeine 主要被定义为单机版的高效缓存库,但它可以通过与其他中间件(如 Redis 或 Hazelcast)集成来实现分布式能力。其高性能特性和易用性使其成为许多开发者的选择。 - 提供近似 LRU 和 LFU 的淘汰算法,适合高频访问的数据缓存需求。 - 可以作为本地缓存层,在分布式环境中配合其他组件完成复杂任务。 代码示例: ```java import com.github.benmanes.caffeine.cache.Caffeine; import java.util.concurrent.TimeUnit; public class CaffeineExample { public static void main(String[] args) { var caffeineCache = Caffeine.newBuilder() .maximumSize(10_000) .expireAfterWrite(5, TimeUnit.MINUTES) .build((k) -> computeValue(k)); System.out.println(caffeineCache.get("exampleKey")); } private static Object computeValue(Object key) { return "Computed-" + key.toString(); } } ``` #### 3. **Hazelcast** Hazelcast 是一款开源的 In-Memory Data Grid (IMDG),它不仅能够提供高效的分布式缓存服务,还具备内置的消息队列、锁等功能。它的特点是完全去中心化的设计,使得整个系统更加健壮可靠[^2]。 - 支持自动分区与备份功能,即使部分节点失效也不会丢失重要信息。 - API 设计友好,易于上手并快速融入现有应用程序之中。 代码示例: ```java import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.map.IMap; public class HazelcastExample { public static void main(String[] args) { HazelcastInstance hazelcastInstance = Hazelcast.newHazelcastInstance(); IMap<Integer, String> map = hazelcastInstance.getMap("distributed-map"); map.put(1, "One"); System.out.println(map.get(1)); } } ``` #### 4. **Redisson** Redisson 是一个针对 Redis 的 Java 客户端封装库,除了常规的操作外,还可以轻松构建起一套完整的分布式对象结构体系,其中包括但不限于 Map、Set、List 等容器形式的支持。借助 Redis 的持久化能力和高可用特性,Redisson 成为了连接传统关系型数据库同现代 NoSQL 技术之间的桥梁之一[^1]。 - 利用 Redis Cluster 架构下的分片技术实现了水平扩容的能力。 - 对 Spring Framework 用户特别友好,几乎零侵入即可享受全套解决方案。 代码示例: ```java import org.redisson.Redisson; import org.redisson.api.RBucket; import org.redisson.api.RedissonClient; import org.redisson.config.Config; public class RedissonExample { public static void main(String[] args) throws InterruptedException { Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379"); RedissonClient redisson = Redisson.create(config); RBucket<String> bucket = redisson.getBucket("test-key"); bucket.set("Hello World!"); Thread.sleep(1000); // Simulate some work delay. System.out.println(bucket.get()); redisson.shutdown(); } } ``` --- ### 总结 每种工具都有自己的优势所在,具体选用哪款取决于实际应用场景的需求分析结果。如果追求简单部署且注重成本控制,则可以选择像 Ehcache 这样的产品;而对于那些希望获得更高灵活性及额外附加价值的企业来说,可能更倾向于采用 Hazelcast 或者 Redisson 方案。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值