flea-cache使用之Memcached接入

1. 参考

flea-cache使用之Memcached接入 源代码

2. 依赖

Memcached-Java-Client-3.0.2.jar

1
2
3
4
5
6
<!-- Memcached相关 -->
<dependency>
<groupId>com.whalin</groupId>
<artifactId>Memcached-Java-Client</artifactId>
<version>3.0.2</version>
</dependency>

spring-context-4.3.18.RELEASE.jar

1
2
3
4
5
6
<!-- Spring相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>

spring-context-support-4.3.18.RELEASE.jar

1
2
3
4
5
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>

3. 基础接入

3.1 定义Flea缓存接口

IFleaCache 自定义缓存接口,包含了一些增删改查的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface IFleaCache {

Object get(String key);

void put(String key, Object value);

void clear();

void delete(String key);

Set<String> getCacheKey();

String getSystemName();
}
  • get : 读缓存
  • put : 写缓存
  • clear : 清空所有缓存
  • delete : 删除指定数据键关键字对应的缓存
  • getCacheKey : 获取 记录当前Cache所有数据键关键字 的Set集合
  • getSystemName : 获取缓存所属系统名

3.2 定义抽象Flea缓存类

AbstractFleaCache 抽象缓存类,实现了Flea缓存接口的读、写、删除和清空缓存的基本操作。

它定义本地读、本地写和本地删除的抽象方法,由子类实现具体的读、 写和删除缓存的操作。

在实际调用写缓存操作时,会同时记录当前缓存数据的数据键关键字 【key】到专门的数据键关键字的缓存中,以Set集合存储。

比如缓存数据主关键字为【name】,需要存储的数据键关键字为 【key】,则在实际调用写缓存操作时,会操作两条缓存数据:

  • 一条是具体的数据缓存,缓存键为 系统名_name_key ,可查看方法 【getNativeKey】,有效期从配置中获取;
  • 一条是数据键关键字的缓存,缓存键为 系统名_name ,可查看方法 【getNativeCacheKey】,默认永久有效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
public abstract class AbstractFleaCache implements IFleaCache {

private static final FleaLogger LOGGER = FleaLoggerProxy.getProxyInstance(AbstractFleaCache.class);

private final String name; // 缓存数据主关键字

private final int expiry; // 缓存数据有效期(单位:s)

private final int nullCacheExpiry; // 空缓存数据有效期(单位:s)

protected CacheEnum cache; // 缓存实现

public AbstractFleaCache(String name, int expiry, int nullCacheExpiry) {
this.name = name;
this.expiry = expiry;
this.nullCacheExpiry = nullCacheExpiry;
}

@Override
public Object get(String key) {
Object value = null;
try {
value = getNativeValue(getNativeKey(key));
if (value instanceof NullCache) {
value = null;
}
} catch (Exception e) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error1(new Object() {}, "The action of getting [" + cache.getName() + "] cache occurs exception : ", e);
}
}
return value;
}

@Override
public void put(String key, Object value) {
try {
putNativeValue(getNativeKey(key), value, expiry);
// 将指定Cache的key添加到Set集合,并存于缓存中
addCacheKey(key);
} catch (Exception e) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error1(new Object() {}, "The action of adding [" + cache.getName() + "] cache occurs exception : ", e);
}
}
}

@Override
public void clear() {
Set<String> keySet = getCacheKey();
if (CollectionUtils.isNotEmpty(keySet)) {
for (String key : keySet) {
deleteNativeValue(getNativeKey(key));
}
// 删除 记录当前Cache所有数据键关键字 的缓存
deleteCacheAllKey();
}
}

@Override
public void delete(String key) {
try {
deleteNativeValue(getNativeKey(key));
// 从 记录当前Cache所有数据键关键字 的缓存中 删除指定数据键关键字key
deleteCacheKey(key);
} catch (Exception e) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error1(new Object() {}, "The action of deleting [" + cache.getName() + "] cache occurs exception : ", e);
}
}
}

/**
* <p> 将指定数据键关键字{@code key}记录到当前Cache所有数据键关键字的缓存中 </p>
*
* @param key 指定Cache的数据键关键字
* @since 1.0.0
*/
private void addCacheKey(String key) {
Set<String> keySet = getCacheKey();
if (CollectionUtils.isEmpty(keySet)) {
keySet = new HashSet<>();
}
if (!keySet.contains(key)) { // 只有其中不存在,才重新设置
keySet.add(key);
putNativeValue(getNativeCacheKey(name), keySet, CommonConstants.NumeralConstants.INT_ZERO);
}
}

/**
* <p> 从 记录当前Cache所有数据键关键字 的缓存中 删除指定数据键关键字{@code key} </p>
*
* @param key 指定Cache的数据键关键字
* @since 1.0.0
*/
private void deleteCacheKey(String key) {
Set<String> keySet = getCacheKey();
if (CollectionUtils.isNotEmpty(keySet)) {
// 存在待删除的数据键关键字
if (keySet.contains(key)) {
if (CommonConstants.NumeralConstants.INT_ONE == keySet.size()) {
deleteCacheAllKey(); // 直接将记录当前Cache所有数据键关键字的缓存从缓存中清空
} else {
// 将数据键关键字从Set集合中删除
keySet.remove(key);
// 重新覆盖当前Cache所有数据键关键字的缓存信息
putNativeValue(getNativeCacheKey(name), keySet, CommonConstants.NumeralConstants.INT_ZERO);
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug1(new Object() {}, "The CacheKey of [{}] is not exist", key);
}
}
}
}

/**
* <p> 删除 记录当前Cache所有数据键关键字 的缓存 </p>
*
* @since 1.0.0
*/
private void deleteCacheAllKey() {
try {
deleteNativeValue(getNativeCacheKey(name));
} catch (Exception e) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error1(new Object() {}, "The action of deleting [" + cache.getName() + "] cache occurs exception : ", e);
}
}
}

@Override
@SuppressWarnings(value = "unchecked")
public Set<String> getCacheKey() {
Set<String> keySet = null;
try {
Object keySetObj = getNativeValue(getNativeCacheKey(name));
if (ObjectUtils.isNotEmpty(keySetObj) && keySetObj instanceof Set) {
keySet = (Set<String>) keySetObj;
}
} catch (Exception e) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error1(new Object() {}, "The action of getting [" + cache.getName() + "] cache occurs exception : ", e);
}
}
return keySet;
}

/**
* <p> 获取缓存值 </p>
*
* @param key 缓存数据键关键字
* @return 缓存值
* @since 1.0.0
*/
public abstract Object getNativeValue(String key);

/**
* <p> 添加缓存数据 </p>
*
* @param key 缓存数据键关键字
* @param value 缓存值
* @param expiry 有效期(单位:s)
* @since 1.0.0
*/
public abstract Object putNativeValue(String key, Object value, int expiry);

/**
* <p> 删除指定缓存数据 </p>
*
* @param key 缓存数据键关键字
* @since 1.0.0
*/
public abstract Object deleteNativeValue(String key);

/**
* <p> 获取实际存储的缓存键【缓存所属系统名 + 缓存名(缓存数据主关键字)+ 缓存数据键(缓存数据关键字)】 </p>
*
* @param key 缓存数据键关键字
* @return 实际存储的缓存键
* @since 1.0.0
*/
private String getNativeKey(String key) {
return StringUtils.strCat(getNativeCacheKey(name), CommonConstants.SymbolConstants.UNDERLINE, key);
}

/**
* <p> 获取缓存主键【包含缓存所属系统名 + 缓存名(缓存数据主关键字)】 </p>
*
* @param name 缓存名【缓存数据主关键字】
* @return 缓存主键【缓存所属系统名 + 缓存名(缓存数据主关键字)】
* @since 1.0.0
*/
protected String getNativeCacheKey(String name) {
return StringUtils.strCat(getSystemName(), CommonConstants.SymbolConstants.UNDERLINE, name);
}

// 省略一些get方法
}

该类实现了IFleaCache接口,同时定义了三个抽象方法 :

1
2
3
4
5
public abstract Object getNativeValue(String key);

public abstract void putNativeValue(String key, Object value, int expiry);

public abstract void deleteNativeValue(String key);

这三个抽象方法由子类实现具体的读,写,删除缓存的原始操作

3.3 定义MemCached Flea缓存类

MemCachedFleaCache 该类继承 AbstractFleaCache,实现Memcached 缓存的接入使用;

MemCached Flea缓存类,实现了以Flea框架操作MemCached缓存的基本操作方法。

在上述基本操作方法中,实际使用MemCached客户端【memCachedClient】 读、写和删除MemCached缓存。其中写缓存方法【putNativeValue】在 添加的数据值为【null】时,默认添加空缓存数据【NullCache】 到MemCached中,有效期取初始化参数【nullCacheExpiry】。

单个缓存接入场景,有效期配置可查看【memcached.properties】中的配置 参数【memcached.nullCacheExpiry

整合缓存接入场景,有效期配置可查看【flea-cache-config.xml】 中的缓存参数【<cache-param key="fleacore.nullCacheExpiry" desc="空缓存数据有效期(单位:s)">300</cache-param>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class MemCachedFleaCache extends AbstractFleaCache {

private final MemCachedClient memCachedClient; // MemCached客户端

/**
* <p> 初始化MemCached Flea缓存类 </p>
*
* @param name 缓存数据主关键字
* @param expiry 缓存数据有效期(单位:s)
* @param nullCacheExpiry 空缓存数据有效期(单位:s)
* @param memCachedClient MemCached客户端
* @since 1.0.0
*/
public MemCachedFleaCache(String name, int expiry, int nullCacheExpiry, MemCachedClient memCachedClient) {
super(name, expiry, nullCacheExpiry);
this.memCachedClient = memCachedClient;
cache = CacheEnum.MemCached;
}

@Override
public Object getNativeValue(String key) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug1(new Object() {}, "KEY = {}", key);
}
return memCachedClient.get(key);
}

@Override
public Object putNativeValue(String key, Object value, int expiry) {
if (ObjectUtils.isEmpty(value))
return memCachedClient.set(key, new NullCache(key), new Date(getNullCacheExpiry() * 1000));
else
return memCachedClient.set(key, value, new Date(expiry * 1000));
}

@Override
public Object deleteNativeValue(String key) {
return memCachedClient.delete(key);
}

@Override
public String getSystemName() {
return MemCachedConfig.getConfig().getSystemName();
}
}

到这一步为止,底层的Flea缓存接口和实现已经完成,但目前还不能使用;

3.4 定义Memcached连接池

MemCachedPool 用于初始化 MemCached 的套接字连接池。

  • 针对单独缓存接入场景,采用默认连接池初始化的方式; 可参考如下:
    1
    2
    3
    4
    5
    6
    7
    8
        // 初始化默认连接池
    MemCachedPool.getInstance().initialize();
    ```

    - 针对整合缓存接入场景,采用指定连接池初始化的方式; 可参考如下:
    ```java
    // 初始化指定连接池
    MemCachedPool.getInstance(group).initialize(cacheServerList);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class MemCachedPool {

private String poolName; // 连接池名

private MemCachedConfig memCachedConfig; // MemCached 配置信息

private SockIOPool sockIOPool; // MemCached SockIOPool

private MemCachedPool() {
}

/**
* <p> 获取MemCached连接池实例 (默认) </p>
*
* @return MemCached连接池实例对象
* @since 1.0.0
*/
public static MemCachedPool getInstance() {
MemCachedPool memCachedPool = new MemCachedPool();
memCachedPool.memCachedConfig = MemCachedConfig.getConfig();
memCachedPool.sockIOPool = SockIOPool.getInstance();
return memCachedPool;
}

/**
* <p> 获取MemCached连接池实例(指定连接池名poolName) </p>
*
* @param poolName 连接池名
* @return MemCached连接池实例对象
* @since 1.0.0
*/
public static MemCachedPool getInstance(String poolName) {
MemCachedPool memCachedPool = new MemCachedPool();
memCachedPool.poolName = poolName;
memCachedPool.sockIOPool = SockIOPool.getInstance(poolName);
return memCachedPool;
}

/**
* <p> 初始化MemCached连接池 </p>
*
* @since 1.0.0
*/
public void initialize() {
// 省略。。。。。。
}

/**
* <p> 初始化MemCached连接池 </p>
*
* @param cacheServerList 缓存服务器集
* @since 1.0.0
*/
public void initialize(List<CacheServer> cacheServerList) {
// 省略。。。。。。
}

// 详见 GitHub 链接
}

3.5 Memcached配置文件

flea-cache 读取 memcached.propertiesMemcached配置文件),用作初始化 MemCachedPool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# Memcached配置
# Memcached缓存所属系统名
memcached.systemName=FleaFrame

# Memcached服务器地址
memcached.server=127.0.0.1:31113,127.0.0.1:31114

# Memcached服务器权重分配
memcached.weight=1,1

# 初始化时对每个服务器建立的连接数目
memcached.initConn=20

# 每个服务器建立最小的连接数
memcached.minConn=20

# 每个服务器建立最大的连接数
memcached.maxConn=500

# 自查线程周期进行工作,其每次休眠时间(单位:ms)
memcached.maintSleep=60000

# Socket的参数,如果是true在写数据时不缓冲,立即发送出去
memcached.nagle=true

# Socket阻塞读取数据的超时时间(单位:ms)
memcached.socketTO=3000

# Socket连接超时时间(单位:ms)
memcached.socketConnectTO=3000

# 空缓存数据有效期(单位:s)
memcached.nullCacheExpiry=10

# Memcached分布式hash算法
# 0 - native String.hashCode();
# 1 - original compatibility
# 2 - new CRC32 based
# 3 - MD5 Based
memcached.hashingAlg=3

3.6 定义抽象Flea缓存管理类

AbstractFleaCacheManager 用于接入 Flea 框架管理缓存。

同步集合类【cacheMap】, 存储的键为缓存数据主关键字, 存储的值为具体的缓存实现类。

  • 如果是整合各类缓存接入,它的键对应缓存定义配置文件【flea-cache.xml】 中的【<cache key="缓存数据主关键字"></cache>】;
  • 如果是单个缓存接入,它的键对应【applicationContext.xml】中 【<entry key="缓存数据主关键字"value="有效期(单位:s)"/>】;

抽象方法【newCache】,由子类实现具体的Flea缓存类创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public abstract class AbstractFleaCacheManager {

private static final ConcurrentMap<String, AbstractFleaCache> cacheMap = new ConcurrentHashMap<>();

private Map<String, Integer> configMap = new HashMap<>(); // 各缓存的时间Map

/**
* <p> 获取所有的Flea缓存 </p>
*
* @return 所有的Flea缓存
* @since 1.0.0
*/
protected Collection<AbstractFleaCache> loadCaches() {
return cacheMap.values();
}

/**
* <p> 根据指定缓存名获取缓存对象 </p>
*
* @param name 缓存名
* @return 缓存对象
* @since 1.0.0
*/
public AbstractFleaCache getCache(String name) {
if (!cacheMap.containsKey(name)) {
synchronized (cacheMap) {
if (!cacheMap.containsKey(name)) {
Integer expiry = configMap.get(name);
if (ObjectUtils.isEmpty(expiry)) {
expiry = CommonConstants.NumeralConstants.INT_ZERO; // 表示永久
configMap.put(name, expiry);
}
cacheMap.put(name, newCache(name, expiry));
}
}
}
return cacheMap.get(name);
}

/**
* <p> 新创建一个缓存对象 </p>
*
* @param name 缓存名
* @param expiry 有效期(单位:s 其中0:表示永久)
* @return 新建的缓存对象
* @since 1.0.0
*/
protected abstract AbstractFleaCache newCache(String name, int expiry);

/**
* <p> 设置各缓存有效期配置Map </p>
*
* @param configMap 有效期配置Map
* @since 1.0.0
*/
public void setConfigMap(Map<String, Integer> configMap) {
this.configMap = configMap;
}

}

3.7 定义Memcached Flea缓存管理类

MemCachedFleaCacheManager 用于接入 Flea 框架管理MemCached 缓存。

它的默认构造方法,用于单个缓存接入场景,首先新建MemCached客户端, 然后初始化 MemCached 连接池。

方法 newCache,用于创建一个MemCached Flea缓存, 它里面包含了 读、写、删除 和 清空 缓存的基本操作。

上述 MemCachedFleaCache 使用, 需要初始化 MemCachedPool

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**
* MemCached Flea缓存管理类,用于接入Flea框架管理MemCached 缓存。
*
* <p> 它的默认构造方法,用于单个缓存接入场景,首先新建MemCached客户端,
* 然后初始化 MemCached 连接池。
*
* <p> 方法 {@code newCache},用于创建一个MemCached Flea缓存,
* 它里面包含了 读、写、删除 和 清空 缓存的基本操作。
*
* @author huazie
* @version 1.0.0
* @see MemCachedFleaCache
* @since 1.0.0
*/
public class MemCachedFleaCacheManager extends AbstractFleaCacheManager {

private MemCachedClient memCachedClient; // MemCached客户端类

/**
* 用于新建MemCached客户端,并初始化MemCached连接池。
*
* @since 1.0.0
*/
public MemCachedFleaCacheManager() {
memCachedClient = new MemCachedClient();
initPool();
}

/**
* 以传入参数初始化MemCached客户端,并初始化MemCached连接池。
*
* @param memCachedClient MemCached客户端
* @since 1.0.0
*/
public MemCachedFleaCacheManager(MemCachedClient memCachedClient) {
this.memCachedClient = memCachedClient;
initPool();
}

/**
* <p> 初始化MemCached连接池 </p>
*
* @since 1.0.0
*/
private void initPool() {
MemCachedPool.getInstance().initialize();
}

@Override
protected AbstractFleaCache newCache(String name, int expiry) {
int nullCacheExpiry = MemCachedConfig.getConfig().getNullCacheExpiry();
return new MemCachedFleaCache(name, expiry, nullCacheExpiry, memCachedClient);
}
}

好了,到了这一步,Memcached已接入完成,开始自测

3.8 Memcached接入自测

FleaCacheTest 单元测试类可点击查看

首先,这里需要按照 Memcached 配置文件中的地址部署相应的 Memcached 服务,可参考笔者的 这篇博文

下面开始演示我们的 Memcached 接入自测:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    @Test
public void testMemeCachedFleaCache() {
try {
AbstractFleaCacheManager manager = FleaCacheManagerFactory.getFleaCacheManager(CacheEnum.MemCached.getName());
AbstractFleaCache cache = manager.getCache("fleaparadetail");
LOGGER.debug("Cache={}", cache);
//## 1. 简单字符串
// cache.put("menu1", "huazie");
// cache.get("menu1");
cache.delete("menu1");
// cache.getCacheKey();
LOGGER.debug(cache.getCacheName() + ">>>" + cache.getCacheDesc());
} catch (Exception e) {
LOGGER.error("Exception:", e);
}
}

4. 进阶接入

4.1 定义抽象Spring缓存

AbstractSpringCache 实现SpringCache接口 和 FleaIFleaCache接口,由具体的Spring缓存管理类初始化。

它实现了读、写、删除和清空缓存的基本操作方法, 内部由具体Flea缓存实现类【fleaCache】 调用对应的 读、写、删除 和 清空 缓存的基本操作方法。

AbstractFleaCache 不同之处在于,实现了 SpringCache 接口,用于对接 Spring,相关配置后面会介绍一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
public abstract class AbstractSpringCache implements Cache, IFleaCache {

private static final FleaLogger LOGGER = FleaLoggerProxy.getProxyInstance(AbstractSpringCache.class);

private final String name; // 缓存主要关键字(用于区分)

private final IFleaCache fleaCache; // 具体Flea缓存实现

public AbstractSpringCache(String name, IFleaCache fleaCache) {
this.name = name;
this.fleaCache = fleaCache;
}

@Override
public String getName() {
return name;
}

@Override
public IFleaCache getNativeCache() {
return fleaCache;
}

@Override
public ValueWrapper get(Object key) {
if (ObjectUtils.isEmpty(key))
return null;
ValueWrapper wrapper = null;
Object cacheValue = get(key.toString());
if (ObjectUtils.isNotEmpty(cacheValue)) {
wrapper = new SimpleValueWrapper(cacheValue);
}
return wrapper;
}

@Override
public <T> T get(Object key, Class<T> type) {
if (ObjectUtils.isEmpty(key) || ObjectUtils.isEmpty(type))
return null;
Object cacheValue = get(key.toString());
if (!type.isInstance(cacheValue)) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug1(new Object() {}, "Cached value is not of required type [{}] : {}", type.getName(), cacheValue);
}
return null;
}
return type.cast(cacheValue);
}

@Override
public <T> T get(Object key, Callable<T> valueLoader) {
return null;
}

@Override
public Object get(String key) {
if (StringUtils.isBlank(key))
return null;
Object obj = null;
if (LOGGER.isDebugEnabled()) {
obj = new Object() {};
LOGGER.debug1(obj, "KEY = {}", key);
}
Object cacheValue = fleaCache.get(key);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug1(obj, "VALUE = {}", cacheValue);
}
return cacheValue;
}

@Override
public void put(Object key, Object value) {
if (ObjectUtils.isEmpty(key))
return;
put(key.toString(), value);
}

@Override
public ValueWrapper putIfAbsent(Object key, Object value) {
if (ObjectUtils.isEmpty(key))
return null;
ValueWrapper wrapper = null;
Object cacheValue = get(key.toString());
if (ObjectUtils.isEmpty(cacheValue)) {
put(key.toString(), value);
} else {
wrapper = new SimpleValueWrapper(cacheValue);
}
return wrapper;
}

@Override
public void put(String key, Object value) {
fleaCache.put(key, value);
}

@Override
public void evict(Object key) {
if (ObjectUtils.isEmpty(key))
return;
delete(key.toString());
}

@Override
public void clear() {
fleaCache.clear();
}

@Override
public void delete(String key) {
fleaCache.delete(key);
}

@Override
public Set<String> getCacheKey() {
return fleaCache.getCacheKey();
}

@Override
public String getSystemName() {
return fleaCache.getSystemName();
}
}

4.2 定义Memcached Spring缓存

MemCachedSpringCache 只定义构造方法,使用 MemCachedFleaCache 作为具体缓存实现。

它继承了抽象Spring缓存父类的 读、写、删除 和 清空 缓存的基本操作方法,由MemCached Spring缓存管理类初始化。
它的构造方法中,必须传入一个具体Flea缓存实现类,这里我们使用 MemCached Flea缓存【MemCachedFleaCache】。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MemCachedSpringCache extends AbstractSpringCache {

/**
* <p> 带参数的构造方法,初始化MemCached Spring缓存类 </p>
*
* @param name 缓存数据主关键字
* @param fleaCache 具体缓存实现
* @since 1.0.0
*/
public MemCachedSpringCache(String name, IFleaCache fleaCache) {
super(name, fleaCache);
}

/**
* <p> 带参数的构造方法,初始化MemCached Spring缓存类 </p>
*
* @param name 缓存数据主关键字
* @param expiry 缓存数据有效期(单位:s)
* @param nullCacheExpiry 空缓存数据有效期(单位:s)
* @param memCachedClient MemCached客户端
* @since 1.0.0
*/
public MemCachedSpringCache(String name, int expiry, int nullCacheExpiry, MemCachedClient memCachedClient) {
this(name, new MemCachedFleaCache(name, expiry, nullCacheExpiry, memCachedClient));
}
}

4.3 定义抽象Spring缓存管理类

AbstractSpringCacheManager 继承 AbstractTransactionSupportingCacheManager,用于对接 Spring 框架管理缓存。

同步集合类【cacheMap】, 存储的键为缓存数据主关键字, 存储的值为具体的缓存实现类。

  • 如果是整合各类缓存接入,它的键对应缓存定义配置文件【flea-cache.xml】 中的【<cache key="缓存数据主关键字"></cache>】;
  • 如果是单个缓存接入,它的键对应【applicationContext.xml】中 【<entry key="缓存数据主关键字" value="有效期(单位:s)"/>】;

抽象方法【newCache】,由子类实现具体的Spring缓存类创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public abstract class AbstractSpringCacheManager extends AbstractTransactionSupportingCacheManager {

private static final ConcurrentMap<String, AbstractSpringCache> cacheMap = new ConcurrentHashMap<>();

private Map<String, Integer> configMap = new HashMap<>(); // 各缓存的时间Map

@Override
protected Collection<? extends AbstractSpringCache> loadCaches() {
return cacheMap.values();
}

@Override
public AbstractSpringCache getCache(String name) {
if (!cacheMap.containsKey(name)) {
synchronized (cacheMap) {
if (!cacheMap.containsKey(name)) {
Integer expiry = configMap.get(name);
if (expiry == null) {
expiry = CommonConstants.NumeralConstants.INT_ZERO; // 表示永久
configMap.put(name, expiry);
}
cacheMap.put(name, newCache(name, expiry));
}
}
}
return cacheMap.get(name);
}

/**
* 由子类实现该方法,新创建一个抽象 Spring 缓存的子类。
*
* @param name 缓存名
* @param expiry 有效期(单位:s 其中0:表示永久)
* @return 新建的缓存对象
* @since 1.0.0
*/
protected abstract AbstractSpringCache newCache(String name, int expiry);

/**
* <p> 设置各缓存有效期配置Map </p>
*
* @param configMap 有效期配置Map
* @since 1.0.0
*/
public void setConfigMap(Map<String, Integer> configMap) {
this.configMap = configMap;
}
}

4.4 定义Memcached Spring缓存管理类

MemCachedSpringCacheManager 用于接入Spring框架管理 MemCached 缓存。

它的默认构造方法,用于单个缓存接入场景,首先新建MemCached 客户端, 然后初始化 MemCached 连接池。

方法【newCache】用于创建一个MemCached Spring缓存, 而它内部是由MemCached Flea缓存实现具体的 读、写、删除 和 清空 缓存的基本操作。

它基本实现同 MemCachedFleaCacheManager,不同在于 newCache 方法返回一个 MemCachedSpringCache 的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class MemCachedSpringCacheManager extends AbstractSpringCacheManager {

private MemCachedClient memCachedClient; // MemCached客户端类

/**
* 用于新建MemCached客户端,并初始化MemCached连接池。
*
* @since 1.0.0
*/
public MemCachedSpringCacheManager() {
memCachedClient = new MemCachedClient();
initPool();
}

/**
* 以传入参数初始化MemCached客户端,并初始化MemCached连接池。
*
* @param memCachedClient MemCached客户端
*/
public MemCachedSpringCacheManager(MemCachedClient memCachedClient) {
this.memCachedClient = memCachedClient;
initPool();
}

/**
* <p> 初始化MemCached连接池 </p>
*
* @since 1.0.0
*/
private void initPool() {
MemCachedPool.getInstance().initialize();
}

@Override
protected AbstractSpringCache newCache(String name, int expiry) {
int nullCacheExpiry = MemCachedConfig.getConfig().getNullCacheExpiry();
return new MemCachedSpringCache(name, expiry, nullCacheExpiry, memCachedClient);
}
}

4.5 spring 配置

如下用于配置缓存管理 MemCachedSpringCacheManager,其中 configMap 为缓存时间(key缓存对象名称 value缓存过期时间)

1
2
3
4
5
6
7
8
9
10
<bean id="memCachedSpringCacheManager" class="com.huazie.fleaframework.cache.memcached.manager.MemCachedSpringCacheManager">
<property name="configMap">
<map>
<entry key="fleaparadetail" value="86400"/>
</map>
</property>
</bean>

<!-- 开启缓存, 此处定义表示由spring接入来管理缓存访问 -->
<cache:annotation-driven cache-manager="memCachedSpringCacheManager" proxy-target-class="true"/>

4.6 缓存自测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
    private ApplicationContext applicationContext;

@Before
public void init() {
applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
LOGGER.debug("ApplicationContext={}", applicationContext);
}

@Test
public void testMemCachedSpringCache() {
try {
AbstractSpringCacheManager manager = (MemCachedSpringCacheManager) applicationContext.getBean("memCachedSpringCacheManager");
LOGGER.debug("MemCachedCacheManager={}", manager);

AbstractSpringCache cache = manager.getCache("fleaparadetail");
LOGGER.debug("Cache={}", cache);

Set<String> cacheKey = cache.getCacheKey();
LOGGER.debug("CacheKey = {}", cacheKey);
// 缓存清理
// cache.clear();

//## 1. 简单字符串
// cache.put("menu1", "huazie");
// cache.get("menu1");
// cache.get("menu1", String.class);

//## 2. 简单对象(要是可以序列化的对象)
// String user = new String("huazie");
// cache.put("user", user);
// LOGGER.debug(cache.get("user", String.class));

//## 3. List塞对象
// List<String> userList = new ArrayList<String>();
// userList.add("huazie");
// userList.add("lgh");
// cache.put("user_list", userList);

// LOGGER.debug(cache.get("user_list",userList.getClass()).toString());

} catch (Exception e) {
LOGGER.error("Exception:", e);
}
}

4.7 业务逻辑层接入缓存管理

@Cacheable 使用,value 为缓存名,也作缓存主关键字, key 为具体的缓存键。

1
2
3
4
5
6
7
8
9
10
11
12
@Cacheable(value = "fleaparadetail", key = "#paraType + '_' + #paraCode")
public FleaParaDetail getParaDetail(String paraType, String paraCode) throws Exception {

List<FleaParaDetail> fleaParaDetails = fleaParaDetailDao.getParaDetail(paraType, paraCode);
FleaParaDetail fleaParaDetailValue = null;

if (CollectionUtils.isNotEmpty(fleaParaDetails)) {
fleaParaDetailValue = fleaParaDetails.get(0);
}

return fleaParaDetailValue;
}

结语

至此,Memcached 的接入工作已经全部完成,下一篇将讲解 flea-cache使用之Redis分片模式接入,敬请期待哦!!!