Redis一个简易的分布式锁

一个临时的功能:当用户登录系统时,如果用户不存在,则为该用户初始化一些身份信息。在入库的时候发现存在一些并发行为,可能会初始化两条用户信息,这种问题需要规避,所以我决定用锁。

因为初始化信息的时候,是插入操作,所以行锁是用不了的,我需要用表锁,但是用表锁又会印象到其他接口的查询功能(是这样的吧?我这块学的还不透彻)。所以,我最终决定用Redis实现一个简单的分布式锁。

分布式锁原理比较简单,就是利用了Redis的单线程性,我直接找了一个线程的Jar包,操作步骤如下:

  1. 引入Jar包
1
2
3
4
5
6
7

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.12.5</version>
</dependency>

  1. 进行配置
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

@Configuration
public class RedissonConfig {
    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private Integer port;

    @Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.lettuce.pool.max-idle}")
    private int maxPoolSize;

    private String cluster;

    @Bean
    public RedissonClient redissonClient() {
        return loadRedisson();
    }

    public RedissonClient loadRedisson() {
        RedissonClient redisson;
        Config config = new Config();
        //单节点
        if (!StringUtils.isEmpty(host)) {
            config.useSingleServer()
                    .setAddress("redis://" + host + ":" + port)
                    .setPassword(StringUtils.isEmpty(password) ? null : password)
                    .setConnectionPoolSize(maxPoolSize)
                    .setDnsMonitoringInterval(-1)
                    //最小空闲连接
                    .setConnectionMinimumIdleSize(0);
        } else {
            //集群节点
            String[] nodes = cluster.split(",");
            //redisson版本是3.5,集群的ip前面要加上“redis://”,不然会报错,3.2版本可不加
            for (int i = 0; i < nodes.length; i++) {
                nodes[i] = "redis://" + nodes[i];
            }
            //这是用的集群server
            config.useClusterServers()
                    //设置集群状态扫描时间2000
                    .setScanInterval(2000)
                    .addNodeAddress(nodes)
                    .setPassword(password)
                    .setMasterConnectionPoolSize(maxPoolSize)
                    .setDnsMonitoringInterval(-1)
                    //最小空闲连接
                    .setMasterConnectionMinimumIdleSize(0);
        }
        redisson = Redisson.create(config);
        return redisson;
    }

    public RedissonClient retryGetRedisson() {
        return loadRedisson();
    }
}

  1. 编写代码
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15

RLock lock = redissonClient.getLock(String.format("SRM:inviteFromMaterialPlatform:%s_%s", tenantId, request.getSupplierId()));

try {
    lock.lock();

    // do something

    return inviteInsert.getId();
} finally {
    if (lock.isLocked()) {
        lock.unlock();
    }
}

遇到的问题:

  1. 因为我使用了Proxifier代理java.exe的所有流量,从而让我本地的服务可以直接访问K8S集群中的服务。但是,使用该工具的时候,会一直报redis不可解析,我已经在我的代码服务器中设置了该host条目。最后解决该问题的方法是:我在我本机的host文件中加上了该host条目,我感觉这种解决问题的方式不是很优雅。