应用场景
在需要定时执行任务时就需要代码实现定时器任务,但是现在的系统大多是分布式系统,N台业务逻辑服务器同一时刻执行任务,如果不作处理,务必会造成资源的严重浪费,严重情况还会引起业务的失败。所以此时就需要实现分布式锁来保证同一时刻只有一台服务器执行该任务,不仅节约了系统资源,在保证高可用的情况下,还能正常的保证业务逻辑的实现。
代码实现
定时器
第一步
设置需要实现定时任务的bean与需要执行的方法名
<bean id="jobDetail1" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="Scheduler1">
</property>
<property name="targetMethod" value="run"/>
<!-- 是否允许任务并发执行。当值为false时,表示必须等到前一个线程处理完毕后才再启一个新的线程 -->
<property name="concurrent" value="false"/>
</bean>
第二步
执行该定时任务的频率,cron表达式工具详见:http://cron.qqe2.com/
<bean id="Triggers1" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="jobDetail" ref="jobDetail1">
</property>
<property name="cronExpression">
<!--Cron表达式的格式:秒 分 时 日 月 周 年(可选), 30s一次-->
<value>*/30 * * * * ?</value>
</property>
</bean>
第三步
将设置好的触发器纳管
<bean id="Scheduler1" autowire="no" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="Triggers1"></ref>
</list>
</property>
<property name="autoStartup" value="true"></property>
</bean>
锁实现
锁实现依赖于数据库,表结构与代码参考如下:
LockUtils代码如下:
package test.util;
import java.util.Date;
/**
* 该类提供抢锁、释放锁操作<br/>
* 保证N台服务器同一时刻只会有一台服务器执行某一操作<br/>
*
* @author
*/
public class LockUtils {
private static final String UNLOCK = "UNLOCK";
private static final String LOCK = "LOCK";
/**
* 抢锁
*
* @param transCoreLockDao
* @param lockKey
* @return
*/
public static boolean getLock(TransCoreLockDao transCoreLockDao, String lockKey) {
TransCoreLock transCoreLock = getTransCoreLock(lockKey, UNLOCK);
return transCoreLockDao.updateByEntity(SQL.get(SQL.UPDATE_TRANS_LOCK_TO_GET_LOCK), transCoreLock) > 0;
}
/**
* 释放锁
*
* @param transCoreLockDao
* @param lockKey
* @return
*/
public static boolean releaseLock(TransCoreLockDao transCoreLockDao, String lockKey) {
TransCoreLock transCoreLock = getTransCoreLock(lockKey, LOCK);
return transCoreLockDao.updateByEntity(SQL.get(SQL.UPDATE_TRANS_LOCK_TO_RELEASE_LOCK), transCoreLock) > 0;
}
/**
* 通过key和lock标识生成对象
*
* @param lockKey
* @param lock
* @return
*/
private static TransCoreLock getTransCoreLock(String lockKey, String lock) {
TransCoreLock transCoreLock = new TransCoreLock();
transCoreLock.setLockKey(lockKey);
transCoreLock.setLockFlag(lock);
transCoreLock.setUpdateDt(new Date());
return transCoreLock;
}
}
注意事项:
1.代码层面一定要极其注重还锁,否则容易影响下一次乃至以后所有的抢锁操作(正常逻辑及异常逻辑的还锁操作)
2.定时器锁较依赖于时钟同步,如果出现时间不同步,则可能会出现,同时两台或N台服务器执行业务逻辑,原因是某台服务器抢到锁–>执行业务逻辑成功→还锁等一系列操作均已完成,也许只是用了几十毫秒时间,另外一台由于时间的未同步,又继续执行了业务逻辑
3.该实现方式建议不作为影响交易逻辑等重要环节的解决方案