【Redis使用系列】使用Redis做防止重復提交


前言

在平時的開發中我們都需要處理重復提交的問題,避免業務出錯或者產生臟數據,雖然可以通過前端控制但這並不是可以完全避免,最好的方式還是前后端均進行控制,這樣的話就可以更有效,盡可能全面的去減少錯誤的發生。

一、比如我們注冊的時候需要發送驗證碼

如果用戶頻繁點擊或者惡意攻擊的話就會造成不斷的請求對服務器產生很大的壓力,為了避免這種情況我們需要做處理,傳統的模式中是在數據庫中記錄手機號、驗證碼已經發送時間,再次請求的時候呢去數據庫查詢是否有該手機號記錄,並校驗是否超過間隔時間,如果超過則重新發送並更新時間,否組不予發送,這樣有一個缺點就是如果同時又很多人在做相同的業務同時查詢就會對數據庫造成很大的壓力。

根據此種情況我們可以使用Redis incrde 原子性遞增,來解決這種高並發的秒殺或者分布式序列號生成等場景。鑒於本場景我們只用他來做計數實現間隔時間內只接收一次請求。

實現邏輯:在發送短信之后使用Redis的incr設置一個遞增的KEY(根據自己的需要設定但是要保證每一個人的唯一),來判斷該KEY的數值,如果等於1說明這是第一次請求,發送短信記錄日志,並設置有效期,如果不等於的話說明是間隔時間內多次請求,就提示請求頻繁,稍后重試。

 1 String redisKey = "SMS_SEND_" + smsPhone;
 2 long count = redisTemplate.opsForValue().increment(redisKey, 1);
 3 if (count == 1) {
 4 //設置有效期一分鍾
 5   redisTemplate.expire(redisKey, 60, TimeUnit.SECONDS);
 6 }
 7 if (count > 1) {
 8    resultMap.put("retCode", "-1");     
 9    resultMap.put("retMsg", "每分鍾只能發送一次短信");
10    outPrintJson(resultMap);
11    return;
12 }
13 /** 發送短信 */
14 ......
15 /** 記錄發送日志 */
16 ......

二、上述方式可以解決特定的問題,當需要處理的情況多的話我們可以考慮使用切面來解決

代碼如下:

 1 package com.slp.annotation;
 2 
 3 public class RedisLockBean {
 4     private String key;
 5     private int timeInSecond;
 6     private String codeName;
 7     private String msgName;
 8     private String code;
 9     private String msg;
10     private boolean isAtController;
11     private boolean isAtService;
12     private boolean isAtParameter;
13     private String returnType;
14     public String getKey() {
15         return key;
16     }
17     public void setKey(String key) {
18         this.key = key;
19     }
20     public int getTimeInSecond() {
21         return timeInSecond;
22     }
23     public void setTimeInSecond(int timeInSecond) {
24         this.timeInSecond = timeInSecond;
25     }
26     public String getCodeName() {
27         return codeName;
28     }
29     public void setCodeName(String codeName) {
30         this.codeName = codeName;
31     }
32     public String getMsgName() {
33         return msgName;
34     }
35     public void setMsgName(String msgName) {
36         this.msgName = msgName;
37     }
38     public String getCode() {
39         return code;
40     }
41     public void setCode(String code) {
42         this.code = code;
43     }
44     public String getMsg() {
45         return msg;
46     }
47     public void setMsg(String msg) {
48         this.msg = msg;
49     }
50     
51     
52     public boolean isAtController() {
53         return isAtController;
54     }
55     public void setAtController(boolean isAtController) {
56         this.isAtController = isAtController;
57     }
58     public boolean isAtService() {
59         return isAtService;
60     }
61     public void setAtService(boolean isAtService) {
62         this.isAtService = isAtService;
63     }
64     public boolean isAtParameter() {
65         return isAtParameter;
66     }
67     public void setAtParameter(boolean isAtParameter) {
68         this.isAtParameter = isAtParameter;
69     }
70     public String getReturnType() {
71         return returnType;
72     }
73     public void setReturnType(String returnType) {
74         this.returnType = returnType;
75     }
76     @Override
77     public String toString() {
78         return "RedisLockBean [key=" + key + ", timeInSecond=" + timeInSecond
79                 + ", codeName=" + codeName + ", msgName=" + msgName + ", code="
80                 + code + ", msg=" + msg + ", isAtController=" + isAtController
81                 + ", isAtService=" + isAtService + ", isAtParameter="
82                 + isAtParameter + ", returnType=" + returnType + "]";
83     }
84 }
 1 package com.slp.annotation;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 /**
 8  * 解決的問題:<br>
 9  *         1.數據庫加鎖性能較差<br>
10  *          2.數據庫加鎖,若相應線程異常,所無法釋放<br>
11  * 注意事項:<br>
12  *           方法的返回值對象必須包含錯誤碼,錯誤信息屬性及其的get方法
13  *
14  */
15 @Target({ElementType.PARAMETER,ElementType.METHOD})
16 @Retention(RetentionPolicy.RUNTIME)
17 public @interface RedisLock {
18     /**
19      * 若加注解的入參時基本數據類型(int,long)或String時,fieldName無效<br>
20      * 若注解的參數是自定義對象時,請注意一下幾點:<br>
21      *         1.確保定義有相應屬性public修飾的get方法<br>
22      *         2.get方法的返回參數是基本的數據類型或String<br>
23      *         3.get方法的返回值不為空<br>
24      * 否則,加鎖失敗.
25      * @return
26      */
27     String[] fieldName() default {};
28     /**
29      * 鎖的有效時間,單位為秒,默認值為1
30      * @return
31      */
32     int timeInSecond() default 1;
33     /**
34      * 加鎖,鎖已被其它請求獲取時,直接返回重復提交,codeName指定返回對象的返回碼對應的屬性,默認值'code'
35      * @return
36      */
37     String codeName() default "code";
38     /**
39      * 加鎖,鎖已被其它請求獲取時,直接返回重復提交,msgName指定返回對象的返回信息對應的屬性,默認值'msg'
40      * @return
41      */
42     String msgName() default "msg";
43     /**
44      * 加鎖,鎖已被其它請求獲取時,直接返回重復提交,code指定返回對象的返回碼對應的值,默認值'09'
45      * @return
46      */
47     String code() default "09";
48     /**
49      * 加鎖,鎖已被其它請求獲取時,直接返回重復提交,msg指定返回對象的返回碼對應的值,默認值'重復提交'
50      * @return
51      */
52     String msg()  default "重復提交";
53     /**
54      * 注解作用與方法時,指定參數在參數列表中的索引
55      */
56     int paramIndex() default 0;
57 }
  1 package com.slp.annotation;
  2 
  3 import java.lang.annotation.Annotation;
  4 import java.lang.reflect.InvocationTargetException;
  5 import java.lang.reflect.Method;
  6 import java.util.ArrayList;
  7 import java.util.Arrays;
  8 import java.util.Collections;
  9 import java.util.HashMap;
 10 import java.util.List;
 11 import java.util.Map;
 12 
 13 import javax.servlet.http.HttpServletRequest;
 14 
 15 import org.apache.commons.lang.StringUtils;
 16 import org.aspectj.lang.ProceedingJoinPoint;
 17 import org.aspectj.lang.Signature;
 18 import org.aspectj.lang.reflect.MethodSignature;
 19 import org.slf4j.Logger;
 20 import org.slf4j.LoggerFactory;
 21 import org.springframework.beans.factory.annotation.Autowired;
 22 import org.springframework.stereotype.Component;
 23 import org.springframework.stereotype.Controller;
 24 import org.springframework.web.bind.annotation.RequestMapping;
 25 
 26 import com.alibaba.fastjson.JSONObject;
 27 import com.cul.culsite.common.RedisKeyConstants;
 28 import com.cul.culsite.service.RedisService;
 29 import com.cul.culsite.util.DateUtil;
 30 import com.cul.culsite.util.OxmHelper;
 31 
 32 @Component
 33 public class RedisLockAspect {
 34     private final static Logger logger = LoggerFactory.getLogger(RedisLockAspect.class);
 35     
 36     protected static final String XML_TYPE = "xml";
 37     protected static final String JSON_TYPE = "json";
 38     protected static final String ILLEGAL_TYPE = "illegal type";
 39     @Autowired
 40     private RedisService redisService;
 41     
 42     public Object redisLockParse(ProceedingJoinPoint p) throws Throwable{
 43         Signature signature = p.getSignature();
 44         boolean isRepetition = false;
 45         RedisLockBean redisLockBean = null;
 46         String value = System.nanoTime()+"";
 47         if(signature instanceof MethodSignature){
 48             //獲得接口中定義的方法的Method,但注解時加載實現類中方法的參數上
 49             MethodSignature methodSignature = (MethodSignature)signature;
 50             Method serviceMethod = methodSignature.getMethod();
 51             
 52             try {
 53                 Method serviceImpMethod = p.getTarget().getClass().getMethod(serviceMethod.getName(), serviceMethod.getParameterTypes());
 54                 //獲取key值
 55                 redisLockBean = getRedisLockKey(p.getTarget(),serviceImpMethod,p.getArgs());
 56                 //成功獲取key值,在redis中加鎖
 57                 if(redisLockBean!=null){
 58                     logger.info("redis lock value is :{}",value);
 59                     boolean isPutSuccess =redisService.setIfAbsent(redisLockBean.getKey(), value, redisLockBean.getTimeInSecond());
 60                     //加鎖失敗,直接返回
 61                     if(!isPutSuccess){
 62                         logger.info("get redis lock fail for {}",redisLockBean.getKey());
 63                         if(redisLockBean.isAtParameter()||redisLockBean.isAtService()){
 64                             Class<?> returnType = serviceImpMethod.getReturnType();
 65                             //加鎖方法有返回值
 66                             if(!returnType.getName().equals(java.lang.Void.class.getName())){
 67                                 //實例化返回值對象
 68                                 try {
 69                                     Object result = returnType.newInstance();
 70                                     //設置返回碼
 71                                     returnType.getMethod(getSetMethodNameByFieldName(redisLockBean.getCodeName()), java.lang.String.class).invoke(result, redisLockBean.getCode());
 72                                     //設置返回信息
 73                                     returnType.getMethod(getSetMethodNameByFieldName(redisLockBean.getMsgName()), java.lang.String.class).invoke(result, redisLockBean.getMsg());
 74                                     return result;
 75                                 } catch (InstantiationException e) {
 76                                     e.printStackTrace();
 77                                 } catch (IllegalAccessException e) {
 78                                     e.printStackTrace();
 79                                 } catch (IllegalArgumentException e) {
 80                                     e.printStackTrace();
 81                                 } catch (InvocationTargetException e) {
 82                                     e.printStackTrace();
 83                                 }
 84                             }else{
 85                                 throw new RuntimeException("@RedisLock作用的方法沒有返回參數");
 86                             }
 87                         }else if(redisLockBean.isAtController()){
 88                             Map<String,String> result = new HashMap<String,String>();
 89                             result.put(redisLockBean.getCodeName(), redisLockBean.getCode());
 90                             result.put(redisLockBean.getMsgName(), redisLockBean.getMsg());
 91                             return response(redisLockBean.getReturnType()==null?"json":redisLockBean.getReturnType(), result);
 92                         }
 93                     }else{
 94                         logger.info("get redis lock success for {}",redisLockBean.getKey());
 95                         isRepetition = true;
 96                     }
 97                 }
 98             } catch (NoSuchMethodException e) {
 99                 e.printStackTrace();
100             } catch (SecurityException e) {
101                 e.printStackTrace();
102             }
103         }
104         Object result = null;
105         try {
106             result = p.proceed();
107         } catch (Throwable e) {
108             throw e;
109         }finally{
110             if(redisLockBean!=null){
111                 if(isRepetition&&value.equals(redisService.get(redisLockBean.getKey()))){
112                     logger.info("lock has released :{}",redisLockBean.getKey());
113                     redisService.delete(redisLockBean.getKey());
114                 }
115                 
116             }
117         }
118         return result;
119     }
120     
121     private RedisLockBean getRedisLockKey(Object target,Method method,Object... object){
122         if(target == null){
123             throw new RuntimeException("get redis lock key error,target is null");
124         }
125         if(method==null){
126             throw new RuntimeException("get redis lock key error,method is null");
127         }
128         List<String> fieldValueList = new ArrayList<String>();
129         RedisLockBean redisLockBean = new RedisLockBean();
130         RedisLock redisLock = null;
131         //類上有@Controller說明@RedisLock是放在請求方法上,使用HttpServletRequest獲取請求參數
132         if(method.isAnnotationPresent(RedisLock.class)&&target.getClass().isAnnotationPresent(Controller.class)){
133             //controller層方法時對外開放的接口
134             if(method.isAnnotationPresent(RequestMapping.class)){
135                 redisLock = method.getAnnotation(RedisLock.class);
136                 //獲取方法中的HttpServletRequest類型的參數
137                 HttpServletRequest request = null;
138                 for(Object para:object){
139                     if(para instanceof HttpServletRequest){
140                         request = (HttpServletRequest)para;
141                         break;
142                     }
143                 }
144                 if(request==null){
145                     throw new RuntimeException("@RedisLock作用於controller層方法時,方法需要包含HttpServletRequest類型的參數");
146                 }
147                 //未定義加鎖參數時,默認使用mac
148                 String[] paraName = redisLock.fieldName();
149                 if(paraName==null||paraName.length==0){
150                     paraName=new String[]{"mac"};
151                 }
152                 for(String para:paraName){
153                     fieldValueList.add(request.getParameter(para));
154                 }
155                 if(fieldValueList.isEmpty()){
156                     throw new RuntimeException("@RedisLock作用於controller層方法時,生成key失敗,請求中沒有mac簽名");
157                 }
158                 //標示注解作用在controller成方法上
159                 redisLockBean.setAtController(true);
160             }else{
161                 throw new RuntimeException("@RedisLock作用於controller層的方法時,該方法上需要使用@RequestMapping注解");
162             }
163             //注解作用於非controller層方法上
164         }else if(method.isAnnotationPresent(RedisLock.class)){
165             redisLock = method.getAnnotation(RedisLock.class);
166             //參數的索引位置
167             int index = redisLock.paramIndex();
168             String[] fieldName = redisLock.fieldName();
169             String[] values = getFieldValue(object[index],fieldName);
170             //注解的參數時基本的數據類型或String,不需要傳入屬性名稱,否則設置的屬性,都必須獲得該屬性值
171             if(values==null || values.length!=fieldName.length && fieldName.length>0){
172                 return null;
173             }
174             fieldValueList.addAll(Arrays.asList(values));
175             redisLockBean.setAtService(true);
176         }else{
177             Annotation[][] annotations;
178             annotations = method.getParameterAnnotations();
179             for(int i=0;i<annotations.length;i++){
180                 for(Annotation annotation:annotations[i]){
181                     if(annotation instanceof RedisLock){
182                         RedisLock redisLockTmp = (RedisLock)annotation;
183                         if(redisLock==null){
184                             redisLock = redisLockTmp;
185                         }
186                         String[] fieldName = redisLockTmp.fieldName();
187                         String[] values = getFieldValue(object[i],fieldName);
188                         //注解的參數時基本的數據類型或String,不需要傳入屬性名稱,否則設置的屬性,都必須獲得該屬性值
189                         if(values==null || values.length!=fieldName.length && fieldName.length>0){
190                             return null;
191                         }
192                         fieldValueList.addAll(Arrays.asList(values));
193                         redisLockBean.setAtParameter(true);
194                     }
195                 }
196             }            
197         }
198         //未使用注解
199         if(fieldValueList.isEmpty()){
200             return null;
201         }
202         
203         //設置其它參數值
204         if(redisLockBean.getTimeInSecond()==0){
205             redisLockBean.setTimeInSecond(redisLock.timeInSecond());
206         }
207         if(StringUtils.isEmpty(redisLockBean.getCodeName())){
208             redisLockBean.setCodeName(redisLock.codeName());
209         }
210         if(StringUtils.isEmpty(redisLockBean.getCode())){
211             redisLockBean.setCode(redisLock.code());
212         }
213         if(StringUtils.isEmpty(redisLockBean.getMsgName())){
214             redisLockBean.setMsgName(redisLock.msgName());
215         }
216         if(StringUtils.isEmpty(redisLockBean.getMsg())){
217             redisLockBean.setMsg(redisLock.msg());
218         }
219         
220         Collections.sort(fieldValueList);
221         logger.info("all value of fieldName is {}",fieldValueList);
222         //生成key值
223         StringBuilder builder = new StringBuilder();
224         builder.append(target.getClass().getName())
225         .append("-")
226         .append(method.getName())
227         .append("-")
228         .append(Arrays.asList(method.getParameterTypes()))
229         .append("-")
230         .append(fieldValueList);
231         String lockKey = RedisKeyConstants.REDIS_LOCK + builder.toString();
232         logger.info("redis lock key is :{}",builder.toString());
233         redisLockBean.setKey(lockKey);
234         logger.info("redisLockBean :{}",redisLockBean.toString());
235         return redisLockBean;
236     }
237     private String[] getFieldValue(Object argObj,String...fieldName){
238         if(fieldName ==null || fieldName.length == 0){
239             return new String[]{getBaseClassValue(argObj)};
240         }
241         List<String> fieldsValue = new ArrayList<String>();
242         for(String field:fieldName){
243             String value = getFieldValue(argObj,field);
244             logger.info("value of fieldName '{}' is :{}",fieldName,value);
245             if(value!=null){
246                 fieldsValue.add(value);
247             }
248         }
249         return fieldsValue.toArray(new String[0]);
250     }
251     private String getFieldValue(Object argObj,String fieldName){
252         if(argObj==null){
253             throw new RuntimeException("argObj is null,cannot get field value of fieldName");
254         }
255         String value = getBaseClassValue(argObj);
256         if(!StringUtils.isEmpty(value)){
257             return value;
258         }
259         String methodName = getGetMethodValueByFieldName(fieldName);
260         Object result = null;
261         try {
262             Method method = argObj.getClass().getMethod(methodName);
263             result = method.invoke(argObj);
264         } catch (NoSuchMethodException e) {
265             logger.error("method {} without parameter is not exists!",methodName);
266             e.printStackTrace();
267         } catch (SecurityException e) {
268             e.printStackTrace();
269         } catch (IllegalAccessException e) {
270             logger.error("method {} without parameter is not public!",methodName);
271             e.printStackTrace();
272         } catch (IllegalArgumentException e) {
273             logger.error("method {} has parameter!",methodName);
274             e.printStackTrace();
275         } catch (InvocationTargetException e) {
276             e.printStackTrace();
277         }
278         if(result==null){
279             logger.warn("method {} does not have returnValue",methodName);
280             return null;
281         }
282         return getBaseClassValue(result);
283     }
284     private String getBaseClassValue(Object object){
285         if(object==null){
286             throw new RuntimeException("argObj is null,cannot get field value ");
287         }
288         if(object instanceof String){
289             return object.toString();
290         }
291         if(object instanceof Integer){
292             int i = (Integer)object;
293             //剔除成員變量的默認值
294             if(i!=0){
295                 return i+"";
296             }
297         }
298         if(object instanceof Long){
299             long i = (Long)object;
300             if(i!=0){
301                 return i+"";
302             }
303         }
304         return null;
305     }
306 
307     private String getGetMethodValueByFieldName(String fieldName){
308         return getMethodNameByFieldNameAndPrefix("get",fieldName);
309     }
310     private String getSetMethodNameByFieldName(String fieldName){
311         return getMethodNameByFieldNameAndPrefix("set",fieldName);
312     }
313     private String getMethodNameByFieldNameAndPrefix(String prefix,String fieldName){
314         if(StringUtils.isEmpty(fieldName)){
315             throw new RuntimeException("cannot get Get method by null or length is 0");
316         }
317         if(StringUtils.isEmpty(prefix)){
318             throw new RuntimeException("cannot get Get method by null without prefix");
319         }
320         String getMethodName = prefix+fieldName.substring(0, 1).toUpperCase();
321         //fieldName 的長度大於一時,索引大於一的字符不改變大小寫
322         if(fieldName.length()>1){
323             getMethodName = getMethodName + fieldName.substring(1);
324         }
325         return getMethodName;
326     }
327     
328     private String response(String type, Object obj) {
329         if (XML_TYPE.equalsIgnoreCase(type)) {
330             String ret = OxmHelper.marshal(obj);
331             logger.info("response:{}",ret);
332             return ret;
333         }
334         if (JSON_TYPE.equalsIgnoreCase(type)) {
335             String ret = JSONObject.toJSONString(obj);
336             logger.info("response:{}",ret);
337             return ret;
338         }
339         return ILLEGAL_TYPE + ":" + type;
340     }
341 }
  1 package com.slp.service.impl;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 import java.util.Set;
  6 import java.util.concurrent.TimeUnit;
  7 
  8 import org.slf4j.Logger;
  9 import org.slf4j.LoggerFactory;
 10 import org.springframework.beans.factory.annotation.Autowired;
 11 import org.springframework.dao.DataAccessException;
 12 import org.springframework.data.redis.core.RedisOperations;
 13 import org.springframework.data.redis.core.RedisTemplate;
 14 import org.springframework.data.redis.core.SessionCallback;
 15 import org.springframework.data.redis.core.ValueOperations;
 16 import org.springframework.stereotype.Service;
 17 import org.springframework.util.Assert;
 18 
 19 import com.cul.culsite.service.RedisService;
 20 
 21 @Service
 22 public class RedisServiceImpl implements RedisService {
 23     private static final Logger logger = LoggerFactory.getLogger(RedisServiceImpl.class);
 24     @Autowired
 25     private RedisTemplate<String, String> redisTemplate;
 26     
 27     /**
 28      * 獲取redis值
 29      * 
 30      * @param key
 31      *            鍵
 32      * @return 33      */
 34     public String get(String key) {
 35         Assert.hasText(key, "redis get key cannot null");
 36 
 37         return redisTemplate.opsForValue().get(key);
 38     }
 39     
 40     /**
 41      * 刪除redis鍵
 42      * 
 43      * @param key
 44      *            鍵
 45      */
 46     @Override
 47     public  void delete(String key) {
 48         Assert.hasText(key, "redis delete key cannot null");
 49         redisTemplate.delete(key);
 50     }
 51 
 52     
 53 
 54     /**
 55      * 設置redis值
 56      * 
 57      * @param key
 58      *            鍵
 59      * @param value
 60      *            值
 61      * @param time
 62      *            時間(分鍾)   如果小於0 默認為1分鍾     
 63      */
 64     @Override
 65     public  boolean setIfAbsent(final String key,final String value,int time) {
 66         Assert.hasText(key, "redis set key cannot null");
 67         Assert.hasText(value, "redis set value cannot null");
 68 
 69         if(time<=0){
 70             time = 1;
 71         }
 72         final int timeInSecond = time;
 73         try{
 74             
 75             @SuppressWarnings("unchecked")
 76             Object isSetSuccess = redisTemplate.execute(new SessionCallback() {
 77     
 78                 @Override
 79                 public Object execute(RedisOperations arg0)
 80                         throws DataAccessException {
 81                     try{
 82                          //開始事務
 83                         List<Object> result=new ArrayList<Object>();
 84                         arg0.multi();
 85                         arg0.opsForValue().setIfAbsent(key, value);
 86 //                        arg0.expireAt(key,DateUtils.addSeconds(new Date(),timeInSecond));
 87                         arg0.expire(key, timeInSecond, TimeUnit.SECONDS);
 88                      //提交事務
 89                         result= arg0.exec();
 90                     
 91                         logger.info("redis mutil for get lock result is :{}",result);
 92                      //執行了兩次redis操作,應該有兩個返回值,否則防止key永久有效,執行刪除操作
 93                         if(result == null||result.size()!=2){
 94                             redisTemplate.delete(key);
 95                             return false;
 96                         }
 97                      //獲取加鎖操作的返回結果
 98                         boolean setIfAbsentResult = false;
 99                         if(result.get(0) instanceof Boolean){
100                             setIfAbsentResult =(Boolean)result.get(0);
101                         }
102                      //獲取設置key有效時間返回結果
103                         boolean expireAtResult = false;
104                         if(result.get(1) instanceof Boolean){
105                             expireAtResult = (Boolean)result.get(1);
106                         }
107                         if(setIfAbsentResult&&expireAtResult){
108                             logger.info("加鎖成功.......");
109                             return true;
110                         }
111                     }catch(Exception e){
112                         e.printStackTrace();
113                     }
114                     return false;
115                 }
116     
117             });
118             if(isSetSuccess instanceof Boolean){
119                 return (Boolean) isSetSuccess;
120             }
121             return false;
122         }catch(Exception e){
123             e.printStackTrace();
124             return false;
125         }
126     }
127     
128     @Override
129     public Set<String> keys(String keyPattern) {
130         Assert.hasText(keyPattern, "keys pattern is null");
131         
132         return redisTemplate.keys(keyPattern);
133     }
134     
135     @Override
136     public long incr(String key) {
137         Assert.hasText(key, "key is null");
138         
139         return redisTemplate.opsForValue().increment(key, 1L);
140     }
141     @Override
142     public long decr(String key) {
143         Assert.hasText(key, "key is null");
144         
145         return redisTemplate.opsForValue().increment(key, -1L);
146     }
147     
148     @Override
149     public void set(String key, String value) {
150         Assert.hasText(key, "key is null");
151         Assert.hasText(value, "value is null");
152         redisTemplate.opsForValue().set(key, value);
153     }
154     
155     @Override
156     public boolean set(final String key, final long value, final int timeInSecond) {
157         Assert.hasText(key, "key is null");
158         Assert.hasText(value + "", "value is null");
159         Assert.hasText(timeInSecond + "", "timeInSecond is null");
160         
161         try{
162             
163             @SuppressWarnings("unchecked")
164             Object isSetSuccess = redisTemplate.execute(new SessionCallback() {
165     
166                 @Override
167                 public Object execute(RedisOperations arg0)
168                         throws DataAccessException {
169                     try{
170                          //開始事務
171                         List<Object> result=new ArrayList<Object>();
172                         arg0.multi();
173                         arg0.opsForValue().increment(key, value);
174                         arg0.expire(key, timeInSecond, TimeUnit.SECONDS);
175                      //提交事務
176                         result= arg0.exec();
177                     
178                         logger.info("result of redis set long value is :{}",result);
179                      //執行了兩次redis操作,應該有兩個返回值,否則防止key永久有效,執行刪除操作
180                         if(result == null || result.size() != 2){
181                             redisTemplate.opsForValue().increment(key, (0 - value));
182                             return false;
183                         }
184                      //獲取加鎖操作的返回結果
185                         long incrementResult = 0;
186                         if(result.get(0) instanceof Long){
187                             incrementResult =(Long)result.get(0);
188                         }
189                      //獲取設置key有效時間返回結果
190                         boolean expireAtResult = false;
191                         if(result.get(1) instanceof Boolean){
192                             expireAtResult = (Boolean)result.get(1);
193                         }
194                         if((incrementResult == value) && expireAtResult){
195                             return true;
196                         }
197                     }catch(Exception e){
198                         e.printStackTrace();
199                     }
200                     redisTemplate.opsForValue().increment(key, (0 - value));
201                     return false;
202                 }
203     
204             });
205             if(isSetSuccess instanceof Boolean){
206                 return (Boolean) isSetSuccess;
207             }
208             return false;
209         }catch(Exception e){
210             e.printStackTrace();
211             return false;
212         }
213         
214     }
215     
216     
217     public Long getLong(String key) {
218         try{
219          Set<String> keys = redisTemplate.keys(key);
220          //key指定的數據不存在
221          if (keys == null || keys.isEmpty()) {
222              return null;
223          }
224          return redisTemplate.opsForValue().increment(key, 0);
225         } catch (DataAccessException e) {
226             logger.info("error :{}", e);
227             logger.info("{}指定的數據不是數值類型", key);
228             throw new RuntimeException(key + "指定的數據不是數值類型");
229         }
230     }
231 
232     public Long getLongNoKeys(String key) {
233         try {
234             long keys = redisTemplate.opsForValue().increment(key, 0);
235             return keys;
236         } catch (DataAccessException e) {
237             logger.info("error :{}", e);
238             logger.info("{}指定的數據不是數值類型", key);
239             throw new RuntimeException(key + "指定的數據不是數值類型");
240         }
241     }
242 
243     
244     
245     
246     /** 
247      * 刪除set集合中的對象 
248      * @param key 
249      * @param value 
250      */  
251     @Override
252     public void srem(String key, String value) {  
253         redisTemplate.boundSetOps(key).remove(value);  
254     }  
255 }

使用方式:

/**
     * 
     * @Description: 
     * @param @param request
     * @param @return
     * @param @throws Exception   
     * @return String  
     * @throws
     * @author liping.sang
     * @date 2017-8-8
     */
    @RedisLock(fieldName={"reqNo"},timeInSecond=3)
    @RequestMapping(method = { RequestMethod.GET, RequestMethod.POST }, value = "/test2")
    @ResponseBody
    public String test2(HttpServletRequest request)
            throws Exception {
1 <aop:pointcut expression="execution(* com.slp.controller.Test.test(..))" id="test"/>
2 
3             <aop:around method="redisLockParse" pointcut-ref="test"/>            

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM