分布式锁

在一些特定业务场景,比如秒杀、提现等情况,需要保证特定业务的执行顺序才能正确的处理业务,而不会出现茅台多卖,薅羊毛等问题出现。在单机部署的系统中,我们可以通过JDK提供的synchronized关键字、ReentranLock,或者基于AQS定制化锁,单机部署的情况下,锁是在多线程之间共享的,但是分布式部署的场景下,却无法提供多进程间的共享。因此需要使用分布式锁来进行多进程间共享。

分布式锁的实现方式有很多种,SoFast框架内置提供基于Redis的分布式锁实现,如果使用该功能,请现在pom.xml中添加依赖:

<dependency>
    <groupId>com.sofast.cloud</groupId>
    <artifactId>so-fast-lock-starter</artifactId>
</dependency>

在配置文件中正常进行Redis配置即可:

spring:
    redis:
    # 地址
    host: xxxx
    port: xxxx
    # 密码
    password: xxxxxxx
    # 连接超时时间
    timeout: 15s
    jedis:
      pool:
        # 连接池中的最小空闲连接
        min-idle: 3
        # 连接池中的最大空闲连接
        max-idle: 10
        # 连接池的最大数据库连接数
        max-active: 50
        # #连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms
    database: 4

使用

在业务开发中,如果要使用分布式锁,请参考以下两种方式:

1.注解方式

​ SOFAST框架中封装了分布式锁注解,通过注解可以非常方便的开启分布式锁

​ 例如:@RedisLock(redisKey = "test-redis-lock-1", waitTime = 5)

​ 使用分布式锁注解,必须提供两个属性:

​ redisKey:指明在redis中存储的key,请保证唯一性

​ waitTime:获取锁时的最大等待时间,单位秒

注意点:waitTime需要根据业务情况设置等待时间,目前redis锁的默认最大锁定时间为60秒,因此该值必须在60秒以内,实际上再业务处理中也很难达到60秒的等待时间。

该分布式锁可用于解决Schedule定时任务多节点部署的问题,可通过使用分布式锁,防止任务重复执行:

1.添加RedisLock注解,并配置参数,waitTime需要根据业务时间处理,例如配置3秒,那么在实际的业务执行中,要保证3秒钟不释放锁,即可保证锁的成功。实现方式很多,例如可显示的强制线程锁定6秒:Thread.Sleep(6000)

2.自定义方式

​ 自定义方式在使用上更加灵活,能满足各种业务场景,但是在处理上要麻烦一些,锁的获取、释放等需要业务编码进行控制。

注入Bean
@Autowired
RedisLockRegistry lockRegistry;
定义一个锁
Lock lock = lockRegistry.obtain("test-key");

注意:这里的key是在redis中存储的key,要保证业务唯一性。key的粒度要尽量小,比如:「xxx业务-xxx用户-xxx方法」等组成的key,可以减少锁冲突。

持有锁,并进行业务处理
if (lock.tryLock(3, TimeUnit.SECONDS)) {
  // 持有锁成功,进行业务处理
}

注意:tryLock时,可以指定时间参数,该参数表示等待获取锁的最大时间。

业务处理完成后,释放锁
lock.unlock();

注意:unlock一定要在finally中执行,保证可以正确的释放锁。

Copyright © 2020. 恩梯梯数据(中国)信息技术有限公司. all right reserved,powered by Gitbook该文件修订时间: 2021-08-19 11:40:26

results matching ""

    No results matching ""