分布式系统定时任务,保证只有一个服务执行了改任务--采用redis分布式锁来实现(文章摘自:https://www.cnblogs.com/0201zcr/p/5942748.html)
文章摘自:https://www.cnblogs.com/0201zcr/p/5942748.html
package com.abtc.server.mine.common.utils; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer; import java.util.UUID; public class RedisLock
{
private static Logger logger = LoggerFactory.getLogger(RedisLock.class); private RedisTemplate redisTemplate; private static final int DEFAULT_ACQUIRY_RESOLUTION_MILLIS = 10; // 记录当前获取锁的标识
private String uuid = UUID.randomUUID().toString(); /**
* Lock key path.
*/
private String lockKey; /**
* 锁超时时间,防止线程在入锁以后,无限的执行等待
*/
private int expireMsecs = 60 * 1000; /**
* 锁等待时间,防止线程饥饿
*/
private int timeoutMsecs = 10 * 1000; private volatile boolean locked = false; /**
* Detailed constructor with default acquire timeout 10000 msecs and lock expiration of 60000 msecs.
*
* @param lockKey lock key (ex. account:1, ...)
*/
public RedisLock(RedisTemplate redisTemplate, String lockKey)
{
this.redisTemplate = redisTemplate;
this.lockKey = lockKey + "_lock";
this.uuid = uuid;
} /**
* Detailed constructor with default lock expiration of 60000 msecs.
*/
public RedisLock(RedisTemplate redisTemplate, String lockKey, int timeoutMsecs)
{
this(redisTemplate, lockKey);
this.timeoutMsecs = timeoutMsecs;
} /**
* Detailed constructor.
*/
public RedisLock(RedisTemplate redisTemplate, String lockKey, int timeoutMsecs, int expireMsecs)
{
this(redisTemplate, lockKey, timeoutMsecs);
this.expireMsecs = expireMsecs;
} /**
* @return lock key
*/
public String getLockKey()
{
return lockKey;
} private String get(final String key)
{
Object obj = null;
try
{
obj = redisTemplate.execute(new RedisCallback<Object>()
{
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException
{
StringRedisSerializer serializer = new StringRedisSerializer();
byte[] data = connection.get(serializer.serialize(key));
connection.close();
if (data == null)
{
return null;
}
return serializer.deserialize(data);
}
});
}
catch (Exception e)
{
logger.error("get redis error, key : {}", key);
}
return obj != null ? obj.toString() : null;
} private boolean setNX(final String key, final String value)
{
Object obj = null;
try
{
obj = redisTemplate.execute(new RedisCallback<Object>()
{
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException
{
StringRedisSerializer serializer = new StringRedisSerializer();
Boolean success = connection.setNX(serializer.serialize(key), serializer.serialize(value));
connection.close();
return success;
}
});
}
catch (Exception e)
{
logger.error("setNX redis error, key : {}", key);
}
return obj != null ? (Boolean) obj : false;
} private String getSet(final String key, final String value)
{
Object obj = null;
try
{
obj = redisTemplate.execute(new RedisCallback<Object>()
{
@Override
public Object doInRedis(RedisConnection connection) throws DataAccessException
{
StringRedisSerializer serializer = new StringRedisSerializer();
byte[] ret = connection.getSet(serializer.serialize(key), serializer.serialize(value));
connection.close();
return serializer.deserialize(ret);
}
});
}
catch (Exception e)
{
logger.error("setNX redis error, key : {}", key);
}
return obj != null ? (String) obj : null;
} /**
* 获得 lock.
* 实现思路: 主要是使用了redis 的setnx命令,缓存了锁.
* reids缓存的key是锁的key,所有的共享, value是锁的到期时间(注意:这里把过期时间放在value了,没有时间上设置其超时时间)
* 执行过程:
* 1.通过setnx尝试设置某个key的值,成功(当前没有这个锁)则返回,成功获得锁
* 2.锁已经存在则获取锁的到期时间,和当前时间比较,超时的话,则设置新的值
*
* @return true if lock is acquired, false acquire timeouted
* @throws InterruptedException in case of thread interruption
*/
public synchronized boolean lock() throws InterruptedException
{
int timeout = timeoutMsecs;
while (timeout >= 0)
{
long expires = System.currentTimeMillis() + expireMsecs + 1;
String expiresStr = String.valueOf(expires) + "&" + uuid; //锁到期时间 + uuid
if (this.setNX(lockKey, expiresStr))
{
// lock acquired
locked = true;
return true;
}
String currentValueStr = this.get(lockKey); //redis里的时间
if (currentValueStr != null && !"".equals(currentValueStr))
{
String[] split = currentValueStr.split("&");
if (split[0] != null && Long.parseLong(split[0]) < System.currentTimeMillis())
{
// 判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
// lock is expired
String oldValueStr = this.getSet(lockKey, expiresStr);
// 获取上一个锁到期时间,并设置现在的锁到期时间,
// 只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的
if (oldValueStr != null && oldValueStr.equals(currentValueStr))
{
// 防止误删(覆盖,因为key是相同的)了他人的锁——这里达不到效果,这里值会被覆盖,但是因为什么相差了很少的时间,所以可以接受 // [分布式的情况下]:如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁
// lock acquired
locked = true;
return true;
}
}
}
Thread.sleep(DEFAULT_ACQUIRY_RESOLUTION_MILLIS);
}
return false;
} /**
* Acqurired lock release.
*/
public synchronized void unlock()
{
if (locked)
{
String keyValue = this.get(lockKey);
if (keyValue != null && !"".equals(keyValue))
{
String[] split = keyValue.split("&");
// 判断uuid是否已经被修改
if (split.length == 2 && uuid.equals(split[1]))
{
redisTemplate.delete(lockKey);
}
}
locked = false;
}
} }
调用:
RedisLock lock = null;
try {
//先获取状态,如果为Y则任务已经发送完毕
if("Y".equals(redisutil.getString("flg"))){
return;
}
lock = new RedisLock(redisTemplate, "flg",10000,20000);//这里的flg最终会变成flg_lock
if(lock.lock()){
//再次检索,因为有可能开始获取状态不是Y,但是在获取锁的过程中被别人置成Y,所以再次检索确保不重发
if("Y".equals(redisutil.getString("flg"))){
return;
}
redisutil.set("flg", "Y",300);
//======================================start
//执行自己需要的任务
//======================================end
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(lock != null)
{lock.unlock();}
}
redisutil网上应该有很多,应该能搜到
注意:虽然可以实现我想要的效果,但是我个人有个疑问,分布式系统的时间是怎样统一的,如果时间不一样,可能会出问题
分布式系统定时任务,保证只有一个服务执行了改任务--采用redis分布式锁来实现(文章摘自:https://www.cnblogs.com/0201zcr/p/5942748.html)的更多相关文章
- Redis 分布式锁使用不当,酿成一个重大事故,超卖了100瓶飞天茅台!!!(转)
基于Redis使用分布式锁在当今已经不是什么新鲜事了. 本篇文章主要是基于我们实际项目中因为redis分布式锁造成的事故分析及解决方案.我们项目中的抢购订单采用的是分布式锁来解决的,有一次,运营做了一 ...
- SpringCloud微服务实战——搭建企业级开发框架(三十九):使用Redis分布式锁(Redisson)+自定义注解+AOP实现微服务重复请求控制
通常我们可以在前端通过防抖和节流来解决短时间内请求重复提交的问题,如果因网络问题.Nginx重试机制.微服务Feign重试机制或者用户故意绕过前端防抖和节流设置,直接频繁发起请求,都会导致系统防重 ...
- 利用redis 分布式锁 解决集群环境下多次定时任务执行
定时任务: @Scheduled(cron= "0 39 3 * * *") public void getAllUnSignData(){ //检查任务锁,若其它节点的相同定时任 ...
- Redis分布式锁服务(八)
阅读目录: 概述 分布式锁 多实例分布式锁 总结 概述 在多线程环境下,通常会使用锁来保证有且只有一个线程来操作共享资源.比如: object obj = new object(); lock (ob ...
- Redis分布式锁服务(转)
原文:http://www.cnblogs.com/mushroom/p/4752499.html 概述 在多线程环境下,通常会使用锁来保证有且只有一个线程来操作共享资源.比如: object obj ...
- Redis分布式锁服务
阅读目录: 概述 分布式锁 多实例分布式锁 总结 概述 在多线程环境下,通常会使用锁来保证有且只有一个线程来操作共享资源.比如: object obj = new object(); lock (ob ...
- 干货,不小心执行了rm -f,除了跑路,如何恢复?https://www.cnblogs.com/justmine/p/10359186.html
前言 每当我们在生产环境服务器上执行rm命令时,总是提心吊胆的,因为一不小心执行了误删,然后就要准备跑路了,毕竟人不是机器,更何况机器也有bug,呵呵. 那么如果真的删除了不该删除的文件,比如数据库. ...
- 你懂什么是分布式系统吗?Redis分布式锁都不会?
分布式系统涉及到很多的技术.理论与协议,很多人也说,分布式系统是“入门容易,深入难”,有一些人简历上写着熟悉分布式系统,很多人都是管中窥豹只见一斑. 究竟什么是分布式系统? 分布式系统是由一组通过网络 ...
- thinkphp5使用workerman的定时器定时任务在某一个时间执行
1.首先通过 composer 安装workerman,在thinkphp5完全开发手册的扩展->coposer包->workerman有详细说明: #在项目根目录执行以下指令compos ...
随机推荐
- beego 初体验 - 参数与传值
beego 支持 restful 风格的 url 传值分为路由传值和表单传值,表单传值可以绑定实体对象 1行 :id/:date 就是路由传值 2.3行是表单传值的路由配置 后台如何接收? 这是con ...
- Divide the Sequence (贪心)
题意:求将一串数据尽可能多分成所有前缀和大于0的连续子串. 思路:由于是要求所有前缀和大于0,那么只要从后往前推就好了. #include<bits/stdc++.h> using nam ...
- 自定义Word颜色主题
外观 说明 看到这个黑色编辑器的界面,第一印象可能认为是Sublime.Atom. VScode或者其它markdown编辑器.其实仅仅是微软的Word经过了自定义主题. 选择清晰易于辨认的字体和深色 ...
- Java当出现未被捕获的异常应该如何处理
在你学习在程序中处理异常之前,看一看如果你不处理它们会有什么情况发生是很有好处的.下面的小程序包括一个故意导致被零除错误的表达式.class Exc0 { public static void ...
- 使用curl自动签到smzdm
curl -H 'cookie:urcookies' -e 'http://www.smzdm.com/qiandao/' -k 'https://zhiyou.smzdm.com/user/chec ...
- DevOps的故事(如何整合开发和运维?)
在一个与我们平行的世界中,有一个软件开发公司.这个公司所做的产品用户量近期增长的十分迅猛,但是令CTO头疼的是公司的两大部门:开发部和运维部近期也是“掐”得厉害.为解决这个问题,CTO决定倒入现在十分 ...
- maven maven-war-plugin 解决java war项目间的依赖(两个war都可独立部署运行,maven 3.2.x亲测)
最近整理基础框架,有些项目不想分布式,所以基础框架必须同时可独立部署,也可直接被作为依赖和业务工程打到一起,记录下解决war项目依赖的要点,一开始用warpath,结果报找不到,有些帖子还是17年的, ...
- Tomcat基本
Tomcat web 应用服务器基础 jdk+tomcat安装 1.运行Tomcat为什么要装jdk? http://blog.sina.com.cn/s/blog_753bc97d0102w5rd. ...
- auto关键字
使用前: #include<string> #include<vector> int main() { std::vector<std::string> vs; f ...
- tp剩余未验证内容-6
杂项 系统中的电感线圈元件, 虽然不消耗电能, 但是会 占用系统的容量(相当于占用资源但是不做事), 会使系统 的发电量的使用效率降低, 线路损耗增大, 发出同样有功用电量所需的设备容量扩大 将感性元 ...