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();
相关文章
|
22天前
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
|
4月前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
228 6
|
24天前
|
缓存 搜索推荐 CDN
HTTP缓存策略的区别和解决的问题
总的来说,HTTP缓存策略是一种权衡,需要根据具体的应用场景和需求来选择合适的策略。理解和掌握这些策略,可以帮助我们更好地优化网页性能,提高用户的浏览体验。
40 11
|
2月前
|
数据采集 缓存 JavaScript
数据抓取的缓存策略:减少重复请求与资源消耗
本教程聚焦于提升爬虫效率与稳定性,通过结合缓存策略、代理IP技术(如爬虫代理)、Cookie和User-Agent设置,优化数据采集流程。以知乎为例,详细讲解如何抓取指定关键词的文章标题和内容。内容涵盖环境准备、代码实现、常见问题及解决方案,并提供延伸练习,帮助读者掌握高效爬虫技巧。适合具备Python基础的初学者,助你规避网站机制,顺利获取目标数据。
数据抓取的缓存策略:减少重复请求与资源消耗
|
3月前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
200 25
|
2月前
|
Java API 微服务
微服务——SpringBoot使用归纳——Spring Boot中的切面AOP处理——Spring Boot 中的 AOP 处理
本文详细讲解了Spring Boot中的AOP(面向切面编程)处理方法。首先介绍如何引入AOP依赖,通过添加`spring-boot-starter-aop`实现。接着阐述了如何定义和实现AOP切面,包括常用注解如`@Aspect`、`@Pointcut`、`@Before`、`@After`、`@AfterReturning`和`@AfterThrowing`的使用场景与示例代码。通过这些注解,可以分别在方法执行前、后、返回时或抛出异常时插入自定义逻辑,从而实现功能增强或日志记录等操作。最后总结了AOP在实际项目中的重要作用,并提供了课程源码下载链接供进一步学习。
96 0
|
2月前
|
Java 开发者 微服务
微服务——SpringBoot使用归纳——Spring Boot中的切面AOP处理——什么是AOP
本文介绍了Spring Boot中的切面AOP处理。AOP(Aspect Oriented Programming)即面向切面编程,其核心思想是分离关注点。通过AOP,程序可以将与业务逻辑无关的代码(如日志记录、事务管理等)从主要逻辑中抽离,交由专门的“仆人”处理,从而让开发者专注于核心任务。这种机制实现了模块间的灵活组合,使程序结构更加可配置、可扩展。文中以生活化比喻生动阐释了AOP的工作原理及其优势。
66 0
|
3月前
|
XML 安全 Java
Spring AOP—深入动态代理 万字详解(通俗易懂)
Spring 第四节 AOP——动态代理 万字详解!
125 24
|
4月前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
213 8
|
4月前
|
缓存 NoSQL Java
Mybatis学习:Mybatis缓存配置
MyBatis缓存配置包括一级缓存(事务级)、二级缓存(应用级)和三级缓存(如Redis,跨JVM)。一级缓存自动启用,二级缓存需在`mybatis-config.xml`中开启并配置映射文件或注解。集成Redis缓存时,需添加依赖、配置Redis参数并在映射文件中指定缓存类型。适用于查询为主的场景,减少增删改操作,适合单表操作且表间关联较少的业务。
101 6