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();
相关文章
|
3月前
|
缓存 负载均衡 监控
135_负载均衡:Redis缓存 - 提高缓存命中率的配置与最佳实践
在现代大型语言模型(LLM)部署架构中,缓存系统扮演着至关重要的角色。随着LLM应用规模的不断扩大和用户需求的持续增长,如何构建高效、可靠的缓存架构成为系统性能优化的核心挑战。Redis作为业界领先的内存数据库,因其高性能、丰富的数据结构和灵活的配置选项,已成为LLM部署中首选的缓存解决方案。
|
3月前
|
缓存 并行计算 监控
vLLM 性能优化实战:批处理、量化与缓存配置方案
本文深入解析vLLM高性能部署实践,揭秘如何通过continuous batching、PagedAttention与前缀缓存提升吞吐;详解批处理、量化、并发参数调优,助力实现高TPS与低延迟平衡,真正发挥vLLM生产级潜力。
842 0
vLLM 性能优化实战:批处理、量化与缓存配置方案
|
4月前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
735 5
|
4月前
|
存储 缓存 Java
Spring中@Cacheable、@CacheEvict以及其他缓存相关注解的实用介绍
缓存是提升应用性能的重要技术,Spring框架提供了丰富的缓存注解,如`@Cacheable`、`@CacheEvict`等,帮助开发者简化缓存管理。本文介绍了如何在Spring中配置缓存管理器,使用缓存注解优化数据访问,并探讨了缓存的最佳实践,以提升系统响应速度与可扩展性。
357 0
Spring中@Cacheable、@CacheEvict以及其他缓存相关注解的实用介绍
|
5月前
|
缓存 NoSQL 数据库
Django缓存机制详解:从配置到实战应用
本文全面解析Django缓存技术,涵盖配置方法与六大缓存后端,结合实战场景演示四种典型应用方式,帮助开发者提升Web应用性能,应对高并发挑战。
193 0
|
6月前
|
缓存 NoSQL API
Django缓存机制详解:从配置到实战应用
本文介绍了 Django 缓存机制的基础知识与实战应用,涵盖缓存概念、Redis 安装配置、缓存策略及 API 使用,并通过 RBAC 权限系统演示缓存的读写与删除操作,助力提升 Web 应用性能。
172 0
|
7月前
|
缓存 负载均衡 网络协议
电商API接口性能优化技术揭秘:缓存策略与负载均衡详解
电商API接口性能优化是提升系统稳定性和用户体验的关键。本文聚焦缓存策略与负载均衡两大核心,详解其在电商业务中的实践。缓存策略涵盖本地、分布式及CDN缓存,通过全量或部分缓存设计和一致性维护,减少后端压力;负载均衡则利用反向代理、DNS轮询等技术,结合动态调整与冗余部署,提高吞吐量与可用性。文中引用大型及跨境电商平台案例,展示优化效果,强调持续监控与迭代的重要性,为电商企业提供了切实可行的性能优化路径。
|
6月前
|
存储 缓存 NoSQL
Spring Cache缓存框架
Spring Cache是Spring体系下的标准化缓存框架,支持多种缓存(如Redis、EhCache、Caffeine),可独立或组合使用。其优势包括平滑迁移、注解与编程两种使用方式,以及高度解耦和灵活管理。通过动态代理实现缓存操作,适用于不同业务场景。
567 0
|
7月前
|
存储 缓存
.NET 6中Startup.cs文件注入本地缓存策略与服务生命周期管理实践:AddTransient, AddScoped, AddSingleton。
记住,选择正确的服务生命周期并妥善管理它们是至关重要的,因为它们直接影响你的应用程序的性能和行为。就像一个成功的建筑工地,工具箱如果整理得当,工具选择和使用得当,工地的整体效率将会大大提高。
291 0
|
4月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
559 0