├── 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 | --------------------------------------------------------------------------------