当今互联网时代,单机服务器已经无法满足系统性能要求,各个公司都采用分布式部署,此时就涉及到分布式锁。常用处理分布式锁的方式是采用redis的setnx命令来实现,接下来我们看一下怎样来封装一个比较好的分布式锁结构。
CacheService.java 缓存处理接口
package com.jd.wyjm.front.service.common; import com.jd.jim.cli.PipelineClient; import com.jd.jim.cli.TransactionClient; import com.jd.jim.cli.driver.types.StringTuple; import java.util.Date; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; /** * 缓存处理接口层 * * @create 2018-09-05 * @author zhangqiang200<https://blog.csdn.net/zhangqiang_accp> */ public interface CacheService { void set(String key, String value); boolean set(String key, String value, long timeout, TimeUnit timeUnit, boolean exist); void setObject(String key, Object object); void setEx(String key, String value, long timeout, TimeUnit timeUnit); String get(String key); Object getObject(String key); Long del(String key); void expire(String key, long timeout, TimeUnit timeUnit); void expireAt(String key, Date date); void persist(String key); boolean setNx(String key, String value); Set<String> hKeys(String key); boolean hSet(String key, String field, String value); void hmSet(String key, Map<String, String> hashes); Map<String, String> hGetAll(String key); String hGet(String key, String field); Map<String, String> hmGet(String key, String... fields); boolean hExists(String key, String field); long hDel(String key, String... fields); Boolean zAdd(String key, final double score, String value); Long zAdd(String key, Set<StringTuple> tuples); Set<String> zRange(String key, final long begin, final long end); Set<StringTuple> zRangeWithScores(String key, final long begin, final long end); Set<StringTuple> zRangeByScoreWithScores(String key, final long begin, final long end); Set<StringTuple> zRevRangeByScoreWithScores(String key, final long begin, final long end); Set<StringTuple> zRevRangeWithScores(String key, final long begin, final long end); Long zRank(String key, String value); Long zCard(String key); Long sAdd(String key, String... values); Set<String> sMembers(String key); boolean sIsMember(String key, String value); Double zIncrBy(String key, Long increment, String value); Boolean exists(String key); }
CacheServiceImpl.java 缓存实现类
package com.jd.wyjm.front.service.common.impl; import com.jd.jim.cli.Cluster; import com.jd.jim.cli.driver.types.StringTuple; import com.jd.wyjm.front.service.common.CacheService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; import java.util.concurrent.TimeUnit; @Service public class CacheServiceImpl implements CacheService { @Autowired private Cluster jimCluster; @Override public void set(String key, String value) { jimCluster.set(key, value); } @Override public boolean set(String key, String value, long timeout, TimeUnit timeUnit, boolean exist) { return jimCluster.set(key, value, timeout, timeUnit, exist); } @Override public void setObject(String key, Object object) { jimCluster.setObject(key, object); } @Override public void setEx(String key, String value, long timeout, TimeUnit timeUnit) { jimCluster.setEx(key, value, timeout, timeUnit); } @Override public String get(String key) { return jimCluster.get(key); } @Override public Object getObject(String key) { return jimCluster.getObject(key); } @Override public Long del(String key) { return jimCluster.del(key); } @Override public void expire(String key, long timeout, TimeUnit timeUnit) { jimCluster.expire(key, timeout, timeUnit); } @Override public void expireAt(String key, Date date) { jimCluster.expireAt(key, date); } @Override public void persist(String key) { jimCluster.persist(key); } @Override public boolean setNx(String key, String value) { return jimCluster.setNX(key, value); } @Override public Set<String> hKeys(String key) { return jimCluster.hKeys(key); } @Override public boolean hSet(String key, String field, String value) { return jimCluster.hSet(key, field, value); } @Override public void hmSet(String key, Map<String, String> hashes) { jimCluster.hMSet(key, hashes); } @Override public Map<String, String> hGetAll(String key) { return jimCluster.hGetAll(key); } @Override public String hGet(String key, String field) { return jimCluster.hGet(key, field); } @Override public Map<String, String> hmGet(String key, String... fields) { List<String> list = jimCluster.hMGet(key, fields); Map<String, String> map = new HashMap<>(fields.length); for (int i = 0; i < fields.length; i++) { map.put(fields[i], list.get(i)); } return map; } @Override public boolean hExists(String key, String field) { return jimCluster.hExists(key, field); } @Override public long hDel(String key, String... fields) { return jimCluster.hDel(key, fields); } @Override public Boolean zAdd(String key, final double score, String value) { return jimCluster.zAdd(key, score, value); } @Override public Long zAdd(String key, Set<StringTuple> tuples) { return jimCluster.zAdd(key, tuples); } @Override public Set<String> zRange(String key, final long begin, final long end) { return jimCluster.zRange(key, begin, end); } @Override public Set<StringTuple> zRangeWithScores(String key, final long begin, final long end) { return jimCluster.zRangeWithScores(key, begin, end); } @Override public Set<StringTuple> zRangeByScoreWithScores(String key, final long begin, final long end) { return jimCluster.zRangeByScoreWithScores(key, begin, end); } @Override public Set<StringTuple> zRevRangeByScoreWithScores(String key, final long begin, final long end) { return jimCluster.zRevRangeByScoreWithScores(key, begin, end); } @Override public Set<StringTuple> zRevRangeWithScores(String key, final long begin, final long end) { return jimCluster.zRevRangeWithScores(key, begin, end); } @Override public Long zRank(String key, String value) { return jimCluster.zRank(key, value); } @Override public Long zCard(String key) { return jimCluster.zCard(key); } @Override public Long sAdd(String key, String... values) { return jimCluster.sAdd(key, values); } @Override public Set<String> sMembers(String key) { return jimCluster.sMembers(key); } @Override public boolean sIsMember(String key, String value) { return jimCluster.sIsMember(key, value); } @Override public Double zIncrBy(String key, Long increment, String value) { return jimCluster.zIncrBy(key, increment, value); } @Override public Boolean exists(String key) { return jimCluster.exists(key); } }
说明:Cluster.java是京东自己封装改造后的Redis客户端,如需要使用调整为自己的redis客户端即可。
CacheLockService.java 分布式锁接口
package com.jd.wyjm.front.service.common; import java.util.concurrent.TimeUnit; /** * 分布式锁处理接口层 * * @create 2018-10-20 * @author zhangqiang200<https://blog.csdn.net/zhangqiang_accp> */ public interface CacheLockService { /** * 非阻塞式的获取锁。 * @param key 锁Key。 * @return 获取锁是否成功。 */ String tryLock(String key); /** * 非阻塞式的获取锁 * @param key 锁Key。 * @param expireTime 锁的过期时间 * @param timeUnit 锁过期时间单位 * @return 获取锁是否成功 */ String tryLock(String key, long expireTime, TimeUnit timeUnit); /** * 根据锁的序列号释放锁 * @param key * @param sequenceForLock * @return */ boolean unlock(String key, String sequenceForLock); /** * 强制释放锁 * @param key * @return */ boolean forceUnlock(String key); }
CacheLockServiceImpl.java,分布式锁实现类
package com.jd.wyjm.front.service.common.impl; import com.jd.wyjm.front.service.common.CacheLockService; import com.jd.wyjm.front.service.common.CacheService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.util.UUID; import java.util.concurrent.TimeUnit; @Service public class CacheLockServiceImpl implements CacheLockService{ @Autowired private CacheService cacheService; private static final String KEY_PREFIX = "LOCK_"; private static final long DEFAULT_EXPAIRE_TIME = 60; private static final TimeUnit DEFAULT_EXPAIRE_TIMEUNIT = TimeUnit.SECONDS; @Override public String tryLock(String key) { if (StringUtils.isEmpty(key)) { throw new IllegalArgumentException("分布式锁Key不能为空."); } String lockKey = KEY_PREFIX.concat(key); return acquireDistributedLock(lockKey, DEFAULT_EXPAIRE_TIME, DEFAULT_EXPAIRE_TIMEUNIT); } @Override public String tryLock(String key, long expireTime, TimeUnit timeUnit) { if (StringUtils.isEmpty(key)) { throw new IllegalArgumentException("分布式锁Key不能为空."); } if (null == timeUnit) { throw new IllegalArgumentException("分布式锁过期时间单位[TimeUnit]不能为空."); } String lockKey = KEY_PREFIX.concat(key); return acquireDistributedLock(lockKey, expireTime, timeUnit); } @Override public boolean unlock(String key, String sequenceForLock) { if (StringUtils.isEmpty(sequenceForLock)) { throw new IllegalArgumentException("分布式锁Key的sequence不能为空."); } String storedSequenceForLock = cacheService.get(KEY_PREFIX.concat(key)); if (sequenceForLock.equals(storedSequenceForLock)) { cacheService.del(KEY_PREFIX.concat(key)); return true; } /* 释放所的逻辑并未在一个事务中,需要完善。 */ return false; } @Override public boolean forceUnlock(String key) { Long l = cacheService.del(KEY_PREFIX.concat(key)); return !(null == l || l.compareTo(1L) < 0); } /** * 请求锁 * * @param lockKey * @param expireTime * @param timeUnit * * @return */ private String acquireDistributedLock(String lockKey, long expireTime, TimeUnit timeUnit) { String sequenceForLock = UUID.randomUUID().toString(); if (cacheService.set(lockKey, sequenceForLock, expireTime, timeUnit, false)) { return sequenceForLock; } else { return null; } } }