原理
SET key value [NX] [XX] [EX ] [PX [millseconds]]
必选参数说明
SET:命令
key:待设置的key
value: 设置的key的value
可选参数说明
NX:表示key不存在才设置,如果存在则返回NULL
XX:表示key存在时才设置,如果不存在则返回NULL
EX seconds:设置过期时间,过期时间精确为秒
PX millseconds:设置过期时间,过期时间精确为毫秒
以上set 代替了 setnx + expire 需要分2次执行命令操作的方式,保证了原子性。
如果setnx 返回ok 说明拿到了锁;如果setnx 返回 nil,说明拿锁失败,被其他线程占用。
Project Directory
Maven Dependency
org.springframework.boot
spring-boot-starter-parent
2.2.8.RELEASE
4.0.0
org.fool.redis
redis-lock-setnx
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-data-redis
org.projectlombok
lombok
1.18.12
org.springframework.boot
spring-boot-maven-plugin
View Code
src/main/resources
application.properties
spring.application.name=redis-lock-setnx
server.port=8888logging.level.org.fool.redis=debugspring.redis.host=localhost
spring.redis.port=6379spring.redis.database=0spring.redis.password=spring.redis.timeout=2000spring.redis.pool.max-active=10spring.redis.pool.max-wait=1000spring.redis.pool.max-idle=10spring.redis.pool.min-idle=5spring.redis.pool.num-tests-per-eviction-run=1024spring.redis.pool.time-between-eviction-runs-millis=30000spring.redis.pool.min-evictable-idle-time-millis=60000spring.redis.pool.soft-min-evictable-idle-time-millis=10000spring.redis.pool.test-on-borrow=true
spring.redis.pool.test-while-idle=true
spring.redis.pool.block-when-exhausted=false
View Code
src/main/java
Application.java
1 packageorg.fool.redis;2
3 importorg.springframework.boot.SpringApplication;4 importorg.springframework.boot.autoconfigure.SpringBootApplication;5
6 @SpringBootApplication7 public classApplication {8 public static voidmain(String[] args) {9 SpringApplication.run(Application.class, args);10 }11 }
JsonUtils.java
1 packageorg.fool.redis.controller;2
3 importcom.fasterxml.jackson.core.JsonProcessingException;4 importcom.fasterxml.jackson.databind.JavaType;5 importcom.fasterxml.jackson.databind.ObjectMapper;6
7 importjava.util.List;8 importjava.util.Map;9
10 public classJsonUtils {11 private static final ObjectMapper MAPPER = newObjectMapper();12
13 public staticString objectToJson(Object data) {14 try{15 String string =MAPPER.writeValueAsString(data);16 returnstring;17 } catch(JsonProcessingException e) {18 e.printStackTrace();19 }20 return null;21 }22
23 public static T jsonToPojo(String jsonData, ClassbeanType) {24 try{25 T t =MAPPER.readValue(jsonData, beanType);26 returnt;27 } catch(Exception e) {28 e.printStackTrace();29 }30
31 return null;32 }33
34 public static List jsonToList(String jsonData, ClassbeanType) {35 JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);36
37 try{38 List list =MAPPER.readValue(jsonData, javaType);39 returnlist;40 } catch(Exception e) {41 e.printStackTrace();42 }43
44 return null;45 }46
47 public static Map jsonToMap(String jsonData, Class keyType, ClassvalueType) {48 JavaType javaType = MAPPER.getTypeFactory().constructMapType(Map.class, keyType, valueType);49
50 try{51 Map map =MAPPER.readValue(jsonData, javaType);52 returnmap;53 } catch(Exception e) {54 e.printStackTrace();55 }56
57 return null;58 }59 }
View Code
OrderRequest.java
1 packageorg.fool.redis.controller;2
3 importlombok.Data;4
5 @Data6 public classOrderRequest {7 privateInteger id;8 privateInteger productId;9 privateInteger price;10 privateInteger userId;11 privateInteger tradeId;12 privateInteger tradeStatus;13 }
OrderController.java
1 packageorg.fool.redis.controller;2
3 importlombok.extern.slf4j.Slf4j;4 importorg.springframework.beans.factory.annotation.Autowired;5 importorg.springframework.data.redis.core.StringRedisTemplate;6 importorg.springframework.util.DigestUtils;7 importorg.springframework.web.bind.annotation.PostMapping;8 importorg.springframework.web.bind.annotation.RequestBody;9 importorg.springframework.web.bind.annotation.RestController;10
11 importjava.util.concurrent.TimeUnit;12
13 @RestController14 @Slf4j15 public classOrderController {16
17 @Autowired18 privateStringRedisTemplate stringRedisTemplate;19
20 @PostMapping(value = "/createOrder", produces = "application/json;charset=utf-8")21 publicString createOrder(@RequestBody OrderRequest request) {22
23 String json =JsonUtils.objectToJson(request);24 String md5 =DigestUtils.md5DigestAsHex(json.getBytes()).toUpperCase();25
26 /*
27 * setIfAbsent <=> SET key value [NX] [XX] [EX ] [PX [millseconds]]28 * set expire time 5 mins29 */
30 Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(md5, "1", 60 * 5, TimeUnit.SECONDS);31 if(flag) {32 //lock success, start to handle business
33 log.debug("{} lock success, start to handle business", md5);34 try{35 //mock to handle business
36 Thread.sleep(1000 * 10);37 } catch(InterruptedException e) {38 e.printStackTrace();39 }40 //end to handle business, need to unlock
41 stringRedisTemplate.delete(md5);42 log.debug("{} unlock success,end to handle business", md5);43 return "SUCCESS";44 } else{45 log.debug("{} lock failure", md5);46 return "try again later";47 }48 }49 }
Test
Console Output