天天看點

rediSsion 分布式鎖使用rediSsion 分布式鎖使用

rediSsion 分布式鎖使用

1場景設定

假定兩個不同服務去請求消耗剩餘商品庫存數(100)

商品庫存為100,數目模拟存在redis中。key值number,value100

2存值

存入方式選StringRedisTemplate存儲或者jedis存儲。

1 StringRedisTemplate存儲

引入相關pom檔案

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.3.4.RELEASE</version>
        </dependency>
         <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>${redisson.version}</version>
        </dependency>
           

配置yml

spring:
  application:
    name: User
  redis:
    database: 7
    host: *******
    jedis:
      pool:
        max-active: 100
        max-idle: 20
        max-wait: -1ms
    password: *******
    port: 6379
    timeout: 20
    redisjedis:
      pool:
        min-idle: 0
           

@Autowired注入使用

@Autowired
    private StringRedisTemplate stringRedisTemplate;//自帶的字元串模闆類,用于存儲字元串
           
stringRedisTemplate.opsForValue().set("number","100");
           

2 jedis存儲(使用jedispool)

引入相關pom檔案

<dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>${jedis.version}</version>
        </dependency>

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>${redisson.version}</version>
        </dependency>
           

配置yml(同上)

添加配置類

/**
 * zbeing
 */
@Configuration
public class RedisUtil {
    private static  Integer maxToal;
    private static  String host;
    private static Integer prot;
    private static Integer timeout;
    private static String password;
    @Value("${spring.redis.jedis.pool.max-active}")
    public  void setMaxToal(Integer maxToal) {
        RedisUtil.maxToal = maxToal;
    }
    @Value("${spring.redis.host}")
    public  void setHost(String host) {
        RedisUtil.host = host;
    }
    @Value("${spring.redis.port}")
    public  void setProt(Integer prot) {
        RedisUtil.prot = prot;
    }
    @Value("${spring.redis.timeout}")
    public  void setTimeout(Integer timeout) {
        RedisUtil.timeout = timeout;
    }
    @Value("${spring.redis.password}")
    public  void setPassword(String password) {
        RedisUtil.password = password;
    }
    public static void saveRedis(String key, String value) {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(maxToal);
        JedisPool pool = new JedisPool(jedisPoolConfig, host, prot,timeout,password,7);
        Jedis jedis = null;
        try {
            jedis = pool.getResource();
            jedis.set(key, value);
            System.out.println("已存入的redis值" + jedis.get(key));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //還回pool中
            if (jedis != null) {
                jedis.close();
            }
        }
        pool.close();
    }

}
           

注:因為JedisPoolpool的util是工具類(靜态),是以交由spring管理時,靜态屬性指派要采用上述set屬性時進行指派

rediSsion 分布式鎖使用rediSsion 分布式鎖使用

使用

RedisUtil.saveRedis("number","100");
           

3 redisson

1redisson 配置類

/**
 * zbeing
 */
@Configuration
public class RedissonConfig {

    @Value("${spring.redis.database}")
    private int database;
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private String port;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.timeout}")
    private int timeout;

    /**
     * RedissonClient,單機模式
     *
     * @return
     * @throws IOException
     */
    @Bean(destroyMethod = "shutdown")
    public RedissonClient redisson() {
        Config config = new Config();
        SingleServerConfig singleServerConfig = config.useSingleServer();
        singleServerConfig.setAddress("redis://" + host + ":" + port);
        singleServerConfig.setTimeout(timeout);
        singleServerConfig.setDatabase(database);
        System.out.println("擷取"+JSON.toJSONString(singleServerConfig));
        if (password != null && !"".equals(password)) { //有密碼
            singleServerConfig.setPassword(password);
        }
        return Redisson.create(config);
    }
    }
           

2redisson使用

static final String KEY = "LOCK_KEY";
@Autowired
    private RedissonClient redissonClient;
    @GetMapping("/test")
    public Object test(){
        for (int i = 0; i <100 ; i++) {
            new Thread(()->{
                redissonClient.getLock(KEY).lock();
                //LockUtil.lock(KEY);
                try {
                    String number = stringRedisTemplate.opsForValue().get("number");
                    int num = Integer.parseInt(number);
                    System.out.println("擷取的數目"+num);
                    Thread.sleep(1000);
                    if(num>0&&num<=100){
                        System.out.println(" 處理業務。。。");
                        stringRedisTemplate.opsForValue().set("number",String.valueOf(num- 1));
                    }
                } catch (Exception e) {
                    //異常處理
                }finally{
                    //釋放鎖
                  //  LockUtil.unlock(KEY);
                    redissonClient.getLock(KEY).unlock();
                }
            }
            ).start();
        }
        //加鎖
        return "SUCCESS";
    }
           

3示範

存入redis值

rediSsion 分布式鎖使用rediSsion 分布式鎖使用

進行不同服務調用

rediSsion 分布式鎖使用rediSsion 分布式鎖使用
rediSsion 分布式鎖使用rediSsion 分布式鎖使用

得到結果

rediSsion 分布式鎖使用rediSsion 分布式鎖使用
rediSsion 分布式鎖使用rediSsion 分布式鎖使用
rediSsion 分布式鎖使用rediSsion 分布式鎖使用

4 redisson優化

package com.demo.p.util;

import java.util.concurrent.TimeUnit;

public interface Locker {
    /**
     * 擷取鎖,如果鎖不可用,則目前線程處于休眠狀态,直到獲得鎖為止。
     *
     * @param lockKey
     */
    void lock(String lockKey);

    /**
     * 釋放鎖
     *
     * @param lockKey
     */
    void unlock(String lockKey);

    /**
     * 擷取鎖,如果鎖不可用,則目前線程處于休眠狀态,直到獲得鎖為止。如果擷取到鎖後,執行結束後解鎖或達到逾時時間後會自動釋放鎖
     *
     * @param lockKey
     * @param timeout
     */
    void lock(String lockKey, int timeout);

    /**
     * 擷取鎖,如果鎖不可用,則目前線程處于休眠狀态,直到獲得鎖為止。如果擷取到鎖後,執行結束後解鎖或達到逾時時間後會自動釋放鎖
     *
     * @param lockKey
     * @param unit
     * @param timeout
     */
    void lock(String lockKey, TimeUnit unit, int timeout);

    /**
     * 嘗試擷取鎖,擷取到立即傳回true,未擷取到立即傳回false
     *
     * @param lockKey
     * @return
     */
    boolean tryLock(String lockKey);

    /**
     * 嘗試擷取鎖,在等待時間内擷取到鎖則傳回true,否則傳回false,如果擷取到鎖,則要麼執行完後程式釋放鎖,
     * 要麼在給定的逾時時間leaseTime後釋放鎖
     *
     * @param lockKey
     * @param waitTime
     * @param leaseTime
     * @param unit
     * @return
     */
    boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit)
            throws InterruptedException;

    /**
     * 鎖是否被任意一個線程鎖持有
     *
     * @param lockKey
     * @return
     */
    boolean isLocked(String lockKey);

}

           
package com.demo.p.util;

import java.util.concurrent.TimeUnit;

public class LockUtil {
    private static Locker locker;

    /**
     * 設定工具類使用的locker
     *
     * @param locker
     */
    public static void setLocker(Locker locker) {
        LockUtil.locker = locker;
    }

    /**
     * 擷取鎖
     *
     * @param lockKey
     */
    public static void lock(String lockKey) {
        locker.lock(lockKey);
    }

    /**
     * 釋放鎖
     *
     * @param lockKey
     */
    public static void unlock(String lockKey) {
        locker.unlock(lockKey);
    }

    /**
     * 擷取鎖,逾時釋放
     *
     * @param lockKey
     * @param timeout
     */
    public static void lock(String lockKey, int timeout) {
        locker.lock(lockKey, timeout);
    }

    /**
     * 擷取鎖,逾時釋放,指定時間機關
     *
     * @param lockKey
     * @param unit
     * @param timeout
     */
    public static void lock(String lockKey, TimeUnit unit, int timeout) {
        locker.lock(lockKey, unit, timeout);
    }

    /**
     * 嘗試擷取鎖,擷取到立即傳回true,擷取失敗立即傳回false
     *
     * @param lockKey
     * @return
     */
    public static boolean tryLock(String lockKey) {
        return locker.tryLock(lockKey);
    }

    /**
     * 嘗試擷取鎖,在給定的waitTime時間内嘗試,擷取到傳回true,擷取失敗傳回false,擷取到後再給定的leaseTime時間逾時釋放
     *
     * @param lockKey
     * @param waitTime
     * @param leaseTime
     * @param unit
     * @return
     * @throws InterruptedException
     */
    public static boolean tryLock(String lockKey, long waitTime, long leaseTime,
                                  TimeUnit unit) throws InterruptedException {
        return locker.tryLock(lockKey, waitTime, leaseTime, unit);
    }

    /**
     * 鎖釋放被任意一個線程持有
     *
     * @param lockKey
     * @return
     */
    public static boolean isLocked(String lockKey) {
        return locker.isLocked(lockKey);
    }

}

           
@GetMapping("/test")
    public Object test(){
        for (int i = 0; i <100 ; i++) {
            new Thread(()->{
                //redissonClient.getLock(KEY).lock();
                LockUtil.lock(KEY);
                try {
                    String number = stringRedisTemplate.opsForValue().get("number");
                    int num = Integer.parseInt(number);
                    System.out.println("擷取的數目"+num);
                    Thread.sleep(1000);
                    if(num>0&&num<=100){
                        System.out.println(" 處理業務。。。");
                        stringRedisTemplate.opsForValue().set("number",String.valueOf(num- 1));
                    }
                } catch (Exception e) {
                    //異常處理
                }finally{
                    //釋放鎖
                    LockUtil.unlock(KEY);
                   // redissonClient.getLock(KEY).unlock();
                }
            }
            ).start();
        }
        //加鎖
        return "SUCCESS";
    }