当前位置: 首页 > 工具软件 > Angel Redis > 使用案例 >

使用Redis做分布式锁

骆照
2023-12-01

一、使用Jedis操作Redis做分布式锁

SpringBoot封装Jedis工具类

工具类代码

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import java.util.Collections;

@Component
@Slf4j
public class JedisClient {

    @Autowired
    JedisPool jedisPool;

    /**
     * 加锁
     *
     * @param lock      锁的key
     * @param requestId 对应客户端的标志
     * @param timeout   锁的过期时间
     * @return true-加锁成功,false-加锁失败
     */
    public boolean lock(String lock, String requestId, Long timeout) {
        try (Jedis jedis = jedisPool.getResource()) {
            SetParams setParams = SetParams.setParams().nx().px(timeout);
            String result = jedis.set(lock, requestId, setParams);
            return "OK".equalsIgnoreCase(result);
        }
    }


    /**
     * 解锁
     *
     * @param lock      锁的key
     * @param requestId 对应客户端的标志
     * @return true-解锁成功,false-解锁失败
     */
    public boolean unlock(String lock, String requestId) {
        try (Jedis jedis = jedisPool.getResource()) {
            // 这段Lua代码的功能是 首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)
            // 在eval命令执行Lua代码的时候,Lua代码将被当成一个命令去执行,保证原子性,并且直到eval命令执行完成,Redis才会执行其他命令。
            String luaScript = "if redis.call('get',KEYS[1]) == ARGV[1] then" +
                    "        return redis.call('del',KEYS[1])" +
                    "    else" +
                    "        return 0" +
                    "    end";
            Object result = jedis.eval(luaScript, Collections.singletonList(lock), Collections.singletonList(requestId));
            return result.equals(1L);
        }
    }

二、使用StringRedisTemplate操作Redis做分布式锁

StringRedisTemplate的使用

加锁

加锁时StringRedisTemplate有对应的方法,可直接为不存在的键设置超时时间,成功返回 true, 失败返回 false

stringRedisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS);

	/**
     * 加锁
     *
     * @param lock      锁的key
     * @param requestId 对应客户端的标志
     * @param timeout   锁的过期时间
     * @return true-加锁成功,false-加锁失败
     */
    public boolean lock(String lock, String requestId, Long timeout) {
		return stringRedisTemplate.opsForValue().setIfAbsent(lock, requestId, timeout, TimeUnit.SECONDS);
    }

解锁

和Jedis相同,一样是执行Lua脚本

	/**
     * 解锁
     *
     * @param lock      锁的key
     * @param requestId 对应客户端的标志
     * @return true-解锁成功,false-解锁失败
     */
    public boolean unlock(String lock, String requestId) {
        String luaScript = "if redis.call('get',KEYS[1]) == ARGV[1] then" +
                    "        return redis.call('del',KEYS[1])" +
                    "    else" +
                    "        return 0" +
                    "    end";
        RedisScript<Long> script = new DefaultRedisScript<>(luaScript, Long.class);
        Long result = stringRedisTemplate.execute(script, Collections.singletonList(lock), requestId);
        Long success = 1L;
        return success.equals(result);
    }

三、Redisson框架

1. 引入依赖

<!--redisson-->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.14.1</version>
</dependency>

2. 配置文件配置redis地址等

spring:
  # redis
  redis:
    # Redis数据库索引(默认为0)
    database: 0
    # Redis服务器地址
    host: localhost
    # Redis服务器连接端口
    port: 6379
    # Redis服务器连接密码(默认为空)
    password: 123456

3. 配置类配置redisson

import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {

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

    @Bean
    public Redisson redisson() {
        Config config = new Config();
        // 单机模式
        config.useSingleServer()
                .setDatabase(database)
                .setAddress(host + ":" + port)
                .setPassword(password);
        return (Redisson) Redisson.create(config);
    }
}

4. 使用

  • @Autowired注解把 Redisson 注入进来
  • 获取锁对象,lock_key表示锁的key
    RLock redissonLock = redisson.getLock("lock_key");
  • 加锁,默认锁过期时间为30s
    redissonLock.lock();
  • 解锁
    redissonLock.unlock();
 类似资料: