nx set 怎么实现的原子性_基于Redis的SETNX实现分布式锁

原理

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值