栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

小型redis分布式锁的场景与实现

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

小型redis分布式锁的场景与实现

目录
  • 一、写在前面
  • 二、问题场景
  • 三、正确流程
  • 四、总结反思
  • 五、写在后面

浪漫主义的核心是对现实的憎恶,以及对逃避现实的那种激情洋溢的渴望。

一、写在前面

(由于考虑到公司业务和代码安全性问题,所有内容已脱敏)

二、问题场景

最近,在实习中遇到如下场景:

还是之前的短信业务,由于生产环境和灰度环境同时发布了定时任务,并且用的同一套数据库,所以,偶尔会出现短信重复发送的问题。即”超卖“现象。

三、正确流程

增加分布式锁,使两个环境只有一个定时任务能拿到锁即可。

工具类 JedisLockUtils

@Slf4j
public class JedisLockUtils {

    private static final String LOCK_SUCCESS = "OK";

    private static final Long RELEASE_SUCCESS = 1L;

    
    public static boolean tryLock(Jedis jedis, String lockKey, String lockValue, int expireTime) {
        try {
            String result = jedis.set(lockKey, lockValue, new SetParams().nx().ex(expireTime));
            return LOCK_SUCCESS.equals(result);
        } finally {
            returnResource(jedis);
        }
    }

    
    public static boolean releaseLock(Jedis jedis, String lockKey, String lockValue) {
        try {
            String script = "";
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(lockValue));
            return RELEASE_SUCCESS.equals(result);
        } finally {
            returnResource(jedis);
        }
    }

    
    public static void returnResource(final Jedis jedis) {
        if (null != jedis) {
            jedis.close();
        }
    }

}

工具锁 RedisLock

@Service
public class RedisLock {

    private static final Logger LOGGER = LoggerFactory.getLogger(RedisLock.class);

    @Resource
    private JedisSentinelPool jedisPool;

    public LockResult tryLock(String lockKey, int seconds) {
        Jedis jedis = jedisPool.getResource();
        long s = new Date().getTime();
        String lockId = UUID.randomUUID().toString();
        try {
            boolean result = false;
            int tryCount = 10; // 最多可尝试 10 次获取锁
            while (!result && tryCount > 0) {
                tryCount--;
                result = JedisLockUtils.tryLock(jedis, lockKey, lockId, seconds);
                if (result) {
                    long e = new Date().getTime();
                    return new LockResult(true, lockId);
                }
                Thread.sleep(500);
            }
        } catch (Exception e) {
            LOGGER.error("try lock error, lockKey={},lockId={}", lockKey, lockId, e);
        }
        return new LockResult(false);
    }

    // 获取不到锁则放弃
    public LockResult failOnCantTryLock(String lockKey, int seconds) {
        Jedis jedis = jedisPool.getResource();
        long s = new Date().getTime();
        String lockId = UUID.randomUUID().toString();
        try {
            boolean result = false;
            int tryCount = 10; // 最多可尝试 10 次获取锁
            while (!result && tryCount > 0) {
                tryCount--;
                result = JedisLockUtils.tryLock(jedis, lockKey, lockId, seconds);
                if (result) {
                    long e = new Date().getTime();
                    return new LockResult(true, lockId);
                }
                Thread.sleep(500);
            }
        } catch (Exception e) {
            LOGGER.error("try lock  error, lockKey={},lockId={}", lockKey, lockId, e);
        }
        return new LockResult(false);
    }

    public boolean releaseLock(String lockKey, String lockId) {
        Jedis jedis = jedisPool.getResource();
        for (int i = 0; i < 3; i++) {
            try {
                return JedisLockUtils.releaseLock(jedis, lockKey, lockId);
            } catch (Exception ignored) {
            }
        }
        return false;
    }

    public boolean checkLock(String lockKey, String lockId) {
        try {
            String value = jedisPool.getResource().get(lockKey);
            if (StringUtils.equals(lockId, value)) {
                return true;
            }
        } catch (Exception e) {
            LOGGER.error("try lock error, lockId={}", lockId, e);
        }
        return false;
    }
}

工具锁 LockResult

@Data
@AllArgsConstructor
@RequiredArgsConstructor

public class LockResult {

    @NonNull
    private boolean success;

    private String lockId;

}

更改后的短信任务

@Component
@Slf4j
public class SmsSendTask {

    @Autowired
    InviteSmsDOMapper inviteSmsDOMapper;

    @Autowired
    private SmsUtil smsUtil;

    @Autowired
    private RedisLock redisLock;

    @Scheduled(cron = "0/1 * * * * ?")
    public void SmsSendTask() {

        LockResult lockResult = null;
        try {
            lockResult = redisLock.failOnCantTryLock("SmsSendTask", 30);
            if (lockResult.isSuccess()) {

               

            }

        } catch (Exception e) {
            log.error(e.getMessage());
        } finally {
            if (null != lockResult && lockResult.isSuccess()) {
                redisLock.releaseLock("SmsSendTask", lockResult.getLockId());
            }
        }

    }
}

四、总结反思

这个问题其实应该在很早前发现,

生成和灰度抢资源我是没有想到的,而且由于定时任务是每秒除触发,所以刚好行程一个小型的抢单场景。

看来以后写定时任务的时候,要对公司发布流程足够熟悉。

五、写在后面

欢迎关注,实现期间会经常记录一些工作中遇到的问题。

欢迎随时留言讨论,与君共勉,知无不答!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1014215.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号