枫叶上的雨露 2017-05-23
有的朋友可能会问,如果一个获得一个锁,执行一个耗时任务,耗时任务耗时大于锁默认的放锁时间,那么会怎么样。
redisson其实已经自动放锁了,避免饿死锁。
在超时上,我们业务也能不允许,所以我添加了fallback策略。
同样的分布式锁回调接口
package com.example.demo.redis2; import javax.persistence.Transient; /** * 分布式锁回调接口 * * @author lk */ public interface DistributedLockCallback<T> { /** * 调用者必须在此方法中实现需要加分布式锁的业务逻辑 * * @return */ public T process(); /** * 调用者业务 * 当超时的时候业务降级 * 与process一同使用 */ public T fallback(); /** * 得到分布式锁名称 * * @return */ public String getLockName(); }
可以看见我新建了fallback接口
package com.example.demo.redis2.service; import com.example.demo.redis2.DistributedLockCallback; import com.example.demo.redis2.DistributedLockTemplate; import com.example.demo.redis2.dao.TestEntityRepository; import com.example.demo.redis2.entity.TestEntity; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import javax.annotation.Resource; import javax.transaction.Transactional; /** * Created by LiaoKe on 2017/5/22. */ @Service public class AsyncService { @Resource TestEntityRepository ts; @Resource DistributedLockTemplate distributedLockTemplate; /** * 加锁 */ @Async @Transactional public void addAsync(){ distributedLockTemplate.lock(new DistributedLockCallback<Object>(){ @Override public Object process() { add(); return null; } @Override public Object fallback() { reduce(); return null; } @Override public String getLockName() { return "MyLock"; } }); } /** * 未加锁 */ @Async public void addNoAsync(){ add(); } /** * 测试异步方法 * 在不加分布式锁的情况下 * num数目会混乱 */ @Async public void add(){ try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } if(ts.findAll().size()==0){ TestEntity t = new TestEntity(); t.setNum(1); ts.saveAndFlush(t); }else{ TestEntity dbt = ts.findAll().get(0); dbt.setNum(dbt.getNum()+1); ts.saveAndFlush(dbt); } } /** * fallback */ @Async private void reduce(){ if(ts.findAll().size()==0){ TestEntity t = new TestEntity(); t.setNum(1); ts.saveAndFlush(t); }else{ TestEntity dbt = ts.findAll().get(0); dbt.setNum(dbt.getNum()-1); ts.saveAndFlush(dbt); } } }
fallback实现了对业务超时的进行的回退,数目减1,并且在方法上加上了@Transactional 事物注解,以防止在fallback发生异常,但是数目缺+1
由于我们使用的默认锁设置,超时是5秒,为了模拟超时,我在add()方法中让线程暂停了6秒。我们来看效果。
package com.example.demo.redis2; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import javax.persistence.Transient; import java.util.concurrent.TimeUnit; /** * Single Instance mode 分布式锁模板 * * @author lk */ public class SingleDistributedLockTemplate implements DistributedLockTemplate { private static final long DEFAULT_TIMEOUT = 5; private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS; private RedissonClient redisson; public SingleDistributedLockTemplate() { } public SingleDistributedLockTemplate(RedissonClient redisson) { this.redisson = redisson; } @Override public <T> T lock(DistributedLockCallback<T> callback) { return lock(callback, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT); } @Override public <T> T lock(DistributedLockCallback<T> callback, long leaseTime, TimeUnit timeUnit) { RLock lock = null; try { System.out.println("获取锁......."); lock = redisson.getLock(callback.getLockName()); lock.lock(leaseTime, timeUnit); T d = callback.process(); return d; } finally { if (lock != null) { if(!lock.isHeldByCurrentThread()){ System.out.println("超时自动放锁......."); callback.fallback(); }else{ System.out.println("释放锁......."); lock.unlock(); } } } } public void setRedisson(RedissonClient redisson) { this.redisson = redisson; } }
同样的url(见上篇文章)访问结果如下
可见数据库并未+1 ,fallback策略成功。
事物是必须加的,我们现在来模拟fallback失败
/** * fallback */ @Async private void reduce(){ int i = 5 /0 ; if(ts.findAll().size()==0){ TestEntity t = new TestEntity(); t.setNum(1); ts.saveAndFlush(t); }else{ TestEntity dbt = ts.findAll().get(0); dbt.setNum(dbt.getNum()-1); ts.saveAndFlush(dbt); } }
加上了一个runtimeException
并且去掉@Trabsactional
我们来看结果
但是数据库中数据却增加了,显示是不行的。
现在我们加上事务注解
可见在事务下,连第一次+1都没有提交,我们事务策略成功
下面附上demo