(六十二)java版spring cloud+spring boot+redis多租户社交电子商务平台--redis分布式锁

ITvv · · 17 次点击 · · 开始浏览    
电子商务平台源码请加企鹅求求:三五三六二四七二五九。在springcloud项目开发中redis分布式锁使用主要有两个场景 1.订单重复提交或支付提交等,防止刷单 2.对某个业务进行锁定,例如:当用户同一时间,进行对账户充值和提现操作,那么这里需要根据用户ID对账户进行锁定,只有一个完成了才可以进行第二个。 开发实现方式 1.pom.xml中引入jar包,最好引入到基础模块中,其他模块通用 ``` <!-- redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 创建redis操作类RedisGlobalLock(自定义) redis提供RedisTemplate方法 redis提供三个方法: (1)lock 获取锁并锁定 本方法是立即获取锁状态,如果获取成功并锁定,如果获取失败 (2)tryLock 尝试获取锁并锁定 本方式是在指定时间尝试获取锁 (3)unlock 释放锁 当业务处理完毕必须释放锁 重点: lock和tryLock区别:lock是实时获取,tryLock是尝试在一段时间内一直在获取 ``` @Service public class RedisGlobalLock { private static Log log = LogFactory.getLog(RedisGlobalLock.class); private static final String TYPE_NAME = RedisGlobalLock.class.getTypeName(); /** 默认30ms尝试一次 */ private final static long LOCK_TRY_INTERVAL = 30L; /** 默认尝试20s */ private final static long LOCK_TRY_TIMEOUT = 20 * 1000L; /** 单个业务持有锁的时间30s,防止死锁 */ private final static long LOCK_EXPIRE = 30 * 1000L; @Autowired private RedisTemplate<String, Object> redisTemplate; /** * 获取锁 * @param key 锁Key * @return 是否获取锁 */ public boolean lock(String key) { return getLock(key, 0, LOCK_EXPIRE, TimeUnit.MILLISECONDS); } /** * 获取锁 * @param key 锁Key * @param expire 有效期 * @param expireUnit 有效期时间单位 * @return 是否获取锁 */ public boolean lock(String key, long expire, TimeUnit expireUnit) { return getLock(key, 0, expire, expireUnit); } /** * 尝试获取锁 * @param key 锁Key * @return 是否获取锁 */ public boolean tryLock(String key) { return tryLock(key, LOCK_TRY_TIMEOUT, TimeUnit.MILLISECONDS); } /** * 尝试获取锁 * @param key 锁Key * @param timeout 等待超时时间 * @param unit 等待超时时间单位 * @return 是否获取锁 */ public boolean tryLock(String key, long timeout, TimeUnit unit) { // 超时时间转成毫秒 timeout = TimeUnit.MILLISECONDS.convert(timeout, unit); return getLock(key,timeout, LOCK_EXPIRE, TimeUnit.MILLISECONDS); } /** * 尝试获取锁 * @param key 锁Key * @param timeout 等待超时时间 * @param timeoutUnit 等待超时时间单位 * @param expire 有效期 * @param expireUnit 有效期时间单位 * @return */ public boolean tryLock(String key, long timeout, TimeUnit timeoutUnit, long expire, TimeUnit expireUnit) { // 超时时间转成毫秒 timeout = TimeUnit.MILLISECONDS.convert(timeout, timeoutUnit); return getLock(key,timeout, expire, expireUnit); } /** * 释放锁 * @param key 锁Key */ public void unlock(String key) { key = getPrefix(TYPE_NAME) + key; Long oldExpireTime = (Long) redisTemplate.opsForValue().get(key); if(null != oldExpireTime && oldExpireTime >= System.currentTimeMillis()) { // 大于过期时间,则删除key redisTemplate.delete(key); } } /** * 获取锁 * @param key 锁键值 * @param timeout 超时时间 * @param time 全局锁生命周期 * @param unit 时间单位 * @return 是否获取到锁 */ private boolean getLock(String key, long timeout, long time, TimeUnit unit) { key = getPrefix(TYPE_NAME) + key; try { long startTimeMillis = System.currentTimeMillis(); do { long newValue = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(time, unit); Boolean isOk = redisTemplate.opsForValue().setIfAbsent(key, newValue); if(isOk) { // 获得锁 redisTemplate.expire(key, time, unit); return true; } // 获取过期时间 Long oldExpireTime = (Long) redisTemplate.opsForValue().get(key); if(null == oldExpireTime) { oldExpireTime = 0L; } if(oldExpireTime >= System.currentTimeMillis()) { // 不小于系统时间并且过了超时时间,则不获取锁 if((System.currentTimeMillis() - startTimeMillis) > timeout) { return false; } // 休眠 Thread.sleep(LOCK_TRY_INTERVAL); } // 新的过期时间 long newExpireTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(time, unit); Long currentExpireTime = (Long) redisTemplate.opsForValue().getAndSet(key, newExpireTime); if(null == currentExpireTime) { currentExpireTime = 0L; } if(currentExpireTime.equals(oldExpireTime)) { // 获取到锁 redisTemplate.expire(key, time, unit); return true; } } while (true); } catch (Exception e) { return false; } } /** * 获取缓存标识前缀 * @param typeName 类名 * @return 前缀 */ protected final String getPrefix(String typeName) { return typeName; } } ``` 在业务逻辑层引入redis操作类 ``` @Resource private RedisGlobalLock redisGlobalLock; // 1、获取分布式锁防止重复调用 ====================== String key = PayDistributePrefix.PAY_MEMBER_ACCOUNT + memberId; if(redisGlobalLock.lock(key)) { try{ System.out.println("--处理业务---"); }catch (Exception e){ throw e; }finally { // 4、释放分布式锁 ====================== redisGlobalLock.unlock(key); } }else{ // 如果没有获取锁 Ensure.that(true).isTrue("17000706"); } ``` 所有锁业务必须释放锁,防止死锁
17 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet