利用redis和redisson实现分布式锁

2,415 阅读2分钟

1 使用redis实现简单的锁

 public R submit(@RequestBody List<WjAnswer> wjAnswers) {
        String lockKey = "lockKey";
        String clientId = UUID.randomUUID().toString();
        try {
            Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 30, TimeUnit.SECONDS);
            /*
          	判断是否新增了key 如果没有新增 则表明有线程在占用这个接口,接口暂时不可用,直接返回错误;如果有新增,则表明没有线程占用接口,可继续执行。
            */
            if (!result) {
                return R.failed().setMsg("请勿频繁刷新");
            }
            System.out.println("这里执行业务代码");
        } finally {
            if (clientId.equals(redisTemplate.opsForValue().get(lockKey))) {
                redisTemplate.delete(lockKey);
            }
        }
    }

1.1 注意事项

  • 利用redis单线程原理实现
  • setIfAbsent: 如果键不存在则新增,存在则不改变已经有的值。
  • 设置redis key有效时间30秒,可以是任意时间。简单业务场景可以这样设置。
  • 存在的问题:高并发下如果业务代码执行时间超过有redis key的效时间,就会造成锁失效
  • 举例:Thread1 运行需要40秒,则lockKey在运行到30秒时已经失效,如果这时Thread2进行请求,则redis中已经没有lockKey,就会新建一把锁,继续执行,造成锁失效。
  • 解决办法:
  1. 新开一个线程 判断lockKey是否存在,如果存在就给lockKey“续命”,重新设置其有效时间,注意:新设的有效时间不能超过原有的时间。
  2. 2.用redisson解决

2 redisson分布式锁实现

2.1 redisson官网: redisson.org/

2.2 redisson初始化客户端

2.2.1 引入依赖
   		<!--redisson-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.5.6</version>
        </dependency>
2.2.2 redisson配置

参考文章地址:

  1. 官方配置文档: github.com/redisson/re…
  2. springBoot集成redisson: blog.csdn.net/lms1719/art…
  • 在项目的resources目录下,添加redisson的配置文件(这里使用yaml格式的配置文件redisson-config.yml,文件名可自己定, 文件的示例配置如下)。
#Redisson配置
singleServerConfig:
  address: "redis://127.0.0.1:6379"
  password: null
  clientName: null
  database: 7 #选择使用哪个数据库0~15
  idleConnectionTimeout: 10000
  pingTimeout: 1000
  connectTimeout: 10000
  timeout: 3000
  retryAttempts: 3
  retryInterval: 1500
  reconnectionTimeout: 3000
  failedAttempts: 3
  subscriptionsPerConnection: 5
  subscriptionConnectionMinimumIdleSize: 1
  subscriptionConnectionPoolSize: 50
  connectionMinimumIdleSize: 32
  connectionPoolSize: 64
  dnsMonitoringInterval: 5000
  #dnsMonitoring: false

threads: 0
nettyThreads: 0
codec:
  class: "org.redisson.codec.JsonJacksonCodec"
transportMode: "NIO"
  • 添加Redisson的配置参数读取类RedissonConfig
package com.dbfor.redis.config;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

@Configuration
public class RedissonConfig {

    @Bean
    public Redisson redisson() throws IOException {
        // 本例子使用的是yaml格式的配置文件,读取使用Config.fromYAML,如果是Json文件,则使用Config.fromJSON
        Config config = Config.fromYAML(RedissonConfig.class.getClassLoader().getResource("redisson-config.yml"));
        return (Redisson)Redisson.create(config);
    }
}

  • 编写测试类
  package com.dbfor.redis;
  
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.redisson.api.RBucket;
  import org.redisson.api.RedissonClient;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.boot.test.context.SpringBootTest;
  import org.springframework.stereotype.Component;
  import org.springframework.test.context.junit4.SpringRunner;
  
  @SpringBootTest
  @RunWith(SpringRunner.class)
  @Component
  public class RedissonTest {
  
      @Autowired
      private RedissonClient redissonClient;
  
      @Test
      public void set() {
          // 设置字符串
          RBucket<String> keyObj = redissonClient.getBucket("k1");
          keyObj.set("v1236");
      }
  }
  测试成功
2.2.4 用Redisson编写分布式锁
@Autowired
private Redisson redisson;

public R submit(@RequestBody List<WjAnswer> wjAnswers) {
          String lockKey = "lockKey";
     	  RLock rLock = redisson.getLock(lockKey);//redisson锁
          try {
  			  rLock.lock();//加锁
              System.out.println("这里执行业务代码");
          } finally {
              rLock.unlock();//释放锁
          }
      }