面试官:用户提现时如何保证线程安全以及幂等性?

场景描述

在多线程访问下,用户在提现的时候,该怎么保证数据的线程安全以及幂等性?

分析

保证数据线程安全,可以使用分布式锁
而要实现同一段时间内请求的幂等性,可以同样借助分布式锁给我们提供的RedissonClient

解决方案

基于redisson分布式锁+redis键值对过期时间,双重校验锁

  • 以下是使用的依赖
<dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson</artifactId>
   <version>3.16.3</version>
</dependency>  
  • spring容器中配置RedissonClient
@Configuration
@Slf4j
public class RedissonSingleConfig {
	@Value("${spring.redis.host}")
    private String redisHost;

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

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

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

    @Value("${spring.redis.timeout}")
    private Duration redisTimeout;


	@Bean
    public RedissonClient redissonClient() {
        RedissonClient redissonClient;

        Config config = new Config();
        //解决中文乱码问题
        config.setCodec(new JsonJacksonCodec());

        SingleServerConfig serverConfig = config.useSingleServer();
        String url = "redis://" + redisHost + ":" + redisPort;

        serverConfig.setAddress(url);
        serverConfig.setPassword(StringUtils.isEmpty(redisPassword)?null:redisPassword);
        serverConfig.setDatabase(redisDatabase);
        serverConfig.setTimeout(Integer.parseInt(String.valueOf(redisTimeout.toMillis())));
        serverConfig.setConnectTimeout(Integer.parseInt(String.valueOf(redisTimeout.toMillis())));

        try {
            redissonClient = Redisson.create(config);
            return redissonClient;
        } catch (Exception e) {
            log.error("RedissonSingleClient init redis url:[{}], Exception:", url, e);
            return null;
        }
    }

}
  • 核心代码
//1.外层校验 防止用户连点 一次性产生多笔提现
RBucket<String> bucket = redissonClient.getBucket("Bucket"+UserId);
String check = (String)bucket.get();
if(check != null){
    //避免重复点击
    return s;
}


//2.获取分布式锁 利用redission分布式锁,防止同一用户并发提现
String lockKey = UserId;
RLock lock = redissonClient.getLock(lockKey);
lock.lock(30, TimeUnit.SECONDS);


//3.双重校验,防止连续提现
if(check != null){
    //避免重复点击
    //return null;
    return s;
}



/**
* 4.提现业务代码
*/


//5.幂等性设置
check.set("提现中。。。请勿重复点击",30,TimeUnit.SECONDS);


//6.释放分布式锁
if(lock.isLocked() && lock.isHeldByCurrentThread()){
    lock.unlock();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值