java B2B2C 源码 多级分销Springboot多租户电子商城系统-springcloud项目redis分布式锁
在springcloud项目开发中redis分布式锁使用主要有两个场景
需要JAVA Spring Cloud大型企业分布式微服务云构建的B2B2C电子商务平台源码请加企鹅求求 :二一四七七七五六三三
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");
}
所有锁业务必须释放锁,防止死锁

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
Java项目启动时,隐藏的 oracle 驱动异常问题
【场景】如果你跟我一样在开发微服务的项目,有一个隐藏的问题你可能没有发现,因为这个问题对你的项目启动似乎不会产生太大的影响,而这个问题的错误级别只是“WARN(警告)”。以下是出现该问题的日志截图: 问题的文字描述:egistered driver with driverclassname=oracle.jdbc.driver.oracledriver was not found, trying direct instantiation. 以下是相关配置信息: 【原因】Oracle 9i 之后,建议使用 oracle.jdbc.OracleDriver ,而不是 oracle.jdbc.driver.OracleDriver。 【官方解释】For Oracle 9i onwards you should use oracle.jdbc.O
-
下一篇
《快学 Go 语言》第 13 课 —— 并发与安全
上一节我们提到并发编程不同的协程共享数据的方式除了通道之外还有就是共享变量。虽然 Go 语言官方推荐使用通道的方式来共享数据,但是通过变量来共享才是基础,因为通道在底层也是通过共享变量的方式来实现的。通道的内部数据结构包含一个数组,对通道的读写就是对内部数组的读写。 在并发环境下共享读写变量必须要使用锁来控制数据结构的安全,Go 语言内置了 sync 包,里面包含了我们平时需要经常使用的互斥锁对象 sync.Mutex。Go 语言内置的字典不是线程安全的,所以下面我们尝试使用互斥锁对象来保护字典,让它变成线程安全的字典。 线程不安全的字典Go 语言内置了数据结构「竞态检查」工具来帮我们检查程序中是否存在线程不安全的代码。当我们在运行代码时,打开 -run 开关,程序就会在内置的通用数据结构中进行埋点检查。竞态检查工具在 Go 1.1 版本中引入,该功能帮助 Go 语言「元团队」找出了 Go 语言标准库中几十个存在线程安全隐患的 bug,这是一个非常了不起的功能。同时这也说明了即使是猿界的神仙,写出来的代码也避免不了有 bug。下面我们来尝试一下 package main import ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- 设置Eclipse缩进为4个空格,增强代码规范
- CentOS关闭SELinux安全模块
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- CentOS8编译安装MySQL8.0.19
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- MySQL数据库在高并发下的优化方案
- SpringBoot2更换Tomcat为Jetty,小型站点的福音