spring aop + xmemcached 配置service层缓存策略

简介:


本文转自 bxst 51CTO博客,原文链接:http://blog.51cto.com/13013670/1944035

Memcached 作用与使用 基本介绍

1,对于缓存的存取方式,简言之,就是以键值对的形式将数据保存在内存中。在日常业务中涉及的操作无非就是增删改查。加入缓存机制后,查询的时候,对数据进行缓存,增删改的时候,清除缓存即可。这其中对于缓存的闭合就非常重要,如果缓存没有及时得到更新,那用户就会获取到过期数据,就会产生问题。

2,对于单一业务的缓存管理(数据库中只操作单表),只需生成一个key,查询时,使用key,置入缓存;增删改时,使用key,清除缓存。将key与表绑定,操作相对简单。

3,但是在现实业务中,更多的是对关联表的增删改查(数据库多表操作),业务之间互相关联,数据库中的某张表不止一个业务再进行操作,将缓存拦截在service层,对业务进行缓存,对多表进行缓存。

4,业务层缓存实现策略:

  4.1,在缓存中建立一个key为"union_query",value为“hashmap<prefix,uqversion>(‘简称uqmap’)“的缓存,prefix保存的是当前业务操作涉及到的数据库表名的组合(数据库表名的唯一性),使用‘|’分隔(例 prefix=“A|B”,此次业务将操作A表与B表),uqversion是业务版本号,从0开始递增。

  4.2,调用一个查询业务时,对数据进行缓存,设置operation为1,告诉cache对象,这是一个缓存操作,例如调用 queryAB(args[])方法时,cache对象切入,将prefix(即”A|B“)与uqversion(初始化为0),存入uqmap中进行缓存。

  4.3,将prefix,uqversion,方法明+参数,进行拼接,使用md5进行加密后作为一个key,将方法的结果集作为value,进行缓存。至此缓存成功。

  4.4,当第二个请求来调用queryAB时,cache对象切入,首先,查询uqmap对象,使用prefix找到对应的uqversion,然后,通过拼接加密获取key,最后取得结果集进行返回。

  4.5,当有一个updateA方法被调用时,设置operation为4,告诉cache对象,这是一个删除缓存的操作,此时prefix的值为“A”,cache对象切入,获取全局的uqmap,遍历其中的prefix,是否包含了表A的名称:如果包含,则更新此prefix的uqversion进行自增,uqversion一旦发生变化,4.3中组合的key将不复存在,业务缓存也就消失了。(对于复杂的updateAB方法,遍历prefix要复杂一点,可以实现)

  4.6,当第三个请求来调用queryAB时,可以获取到uqversion,组合成key后,但是没有对应的value。此时确定缓存不存在时,继续正常执行方法,获取结果集,返回给客户的同时,将结果集进行缓存。

5,对于缓存的操作,网上有三种api可以选择(memcached client forjava、spymemcached、xmemcached),具体的好坏,本人在这就不做分析。本人使用的是XMemcached api。

具体实现细节:

1,新建 @interface Annotation{ } 定义一个注解 @Annotation,一个注解是一个类。定义缓存策略。

?
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
import  java.lang.annotation.Documented;
import  java.lang.annotation.ElementType;
import  java.lang.annotation.Inherited;
import  java.lang.annotation.Retention;
import  java.lang.annotation.RetentionPolicy;
import  java.lang.annotation.Target;
 
/**
  * 用于查找的时候,放置缓存信息
  * @author shufeng
  */
@Target (ElementType.METHOD)
@Retention (RetentionPolicy.RUNTIME)
@Documented
@Inherited
public  @interface  XmemCache{
 
     /**
      * 值为当前操作的表名,表名唯一
      * 涉及到多表操作,使用|分隔
      */
     String prefix()  default  "" ;
     
     /*
      *    缓存有效期 设置,单位为秒
      *    指定间隔时间,默认值为3600秒(1小时)
      * */
     int interval() default 3600; 
     
     /**
      *     1 从cache里取值,如果未置入cache,则置入
      *   2 replace cache value                     未扩展
      *   3 replace cache value,并返回旧值         未扩展
      *   4 remove cache key 从cache里删除对应的缓存        
      *   5 remove cache key 从cache里删除对应的缓存,并返回未删除之前的值       未扩展
      **/
     int  operation()  default  1
}

2,memcache基础操作类,对一些常用方法进行封装,对memcachedclient进行配置

?
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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
import  java.io.IOException;
import  java.io.InputStream;
import  java.util.HashMap;
import  java.util.Iterator;
import  java.util.Map;
import  java.util.Properties;
import  java.util.Set;
import  java.util.concurrent.TimeoutException;
 
import  org.slf4j.Logger;
import  org.slf4j.LoggerFactory;
import  org.springframework.beans.factory.DisposableBean;
 
import  com.node.hlhw.rbac.api.constant.Constant;
 
import  net.rubyeye.xmemcached.GetsResponse;
import  net.rubyeye.xmemcached.MemcachedClient;
import  net.rubyeye.xmemcached.MemcachedClientBuilder;
import  net.rubyeye.xmemcached.XMemcachedClientBuilder;
import  net.rubyeye.xmemcached.command.BinaryCommandFactory;
import  net.rubyeye.xmemcached.exception.MemcachedException;
import  net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator;
import  net.rubyeye.xmemcached.transcoders.SerializingTranscoder;
import  net.rubyeye.xmemcached.utils.AddrUtil;
 
/**
  * @author Melody shufeng
  * 对memcachedclient进行封装,添加一下常用方法
  */
public  class  MemcachedOperate  implements  DisposableBean {
 
     /*
      * timeout - Operation timeout,if the method is not returned in this
      * time,throw TimeoutException timeout - operation timeout,in milliseconds
      * exp - An expiration time, in seconds. Can be up to 30 days. After 30
      * days, is treated as a unix timestamp of an exact date. value - stored
      * data
      */
     private static final Logger logger = LoggerFactory.getLogger(MemcachedOperate.class);
     
     private static Properties PROPERTIES = new Properties();
     
     private static String MEMCACHED_SETTING = "memcached.properties";
     
     private static MemcachedClient memcachedClient;
     
 
     public static MemcachedClient getClient(){
         return memcachedClient;
     }
     
     
     /**
      * 静态代码块,类加载时,初始化缓存客户端
      * 确保只创建一个client实例
      * author shufeng 
      */
     static {
         InputStream in = Object.class.getResourceAsStream("/" + MEMCACHED_SETTING);
         try {
             PROPERTIES.load(in);
         } catch (IOException e) {
             e.printStackTrace();
         }
         String servers = PROPERTIES.getProperty("memcached.servers", "");
         if (null != servers && !"".equals(servers)) {
             try {
                 logger.debug("启动memcached连接");
                 MemcachedClientBuilder builder = new XMemcachedClientBuilder(AddrUtil.getAddresses(servers));
                 builder.setConnectionPoolSize(100);
                 builder.setFailureMode(true);
                 builder.setCommandFactory(new BinaryCommandFactory());
                 builder.setSessionLocator(new KetamaMemcachedSessionLocator());
                 builder.setTranscoder(new SerializingTranscoder());
                 memcachedClient = builder.build();
                 memcachedClient.setEnableHeartBeat(false); // 关闭心跳
                 memcachedClient.flushAll(); // 清空缓存
             } catch (IOException e) {
                 e.printStackTrace();
             } catch (TimeoutException e) {
                 e.printStackTrace();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             } catch (MemcachedException e) {
                 e.printStackTrace();
             } catch (Exception e) {
                 e.printStackTrace();
             }
         }
     }
     
     /**
      * @param key
      * @return 获取value
      */
     public static Object get(String key) {
         Object object = null;
         try {
             object = memcachedClient.get(key);
         } catch (TimeoutException e) {
             e.printStackTrace();
         } catch (InterruptedException e) {
             e.printStackTrace();
         } catch (MemcachedException e) {
             e.printStackTrace();
         }
         return object;
     }
 
 
     public static void setWithNoReply(String key, int exp, Object value) {
         try {
             memcachedClient.setWithNoReply(key, exp, value);
         } catch (InterruptedException e) {
             e.printStackTrace();
相关文章
|
21天前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
31 0
|
14天前
|
Java API Spring
在 Spring 配置文件中配置 Filter 的步骤
【10月更文挑战第21天】在 Spring 配置文件中配置 Filter 是实现请求过滤的重要手段。通过合理的配置,可以灵活地对请求进行处理,满足各种应用需求。还可以根据具体的项目要求和实际情况,进一步深入研究和优化 Filter 的配置,以提高应用的性能和安全性。
|
12天前
|
存储 缓存 监控
利用 Redis 缓存特性避免缓存穿透的策略与方法
【10月更文挑战第23天】通过以上对利用 Redis 缓存特性避免缓存穿透的详细阐述,我们对这一策略有了更深入的理解。在实际应用中,我们需要根据具体情况灵活运用这些方法,并结合其他技术手段,共同保障系统的稳定和高效运行。同时,要不断关注 Redis 缓存特性的发展和变化,及时调整策略,以应对不断出现的新挑战。
43 10
|
8天前
|
Web App开发 缓存 UED
如何设置浏览器的缓存策略?
【10月更文挑战第23天】通过合理地设置浏览器的缓存策略,可以在提高网页性能、减少网络流量的同时,确保用户能够获取到最新的内容,从而提升用户体验和网站的性能优化效果。
38 4
|
6天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
18 1
|
9天前
|
存储 消息中间件 缓存
缓存策略
【10月更文挑战第25天】在实际应用中,还需要不断地监控和调整缓存策略,以适应系统的变化和发展。
|
9天前
|
存储 缓存 Java
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
43 2
|
12天前
|
缓存 监控 NoSQL
Redis 缓存穿透及其应对策略
【10月更文挑战第23天】通过以上对 Redis 缓存穿透的详细阐述,我们对这一问题有了更深入的理解。在实际应用中,我们需要根据具体情况综合运用多种方法来解决缓存穿透问题,以保障系统的稳定运行和高效性能。同时,要不断关注技术的发展和变化,及时调整策略,以应对不断出现的新挑战。
32 4
|
16天前
|
存储 缓存 NoSQL
保持HTTP会话状态:缓存策略与实践
保持HTTP会话状态:缓存策略与实践
|
22天前
|
Java BI 调度
Java Spring的定时任务的配置和使用
遵循上述步骤,你就可以在Spring应用中轻松地配置和使用定时任务,满足各种定时处理需求。
108 1

热门文章

最新文章