zoukankan      html  css  js  c++  java
  • 基于Redis的SETNX实现分布式锁

    原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/13200032.html

    原理

    SET key value [NX] [XX] [EX <seconds>] [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

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.8.RELEASE</version>
            <relativePath/>
        </parent>
    
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.fool.redis</groupId>
        <artifactId>redis-lock-setnx</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    View Code

    src/main/resources

    application.properties

    spring.application.name=redis-lock-setnx
    server.port=8888
    
    logging.level.org.fool.redis=debug
    
    spring.redis.host=localhost
    spring.redis.port=6379
    spring.redis.database=0
    spring.redis.password=
    spring.redis.timeout=2000
    spring.redis.pool.max-active=10
    spring.redis.pool.max-wait=1000
    spring.redis.pool.max-idle=10
    spring.redis.pool.min-idle=5
    spring.redis.pool.num-tests-per-eviction-run=1024
    spring.redis.pool.time-between-eviction-runs-millis=30000
    spring.redis.pool.min-evictable-idle-time-millis=60000
    spring.redis.pool.soft-min-evictable-idle-time-millis=10000
    spring.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 package org.fool.redis;
     2 
     3 import org.springframework.boot.SpringApplication;
     4 import org.springframework.boot.autoconfigure.SpringBootApplication;
     5 
     6 @SpringBootApplication
     7 public class Application {
     8     public static void main(String[] args) {
     9         SpringApplication.run(Application.class, args);
    10     }
    11 }

    JsonUtils.java

     1 package org.fool.redis.controller;
     2 
     3 import com.fasterxml.jackson.core.JsonProcessingException;
     4 import com.fasterxml.jackson.databind.JavaType;
     5 import com.fasterxml.jackson.databind.ObjectMapper;
     6 
     7 import java.util.List;
     8 import java.util.Map;
     9 
    10 public class JsonUtils {
    11     private static final ObjectMapper MAPPER = new ObjectMapper();
    12 
    13     public static String objectToJson(Object data) {
    14         try {
    15             String string = MAPPER.writeValueAsString(data);
    16             return string;
    17         } catch (JsonProcessingException e) {
    18             e.printStackTrace();
    19         }
    20         return null;
    21     }
    22 
    23     public static <T> T jsonToPojo(String jsonData, Class<T> beanType) {
    24         try {
    25             T t = MAPPER.readValue(jsonData, beanType);
    26             return t;
    27         } catch (Exception e) {
    28             e.printStackTrace();
    29         }
    30 
    31         return null;
    32     }
    33 
    34     public static <T> List<T> jsonToList(String jsonData, Class<T> beanType) {
    35         JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
    36 
    37         try {
    38             List<T> list = MAPPER.readValue(jsonData, javaType);
    39             return list;
    40         } catch (Exception e) {
    41             e.printStackTrace();
    42         }
    43 
    44         return null;
    45     }
    46 
    47     public static <K, V> Map<K, V> jsonToMap(String jsonData, Class<K> keyType, Class<V> valueType) {
    48         JavaType javaType = MAPPER.getTypeFactory().constructMapType(Map.class, keyType, valueType);
    49 
    50         try {
    51             Map<K, V> map = MAPPER.readValue(jsonData, javaType);
    52             return map;
    53         } catch (Exception e) {
    54             e.printStackTrace();
    55         }
    56 
    57         return null;
    58     }
    59 }
    View Code

    OrderRequest.java

     1 package org.fool.redis.controller;
     2 
     3 import lombok.Data;
     4 
     5 @Data
     6 public class OrderRequest {
     7     private Integer id;
     8     private Integer productId;
     9     private Integer price;
    10     private Integer userId;
    11     private Integer tradeId;
    12     private Integer tradeStatus;
    13 }

    OrderController.java

     1 package org.fool.redis.controller;
     2 
     3 import lombok.extern.slf4j.Slf4j;
     4 import org.springframework.beans.factory.annotation.Autowired;
     5 import org.springframework.data.redis.core.StringRedisTemplate;
     6 import org.springframework.util.DigestUtils;
     7 import org.springframework.web.bind.annotation.PostMapping;
     8 import org.springframework.web.bind.annotation.RequestBody;
     9 import org.springframework.web.bind.annotation.RestController;
    10 
    11 import java.util.concurrent.TimeUnit;
    12 
    13 @RestController
    14 @Slf4j
    15 public class OrderController {
    16 
    17     @Autowired
    18     private StringRedisTemplate stringRedisTemplate;
    19 
    20     @PostMapping(value = "/createOrder", produces = "application/json;charset=utf-8")
    21     public String 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 <seconds>] [PX [millseconds]]
    28          * set expire time 5 mins
    29          */
    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

  • 相关阅读:
    【vim使用】光标的 段落切换、括号切换
    【vim使用】行数跳转 和 屏幕移动
    【vim使用】vim的三种工作模式
    【vim使用】vim的异常处理,删除交换文件
    【vim使用】打开文件,并且定位行
    【vim使用】vim新建文件、打开文件
    【vim使用】vim简介
    【终端使用】SSH免密码登录——02
    【终端使用】SSH免密码登录——01
    【终端使用】"apt"命令 安装/卸载软件
  • 原文地址:https://www.cnblogs.com/agilestyle/p/13200032.html
Copyright © 2011-2022 走看看