├── README.md
└── redislockframework
├── .classpath
├── .gitignore
├── .project
├── .settings
├── org.eclipse.core.resources.prefs
├── org.eclipse.jdt.core.prefs
└── org.eclipse.m2e.core.prefs
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── liushao
│ └── redislockframework
│ ├── CacheLock.java
│ ├── CacheLockException.java
│ ├── CacheLockInterceptor.java
│ ├── LockedComplexObject.java
│ ├── LockedObject.java
│ ├── RedisClient.java
│ ├── RedisFactory.java
│ ├── RedisLock.java
│ └── Util.java
└── test
└── java
└── com
└── liushao
└── redislockframework
├── SecKillImpl.java
├── SecKillTest.java
└── SeckillInterface.java
/README.md:
--------------------------------------------------------------------------------
1 | # redisframework
2 | a demo that shows how to implement a 'second kill' using redis distributed lock.
3 |
--------------------------------------------------------------------------------
/redislockframework/.classpath:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/redislockframework/.gitignore:
--------------------------------------------------------------------------------
1 | /target/
2 |
--------------------------------------------------------------------------------
/redislockframework/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | redislockframework
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.m2e.core.maven2Builder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javanature
21 | org.eclipse.m2e.core.maven2Nature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/redislockframework/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding//src/main/java=UTF-8
3 | encoding//src/test/java=UTF-8
4 | encoding/=UTF-8
5 |
--------------------------------------------------------------------------------
/redislockframework/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
3 | org.eclipse.jdt.core.compiler.compliance=1.8
4 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
5 | org.eclipse.jdt.core.compiler.source=1.8
6 |
--------------------------------------------------------------------------------
/redislockframework/.settings/org.eclipse.m2e.core.prefs:
--------------------------------------------------------------------------------
1 | activeProfiles=
2 | eclipse.preferences.version=1
3 | resolveWorkspaceProjects=true
4 | version=1
5 |
--------------------------------------------------------------------------------
/redislockframework/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | com.liushao
6 | redislockframework
7 | 1.0.0-RELEASE
8 | jar
9 |
10 | redislockframework
11 | http://maven.apache.org
12 |
13 |
14 | UTF-8
15 |
16 |
17 |
18 |
19 | junit
20 | junit
21 | 4.7
22 | test
23 |
24 |
25 |
26 | com.alibaba
27 | fastjson
28 | 1.2.7
29 |
30 |
31 |
32 | redis.clients
33 | jedis
34 | 2.7.2
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/redislockframework/src/main/java/com/liushao/redislockframework/CacheLock.java:
--------------------------------------------------------------------------------
1 | package com.liushao.redislockframework;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | @Target(ElementType.METHOD)
10 | @Retention(RetentionPolicy.RUNTIME)
11 | @Documented
12 | public @interface CacheLock {
13 | String lockedPrefix() default "";//redis 锁key的前缀
14 | long timeOut() default 2000;//锁时间
15 | int expireTime() default 100000;//key在redis里存在的时间,1000S
16 | }
17 |
--------------------------------------------------------------------------------
/redislockframework/src/main/java/com/liushao/redislockframework/CacheLockException.java:
--------------------------------------------------------------------------------
1 | package com.liushao.redislockframework;
2 |
3 | public class CacheLockException extends Throwable{
4 | private String msg;
5 |
6 | public String getMsg() {
7 | return msg;
8 | }
9 |
10 | public void setMsg(String msg) {
11 | this.msg = msg;
12 | }
13 |
14 | public CacheLockException(String msg) {
15 | this.msg = msg;
16 | }
17 |
18 | public CacheLockException() {
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/redislockframework/src/main/java/com/liushao/redislockframework/CacheLockInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.liushao.redislockframework;
2 |
3 | import java.lang.annotation.Annotation;
4 | import java.lang.reflect.InvocationHandler;
5 | import java.lang.reflect.Method;
6 |
7 | public class CacheLockInterceptor implements InvocationHandler{
8 | public static int ERROR_COUNT = 0;
9 | private Object proxied;
10 |
11 |
12 | public CacheLockInterceptor(Object proxied) {
13 | this.proxied = proxied;
14 | }
15 |
16 | @Override
17 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
18 |
19 | CacheLock cacheLock = method.getAnnotation(CacheLock.class);
20 | //没有cacheLock注解,pass
21 | if(null == cacheLock){
22 | System.out.println("no cacheLock annotation");
23 | return method.invoke(proxied, args);
24 | }
25 |
26 | //获得方法中参数的注解
27 | Annotation[][] annotations = method.getParameterAnnotations();
28 | //根据获取到的参数注解和参数列表获得加锁的参数
29 | Object lockedObject = getLockedObject(annotations,args);
30 | String objectValue = lockedObject.toString();
31 | RedisLock lock = new RedisLock(cacheLock.lockedPrefix(), objectValue);
32 | boolean result = lock.lock(cacheLock.timeOut(), cacheLock.expireTime());
33 | if(!result){//取锁失败
34 | ERROR_COUNT += 1;
35 | throw new CacheLockException("get lock fail");
36 |
37 | }
38 | try{
39 | //执行方法
40 | return method.invoke(proxied, args);
41 | }finally{
42 | System.out.println("intecepor 释放锁");
43 | lock.unlock();//释放锁
44 | }
45 |
46 | }
47 |
48 |
49 | /**
50 | * 从方法参数中找出@lockedComplexOnbject的参数,在redis中取该参数对应的锁
51 | * @param annotations
52 | * @param args
53 | * @return
54 | * @throws CacheLockException
55 | */
56 | private Object getLockedObject(Annotation[][] annotations,Object[] args) throws CacheLockException{
57 | if(null == args || args.length == 0){
58 | throw new CacheLockException("方法参数为空,没有被锁定的对象");
59 | }
60 |
61 | if(null == annotations || annotations.length == 0){
62 | throw new CacheLockException("没有被注解的参数");
63 | }
64 | //不支持多个参数加锁,只支持第一个注解为lockedObject或者lockedComplexObject的参数
65 | int index = -1;//标记参数的位置指针
66 | for(int i = 0;i < annotations.length;i++){
67 | for(int j = 0;j < annotations[i].length;j++){
68 | if(annotations[i][j] instanceof LockedComplexObject){//注解为LockedComplexObject
69 | index = i;
70 | try {
71 | return args[i].getClass().getField(((LockedComplexObject)annotations[i][j]).field());
72 | } catch (NoSuchFieldException | SecurityException e) {
73 | throw new CacheLockException("注解对象中没有该属性" + ((LockedComplexObject)annotations[i][j]).field());
74 | }
75 | }
76 |
77 | if(annotations[i][j] instanceof LockedObject){
78 | index = i;
79 | break;
80 | }
81 | }
82 | //找到第一个后直接break,不支持多参数加锁
83 | if(index != -1){
84 | break;
85 | }
86 | }
87 |
88 | if(index == -1){
89 | throw new CacheLockException("请指定被锁定参数");
90 | }
91 |
92 | return args[index];
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/redislockframework/src/main/java/com/liushao/redislockframework/LockedComplexObject.java:
--------------------------------------------------------------------------------
1 | package com.liushao.redislockframework;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 |
9 | @Target(ElementType.PARAMETER)
10 | @Retention(RetentionPolicy.RUNTIME)
11 | @Documented
12 | public @interface LockedComplexObject {
13 | String field() default "";//含有成员变量的复杂对象中需要加锁的成员变量,如一个商品对象的商品ID
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/redislockframework/src/main/java/com/liushao/redislockframework/LockedObject.java:
--------------------------------------------------------------------------------
1 | package com.liushao.redislockframework;
2 |
3 | import java.lang.annotation.Documented;
4 | import java.lang.annotation.ElementType;
5 | import java.lang.annotation.Retention;
6 | import java.lang.annotation.RetentionPolicy;
7 | import java.lang.annotation.Target;
8 | /**
9 | *
10 | * @author liushao
11 | *
12 | */
13 | @Target(ElementType.PARAMETER)
14 | @Retention(RetentionPolicy.RUNTIME)
15 | @Documented
16 | public @interface LockedObject {
17 |
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/redislockframework/src/main/java/com/liushao/redislockframework/RedisClient.java:
--------------------------------------------------------------------------------
1 | package com.liushao.redislockframework;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.Iterator;
6 | import java.util.List;
7 | import java.util.Map;
8 | import java.util.Map.Entry;
9 | import java.util.Set;
10 | import com.alibaba.fastjson.JSON;
11 |
12 | import redis.clients.jedis.Jedis;
13 | import redis.clients.jedis.JedisPool;
14 | import redis.clients.jedis.Transaction;
15 |
16 | public class RedisClient {
17 |
18 | public JedisPool jedisPool;
19 |
20 | public RedisClient(JedisPool pool) {
21 | this.jedisPool = pool;
22 | }
23 |
24 | public RedisClient(){
25 |
26 | }
27 |
28 | public JedisPool getJedisPool() {
29 | return jedisPool;
30 | }
31 |
32 | public void setJedisPool(JedisPool jedisPool) {
33 | this.jedisPool = jedisPool;
34 | }
35 |
36 |
37 | /**
38 | * 根据key来获取对应的value
39 | */
40 | public Object getByKey(String key) {
41 | Jedis client = jedisPool.getResource();
42 | Object o = null;
43 | try {
44 | o = client.get(key);
45 | } finally {
46 | jedisPool.returnResourceObject(client);// 向连接池“归还”资源
47 | }
48 | return o;
49 | }
50 |
51 | /**
52 | * 判断String类型key是否存在
53 | */
54 | public boolean isKeyExist(String key) {
55 | Jedis client = jedisPool.getResource();
56 | boolean o = false;
57 | try {
58 | o = client.exists(key);
59 | } finally {
60 | jedisPool.returnResourceObject(client);;// 向连接池“归还”资源
61 | }
62 | return o;
63 | }
64 |
65 | /**
66 | * String类型的键值写入redis
67 | *
68 | * @param key
69 | * @param value
70 | * @return
71 | */
72 | public boolean set(String key, String value) {
73 | Jedis client = jedisPool.getResource();
74 | String issuccess = "";
75 | try {
76 | issuccess = client.set(key, value);
77 | if ("OK".equals(issuccess)) {
78 | return true;
79 | } else {
80 | return false;
81 | }
82 | } finally {
83 | jedisPool.returnResourceObject(client);;// 向连接池“归还”资源
84 | }
85 | }
86 |
87 | public Long setnx(String key, String value) {
88 | Jedis client = jedisPool.getResource();
89 | try {
90 |
91 | Long result = client.setnx(key, value);
92 | System.out.println("setnx key=" + key + " value=" + value +
93 | "result=" + result) ;
94 | return result;
95 | } finally {
96 | jedisPool.returnResourceObject(client);;// 向连接池“归还”资源
97 | }
98 | }
99 |
100 | /**
101 | * String类型的键值写入redis,并设置失效时间
102 | *
103 | * @param key
104 | * @param value
105 | * @return
106 | */
107 | public boolean setKeyWithExpireTime(String key, String value, int time) {
108 | if (time == 0) {
109 |
110 | }
111 | Jedis client = jedisPool.getResource();
112 | String issuccess = "";
113 | try {
114 | issuccess = client.setex(key, time, value);
115 | if ("OK".equals(issuccess)) {
116 | return true;
117 | } else {
118 | return false;
119 | }
120 | } finally {
121 | jedisPool.returnResourceObject(client);;// 向连接池“归还”资源
122 | }
123 | }
124 |
125 | /**
126 | * list结构的数据写入redis
127 | *
128 | * @param key
129 | * @param value
130 | * @return
131 | */
132 | public boolean lpush(String key, List value) {
133 | Jedis client = jedisPool.getResource();
134 | try {
135 | Transaction tx = client.multi();
136 | for (String one : value) {
137 | tx.lpush(key, one);
138 | }
139 | tx.exec();
140 | return true;
141 | } finally {
142 | jedisPool.returnResourceObject(client);;// 向连接池“归还”资源
143 | }
144 | }
145 |
146 | /**
147 | * 根据key获取list类型
148 | *
149 | * @param key
150 | * @return
151 | */
152 | public List lrange(String key) {
153 | Jedis client = jedisPool.getResource();
154 | List returnList = null;
155 | try {
156 | returnList = client.lrange(key, 0, -1);
157 |
158 | } finally {
159 | jedisPool.returnResourceObject(client);;// 向连接池“归还”资源
160 | }
161 | return returnList;
162 | }
163 |
164 | public List lrange(String key, int start, int length){
165 | Jedis client = jedisPool.getResource();
166 | try {
167 | return client.lrange(key, start, length);
168 | } finally{
169 | jedisPool.returnResourceObject(client);
170 | }
171 | }
172 |
173 | /**
174 | *
175 | * @param key
176 | * @param o
177 | * @return
178 | */
179 | public boolean setAnObject(String key, Object o) {
180 | Jedis client = jedisPool.getResource();
181 | try {
182 | String afterSerialize = JSON.toJSONString(o);
183 | o = client.set(key, afterSerialize);
184 | return true;
185 | } finally {
186 | jedisPool.returnResourceObject(client);// 向连接池“归还”资源
187 | }
188 | }
189 |
190 | @SuppressWarnings("unchecked")
191 | public T getSetT(String key, T newValue) {
192 | Jedis client = jedisPool.getResource();
193 | T t;
194 | try {
195 | String afterSerialize = Util.beanToJson(newValue);
196 | String value = client.getSet(key, afterSerialize);
197 | t = (T) Util.jsonToBean(value, newValue.getClass());
198 | return t;
199 | } finally {
200 | jedisPool.returnResourceObject(client);// 向连接池“归还”资源
201 | }
202 | }
203 |
204 | public T getAnObject(String key, Class zz) {
205 | Jedis client = jedisPool.getResource();
206 | T t;
207 | try {
208 | String s = client.get(key);
209 | if (s == null || s.length() == 0) {
210 | return null;
211 | }
212 | t = Util.jsonToBean(s, zz);
213 | } finally {
214 | jedisPool.returnResourceObject(client);
215 | }
216 | return t;
217 |
218 | }
219 |
220 | public List getKeys(String pattern) {
221 | Jedis client = jedisPool.getResource();
222 | List result = new ArrayList();
223 | try {
224 | Set set = client.keys(pattern);
225 | result.addAll(set);
226 | } finally {
227 | jedisPool.returnResourceObject(client);// 向连接池“归还”资源
228 | }
229 | return result;
230 | }
231 |
232 | public boolean delKey(String key) {
233 | Jedis client = jedisPool.getResource();
234 | try {
235 | System.out.println("del key=" + key);
236 | client.del(key);
237 | return true;
238 | } finally {
239 | jedisPool.returnResourceObject(client);// 向连接池“归还”资源
240 | }
241 | }
242 |
243 | public boolean hset(String key, String field, T t) {
244 | Jedis client = jedisPool.getResource();
245 | try {
246 | String afterSerialize = Util.beanToJson(t);
247 | client.hset(key, field, afterSerialize);
248 | return true;
249 | } finally {
250 | jedisPool.returnResourceObject(client);// 向连接池“归还”资源
251 | }
252 |
253 | }
254 |
255 | /**
256 | * 存入的时hash结构的数据
257 | *
258 | * @param key
259 | * key
260 | * @param map
261 | * map的key实质为field。
262 | * @return
263 | */
264 | public boolean hmset(String key, Map map) {
265 | Jedis client = jedisPool.getResource();
266 | try {
267 | Iterator> iterator = map.entrySet().iterator();
268 | Map stringMap = new HashMap();
269 | String filed;
270 | String value;
271 | while (iterator.hasNext()) {
272 | Entry entry = iterator.next();
273 | filed = String.valueOf(entry.getKey());
274 | value = Util.beanToJson(entry.getValue());
275 | stringMap.put(filed, value);
276 | }
277 | client.hmset(key, stringMap);
278 | return true;
279 | } finally {
280 | jedisPool.returnResourceObject(client);// 向连接池“归还”资源
281 | }
282 |
283 | }
284 |
285 | public T hgetObject(String key, String field, Class cls) {
286 | Jedis client = jedisPool.getResource();
287 | try {
288 | String value = client.hget(key, field);
289 | return (T) Util.jsonToBean(value, cls);
290 | } finally {
291 | jedisPool.returnResourceObject(client);// 向连接池“归还”资源
292 | }
293 |
294 | }
295 |
296 | public String hgetString(String key, String field) {
297 | Jedis client = jedisPool.getResource();
298 | try {
299 | String value = client.hget(key, field);
300 | return value;
301 | } finally {
302 | jedisPool.returnResourceObject(client);// 向连接池“归还”资源
303 | }
304 |
305 | }
306 |
307 | public Map hGetAll(String key) {
308 | Jedis client = jedisPool.getResource();
309 | try {
310 | return client.hgetAll(key);
311 | } finally {
312 | jedisPool.returnResourceObject(client);// 向连接池“归还”资源
313 | }
314 |
315 | }
316 |
317 | public List hkeys(String key) {
318 | Jedis client = jedisPool.getResource();
319 | try {
320 | List fields = new ArrayList();
321 | Set set = client.hkeys(key);
322 | fields.addAll(set);
323 | return fields;
324 | } finally {
325 | jedisPool.returnResourceObject(client);// 向连接池“归还”资源
326 | }
327 |
328 | }
329 |
330 | public List hvals(String key) {
331 | Jedis client = jedisPool.getResource();
332 | try {
333 | List values = client.hvals(key);
334 | return values;
335 | } finally {
336 | jedisPool.returnResourceObject(client);// 向连接池“归还”资源
337 | }
338 |
339 | }
340 |
341 | public boolean hexists(String key, String field) {
342 | Jedis client = jedisPool.getResource();
343 | try {
344 | return client.hexists(key, field);
345 | } finally {
346 | jedisPool.returnResourceObject(client);// 向连接池“归还”资源
347 | }
348 | }
349 |
350 | public long incr(String key) {
351 | Jedis client = jedisPool.getResource();
352 | try {
353 | return client.incr(key);
354 | } finally {
355 | jedisPool.returnResourceObject(client);
356 | }
357 | }
358 |
359 | public void hdel(String key, String... fields) {
360 | Jedis client = jedisPool.getResource();
361 | try {
362 | client.hdel(key, fields);
363 | } finally {
364 | jedisPool.returnResourceObject(client);
365 | }
366 | }
367 |
368 | /**
369 | *
370 | *
371 | * @param key
372 | * @param field
373 | */
374 | public void lpush(String key, String field) {
375 | Jedis client = jedisPool.getResource();
376 | try {
377 | client.lpush(key, field);
378 | } finally {
379 | jedisPool.returnResourceObject(client);
380 | }
381 | }
382 |
383 | public void lpush(String key, Object obj) {
384 | Jedis client = jedisPool.getResource();
385 | try {
386 | String field = Util.beanToJson(obj);
387 | client.lpush(key, field);
388 | } finally {
389 | jedisPool.returnResourceObject(client);
390 | }
391 | }
392 |
393 | /**
394 | * 该方法不适用于普通的调用,该方法只针对于错误日志记录
395 | *
396 | * @param key
397 | * @param field
398 | */
399 | public void lpushForErrorMsg(String key, String field) {
400 | Jedis client = jedisPool.getResource();
401 | try {
402 | if (client.llen(key) > 1000) {
403 | return;
404 | }
405 | client.lpush(key, field);
406 | } finally {
407 | jedisPool.returnResourceObject(client);
408 | }
409 | }
410 |
411 | public long llen(String key){
412 | Jedis client = jedisPool.getResource();
413 | try {
414 | return client.llen(key);
415 | } finally{
416 | jedisPool.returnResourceObject(client);
417 | }
418 | }
419 |
420 | public List blPop(String key, int timeoutSeconds) {
421 | Jedis client = jedisPool.getResource();
422 | try {
423 | return client.blpop(timeoutSeconds, key);
424 | } finally {
425 | jedisPool.returnResourceObject(client);
426 | }
427 | }
428 |
429 | public long sadd(String key, String... values) {
430 | Jedis client = jedisPool.getResource();
431 | try {
432 | return client.sadd(key, values);
433 | } finally {
434 | jedisPool.returnResourceObject(client);
435 | }
436 | }
437 |
438 | public long sadd(String key, List ts) {
439 | Jedis client = jedisPool.getResource();
440 | try {
441 | if (ts == null || ts.size() ==0) {
442 | return 0l;
443 | }
444 | String[] values = new String[ts.size()];
445 | for (int i = 0; i < ts.size(); i++) {
446 | values[i] = ts.get(i).toString();
447 | }
448 | return client.sadd(key, values);
449 | } finally {
450 | jedisPool.returnResourceObject(client);
451 | }
452 | }
453 |
454 | public long srem(String key, String... values) {
455 | Jedis client = jedisPool.getResource();
456 | try {
457 | return client.srem(key, values);
458 | } finally {
459 | jedisPool.returnResourceObject(client);
460 | }
461 | }
462 |
463 | public long srem(String key, List ts) {
464 | Jedis client = jedisPool.getResource();
465 | try {
466 | if (ts == null || ts.size() ==0) {
467 | return 0l;
468 | }
469 | String[] values = new String[ts.size()];
470 | for (int i = 0; i < ts.size(); i++) {
471 | values[i] = ts.get(i).toString();
472 | }
473 | return client.srem(key, values);
474 | } finally {
475 | jedisPool.returnResourceObject(client);
476 | }
477 | }
478 |
479 | public Set getByRange(String key, double min, double max) {
480 | Jedis client = jedisPool.getResource();
481 | try {
482 | return client.zrangeByScore(key, min, max);
483 | } finally {
484 | jedisPool.returnResourceObject(client);
485 | }
486 | }
487 |
488 | public Long decr(String key) {
489 | Jedis client = jedisPool.getResource();
490 | try {
491 | return client.decr(key);
492 | } finally {
493 | jedisPool.returnResourceObject(client);
494 | }
495 | }
496 |
497 | public Long hlen(String key) {
498 | Jedis client = jedisPool.getResource();
499 | try {
500 | return client.hlen(key);
501 | } finally {
502 | jedisPool.returnResourceObject(client);
503 | }
504 | }
505 |
506 | public List hmget(String key, String... fields) {
507 | Jedis client = jedisPool.getResource();
508 | try {
509 | return client.hmget(key, fields);
510 | } finally {
511 | jedisPool.returnResourceObject(client);
512 | }
513 | }
514 |
515 | /**
516 | * 从redis里面得到以 某字符串开头的所有key
517 | *
518 | * @param str
519 | * */
520 | public Set getKeyByStr(String str) {
521 | Jedis client = jedisPool.getResource();
522 |
523 | Set keys = null;
524 | try {
525 | keys = client.keys(str);
526 | } finally {
527 | jedisPool.returnResourceObject(client);
528 | }
529 | return keys;
530 | }
531 |
532 | public void ltrim(String key, int start, int stop){
533 | Jedis client = jedisPool.getResource();
534 | try {
535 | client.ltrim(key, start, stop);
536 | } finally{
537 | jedisPool.returnResourceObject(client);
538 | }
539 | }
540 | /**
541 | *
542 | * @param key
543 | * @param seconds
544 | * @return
545 | */
546 | public Long expire(String key,Integer seconds){
547 | Jedis client = jedisPool.getResource();
548 | Long success = 1l;
549 | try{
550 | success = client.expire(key, seconds);
551 | }finally{
552 | jedisPool.returnResourceObject(client);;
553 | }
554 | return success;
555 | }
556 |
557 | /**
558 | * 存入的时hash结构的数据,并且去掉value中的引号
559 | *
560 | * @param key
561 | * key
562 | * @param map
563 | * map的key实质为field。
564 | * @return
565 | */
566 | public boolean hmsetWithoutQuotationMarks(String key, Map map) {
567 | Jedis client = jedisPool.getResource();
568 | try {
569 | Iterator> iterator = map.entrySet().iterator();
570 | Map stringMap = new HashMap();
571 | String filed;
572 | String value;
573 | while (iterator.hasNext()) {
574 | Entry entry = iterator.next();
575 | filed = String.valueOf(entry.getKey());
576 | value = JSON.toJSONString(entry.getValue()).replace("\"", "");
577 | stringMap.put(filed, value);
578 | }
579 | client.hmset(key, stringMap);
580 | return true;
581 | } finally {
582 | jedisPool.returnResourceObject(client);// 向连接池“归还”资源
583 | }
584 | }
585 |
586 | }
587 |
--------------------------------------------------------------------------------
/redislockframework/src/main/java/com/liushao/redislockframework/RedisFactory.java:
--------------------------------------------------------------------------------
1 | package com.liushao.redislockframework;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.util.Properties;
6 |
7 | import redis.clients.jedis.JedisPool;
8 | import redis.clients.jedis.JedisPoolConfig;
9 |
10 | public class RedisFactory {
11 |
12 | public static JedisPoolConfig getPoolConfig() throws IOException{
13 | Properties properties = new Properties();
14 |
15 | InputStream in = RedisFactory.class.getClassLoader().getResourceAsStream("redis.properties");
16 |
17 | try {
18 | properties.load(in);
19 | JedisPoolConfig config = new JedisPoolConfig();
20 | config.setMaxIdle(Integer.parseInt(properties.getProperty("maxIdle", "100")));
21 | config.setMinIdle(Integer.parseInt(properties.getProperty("minIdle", "1")));
22 | config.setMaxTotal(Integer.parseInt(properties.getProperty("maxTotal", "1000")));
23 | return config;
24 | } finally {
25 | in.close();
26 | }
27 |
28 | }
29 |
30 | public static RedisClient getDefaultClient(){
31 | JedisPool pool = new JedisPool("127.0.0.1");
32 | RedisClient client = new RedisClient(pool);
33 | return client;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/redislockframework/src/main/java/com/liushao/redislockframework/RedisLock.java:
--------------------------------------------------------------------------------
1 | package com.liushao.redislockframework;
2 |
3 | import java.util.Random;
4 |
5 |
6 |
7 | public class RedisLock {
8 |
9 | //纳秒和毫秒之间的转换率
10 | public static final long MILLI_NANO_TIME = 1000 * 1000L;
11 |
12 | public static final String LOCKED = "TRUE";
13 |
14 | public static final Random RANDOM = new Random();
15 | private String key;
16 | //封装的操作redis的工具
17 | private RedisClient redisClient;
18 |
19 | private boolean lock = true;
20 |
21 |
22 |
23 | /**
24 | *
25 | * @param purpose 锁前缀
26 | * @param key 锁定的ID等东西
27 | * @param client
28 | */
29 | public RedisLock(String purpose, String key){
30 | this.key = purpose + "_" + key + "_lock";
31 | this.redisClient = RedisFactory.getDefaultClient();
32 | }
33 |
34 | public RedisLock(String purpose, String key,RedisClient client){
35 | this.key = purpose + "_" + key + "_lock";
36 | this.redisClient = RedisFactory.getDefaultClient();
37 | this.redisClient = client;
38 | }
39 |
40 | /**
41 | * 加锁
42 | * 使用方式为:
43 | * lock();
44 | * try{
45 | * executeMethod();
46 | * }finally{
47 | * unlock();
48 | * }
49 | * @param timeout timeout的时间范围内轮询锁
50 | * @param expire 设置锁超时时间
51 | * @return 成功 or 失败
52 | */
53 | public boolean lock(long timeout,int expire){
54 | long nanoTime = System.nanoTime();
55 | timeout *= MILLI_NANO_TIME;
56 | try {
57 | //在timeout的时间范围内不断轮询锁
58 | while (System.nanoTime() - nanoTime < timeout) {
59 | //锁不存在的话,设置锁并设置锁过期时间,即加锁
60 | if (this.redisClient.setnx(this.key, LOCKED) == 1) {
61 | this.redisClient.expire(key, expire);//设置锁过期时间是为了在没有释放
62 | //锁的情况下锁过期后消失,不会造成永久阻塞
63 | this.lock = true;
64 | return this.lock;
65 | }
66 | System.out.println("出现锁等待");
67 | //短暂休眠,避免可能的活锁
68 | Thread.sleep(3, RANDOM.nextInt(30));
69 | }
70 | } catch (Exception e) {
71 | throw new RuntimeException("locking error",e);
72 | }
73 | return false;
74 | }
75 |
76 | public void unlock() {
77 | try {
78 | if(this.lock){
79 | redisClient.delKey(key);//直接删除
80 | }
81 | } catch (Throwable e) {
82 |
83 | }
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/redislockframework/src/main/java/com/liushao/redislockframework/Util.java:
--------------------------------------------------------------------------------
1 | package com.liushao.redislockframework;
2 |
3 | import com.alibaba.fastjson.JSON;
4 |
5 | public class Util {
6 | public static String beanToJson(Object o){
7 | return JSON.toJSONString(o);
8 | }
9 | //parse an object from
10 | public static T jsonToBean(String json,Class cls){
11 | return JSON.parseObject(json, cls);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/redislockframework/src/test/java/com/liushao/redislockframework/SecKillImpl.java:
--------------------------------------------------------------------------------
1 | package com.liushao.redislockframework;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | public class SecKillImpl implements SeckillInterface{
7 | static Map inventory ;
8 | static{
9 | inventory = new HashMap<>();
10 | inventory.put(10000001L, 10000l);
11 | inventory.put(10000002L, 10000l);
12 | }
13 |
14 | @Override
15 | public void secKill(String arg1, Long arg2) {
16 | //最简单的秒杀,这里仅作为demo示例
17 | reduceInventory(arg2);
18 | }
19 | //模拟秒杀操作,姑且认为一个秒杀就是将库存减一,实际情景要复杂的多
20 | public Long reduceInventory(Long commodityId){
21 | inventory.put(commodityId,inventory.get(commodityId) - 1);
22 | return inventory.get(commodityId);
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/redislockframework/src/test/java/com/liushao/redislockframework/SecKillTest.java:
--------------------------------------------------------------------------------
1 | package com.liushao.redislockframework;
2 |
3 |
4 | import java.io.IOException;
5 | import java.lang.reflect.Proxy;
6 | import java.util.concurrent.CountDownLatch;
7 |
8 | import org.junit.Before;
9 | import org.junit.Test;
10 |
11 | import redis.clients.jedis.JedisPool;
12 |
13 | public class SecKillTest {
14 | private static Long commidityId1 = 10000001L;
15 | private static Long commidityId2 = 10000002L;
16 | private
17 | RedisClient client;
18 | public static String HOST = "127.0.0.1";
19 | private JedisPool jedisPool;
20 | @Before
21 | public synchronized void beforeTest() throws IOException{
22 |
23 |
24 | jedisPool = new JedisPool("127.0.0.1");
25 |
26 | }
27 |
28 | @Test
29 | public void testSecKill(){
30 | int threadCount = 1000;
31 | int splitPoint = 500;
32 | CountDownLatch endCount = new CountDownLatch(threadCount);
33 | CountDownLatch beginCount = new CountDownLatch(1);
34 | SecKillImpl testClass = new SecKillImpl();
35 |
36 | Thread[] threads = new Thread[threadCount];
37 | //起500个线程,秒杀第一个商品
38 | for(int i= 0;i < splitPoint;i++){
39 | threads[i] = new Thread(new Runnable() {
40 | public void run() {
41 | try {
42 | //等待在一个信号量上,挂起
43 | beginCount.await();
44 | //用动态代理的方式调用secKill方法
45 | SeckillInterface proxy = (SeckillInterface) Proxy.newProxyInstance(SeckillInterface.class.getClassLoader(),
46 | new Class[]{SeckillInterface.class}, new CacheLockInterceptor(testClass));
47 | proxy.secKill("test", commidityId1);
48 | endCount.countDown();
49 | } catch (InterruptedException e) {
50 | // TODO Auto-generated catch block
51 | e.printStackTrace();
52 | }
53 | }
54 | });
55 | threads[i].start();
56 |
57 | }
58 |
59 | for(int i= splitPoint;i < threadCount;i++){
60 | threads[i] = new Thread(new Runnable() {
61 | public void run() {
62 | try {
63 | //等待在一个信号量上,挂起
64 | beginCount.await();
65 | //用动态代理的方式调用secKill方法
66 | beginCount.await();
67 | SeckillInterface proxy = (SeckillInterface) Proxy.newProxyInstance(SeckillInterface.class.getClassLoader(),
68 | new Class[]{SeckillInterface.class}, new CacheLockInterceptor(testClass));
69 | proxy.secKill("test", commidityId2);
70 | //testClass.testFunc("test", 10000001L);
71 | endCount.countDown();
72 | } catch (InterruptedException e) {
73 | // TODO Auto-generated catch block
74 | e.printStackTrace();
75 | }
76 | }
77 | });
78 | threads[i].start();
79 |
80 | }
81 |
82 |
83 | long startTime = System.currentTimeMillis();
84 | //主线程释放开始信号量,并等待结束信号量
85 | beginCount.countDown();
86 |
87 | try {
88 | //主线程等待结束信号量
89 | endCount.await();
90 | //观察秒杀结果是否正确
91 | System.out.println(SecKillImpl.inventory.get(commidityId1));
92 | System.out.println(SecKillImpl.inventory.get(commidityId2));
93 | System.out.println("error count" + CacheLockInterceptor.ERROR_COUNT);
94 | System.out.println("total cost " + (System.currentTimeMillis() - startTime));
95 | } catch (InterruptedException e) {
96 | // TODO Auto-generated catch block
97 | e.printStackTrace();
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/redislockframework/src/test/java/com/liushao/redislockframework/SeckillInterface.java:
--------------------------------------------------------------------------------
1 | package com.liushao.redislockframework;
2 |
3 |
4 | public interface SeckillInterface {
5 | @CacheLock(lockedPrefix="TEST_PREFIX")
6 | public void secKill(String arg1,@LockedObject Long arg2);
7 | }
8 |
--------------------------------------------------------------------------------