本地缓存
当系统中需要有一些基础数据时,往往会使用本地缓存将这些数据缓存在内存中,以减少对数据库的查询,提升系统性能,但是本地缓存如何处理数据更新的情况呢?当应用集群中某台机器更新了数据,其它机器如何快速响应?
一般更新本地缓存有如下 方式:
- 定时更新,这种方式延迟比较大
- 通过消息广播机制更新
- 使用协调者,比如zookeeper,适用于更新频率低的场景,高频率更新的不合适
本文介绍第三种方式,如何利用zookeeper做缓存的即时更新
基本思路
假设应用有三台机器,分别标记为ABC,整个过程遵循如下逻辑:
- 应用在创建本地缓存时,向zookeeper注册一个节点
- 注册监听器,监听数据变化
- 当其中一台机器更新缓存后(假设是机器A),修改zookeeper上的节点的数据,标识有缓存更新
- 集群中所有机器都会收到节点数据更新的回调,随后刷新缓存
整体逻辑如下图:
![d2d18891f2fd4a9d5071ec283a252c04.png](https://i-blog.csdnimg.cn/blog_migrate/16caba68f8c398942951b315e8c697c4.jpeg)
实现
将实现分为两部分,第一部分基于zk实现一个可复用的通知服务,用于通知集群中的其它机器,第二部分基于通知服务实现对缓存的封装
通知服务
通知服务抽象出一个资源的概念,用String表示资源Id,一个资源Id对应于zookeeper上的一个节点,针对资源id触发通知服务实际上就是修改资源id对应的节点上的数据,接口如下:
![db689873f5f806cd0c8764e3e735287e.png](https://i-blog.csdnimg.cn/blog_migrate/0ffbdbd14abaac5af891af64fa0fb5cd.png)
接口定义中包含三个方法:
- Register:注册资源
- Unregister:删除资源
- Notify:通知,目前只接受String类型的值
![cbc4ddbfce1cea6fe535966588e9fcdd.png](https://i-blog.csdnimg.cn/blog_migrate/b08c306933892ac82fe63d9fff56f6e1.jpeg)
缓存封装
因为本地缓存有多种实现方式:
- Guava
- ConcurrentHashMap
- 其它…
为了支持各种类型的local cache实现,定义CacheProvider接口,用于创建缓存:
![9575b475a15042e0add33570bcd5212c.png](https://i-blog.csdnimg.cn/blog_migrate/254eac26f16c939858d65bac946276db.png)
为zookeeper提供抽象类的支持:
![872e02a3dfc2ba237f6e58c7ec31cbd9.png](https://i-blog.csdnimg.cn/blog_migrate/c7bde67d85a1fce782a0dbd9042a6d95.jpeg)
不同的local cache实现从此abstract类继承,例如guava的实现:
![c28edbafa6579d1fae78dea2efb37332.png](https://i-blog.csdnimg.cn/blog_migrate/9f5551dd9d87c19a02d5bbed9af211cb.jpeg)
基于ConcurrentHashMap的local cache实现:
![66d9b36cc13907e3e9b7c52d17d34085.png](https://i-blog.csdnimg.cn/blog_migrate/42edf2b9c9a16982746ad0e8dc4eb649.png)
最后通过一个工具类提供的工厂方法来使用:
![feafc85cef377fc9142d5e6a852eee30.png](https://i-blog.csdnimg.cn/blog_migrate/4ff1a53780b800a0a29ef8961e361d4a.jpeg)
使用方式
最后看一下使用方式:
![8e20641582b915d43251aaea0113106c.png](https://i-blog.csdnimg.cn/blog_migrate/dc0d94b168b77a508fc8cc04d5e51e27.jpeg)